Multiple Queries on Same field in tables - php

Really hope someone can help me or at least point me in the right direction. I working on a product site, that allows visitors to filter their results using a menu on side of the product results, similar to what www.asos.com have done. Basically, they can chose product type, style,price,fit ... etc. Each of these fiter types, have their own table and a reference table which links each value to the products table. EG There is a
Products table with productkey and all other product information
Colours table, with the following fields, Colour_ID, Name
Colourref table with productkey, Colour_ID
I'm using MySQL and PHP. I know how to query the database and display the data if the visitor makes one selection from each filter type and then to display the counts for each attribute, but I'd really like to be able to allow them to make multiple selections and then calculate the counts for each attribute, based on what's been selected. I've looked into how this should be done, and I've seen that subqueries are an option, but I'm concerned about how many subqueries I would need to create as I have 9 filter groups, that can have a large number of values. There are currently, 1/2 million products in the database, and this grow over time. I'm capturing the values that need to be queried via the url, so as an example
page=1&view=20&product=125,137,147&type=1,3,5&colour=3,9,5&material=187,345
As you can see from the example, I can have multiple values for each time. I tried writing a query using AND. Example, product = 125 AND product = 137, but that doesn't work.
Does anyone have any advice on the best way to go about doing this even if it's just a point in the right direction?
Any help will be greatly appreciated
Thank you in Advance
Vivien

Basically you answered your own question already:
SELECT ...
FROM ...
WHERE (product = 125 OR product = 137) AND
(colour = 3 OR colour = 8 OR colour = 5) ...
You need to use OR instead of AND if you want to select several products, colours and so on.
If you want both a product and colour then you need to combine those using AND in between. There is no need for subqueries here.
It's easier to use IN though:
SELECT ...
FROM ...
WHERE product IN (125, 137, 147) AND colour IN (3, 5, 8)
A more complete example of this SQL code:
SELECT p.*
FROM Products p
LEFT JOIN Colourref cr
ON cr.productkey = p.productkey
LEFT JOIN Colours c
ON c.Colour_ID = cr.Colour_ID
WHERE
p.productkey IN (1, 2, 4)
AND c.Colour_ID IN (1, 2)
This will select all products that have the ID 1, 2 or 4 which have the colours 1 or 2.
It left joins the required tables on the IDs and then filters the desired values.

Related

JetReviews + JetEngine Related CPTs

I have created two Custom Post Types using JetEngine:
Place
Product
I have also enabled the JetReviews for the Place CPT. So, each Place can have multiple Products, and each Product can have multiple reviews.
I want to display the Place rating average based on their Product ratings. For instance: Product 1 has three reviews, the first one has 2 stars, the second one has 5 stars and the third one has 1 star. Therefore, the Product 1 rating is "2,66" stars.
Since I wasn't able to find a graphical solution to achieve this, I have created an SQL query using JetEngine QueryBuilder:
SELECT AVG({prefix}jet_reviews.rating)
FROM {prefix}jet_reviews
INNER JOIN {prefix}posts
ON {prefix}posts.id = {prefix}jet_reviews.post_id
INNER JOIN {prefix}jet_rel_default
ON {prefix}jet_rel_default.child_object_id = {prefix}posts.id
WHERE {prefix}jet_rel_default.parent_object_id = "{current_id}";
But here I didn't know how refer to the Place ID when viewing the Single Post, So I guessed it could be the same as the {prefix} (but this didn't work).
I have tried asking this on Crocoblock FB Community and its subreddit a while ago but hadn't been answered.
Does anyone know how to display the Product's average rating in each Place Single Post like I "explained" before?

How to make a MYSQL query to include rows once of a column based on another?

Seems to be a simple question for someone who knows well SQL, but not for me.
Anyhow, here is a sample table:
Primary (and only) key is on id. So basically the scenario is as fallows. User may add images. Newly added images are inserted with comune_number value 0 avoiding duplicates (on file name = image via PHP). Images inserted can be assigned to a category from another table. The same image can be assigned to many categories, for each is inserted a new row with the category id (comune_number). Relation between the two tables is on comune_number.
I would like to show the images, with a checkbox checked for which is assigned already.
My question is simple. How to include all images once, but where the same image is already assigned, include that comune_number instead of 0. I don't care about the order. The result I would like to achieve is something like this:
I'm aware of GROUP BY, so if I try
mysql_query("SELECT * FROM banner WHERE `comune_number` = 0");
or
mysql_query("SELECT * FROM banner GROUP BY `image`");
I end up with the same (not wanted) result.
Most likely I have to combine two queries in one, but I can't figure out which and how.
Note1: I have tried many combinations in phpMyAdmin, based on my (little) knowledge and on what I found with Google (including Stackoverflow), but none of them resulted as shown.
Note2: MySQL version 5.1
Either MYSQL only or combined with PHP solutions are welcome.
EDIT
I need to keep the comune_number. So when I show the images, I need to know the id.
My queries must rely on comune_number, so when I need comune_number = 10, the result should be as on second image above.
EDIT2
It seems I wasn't made myself clear. So what I want, when user is watching category of id 10, show him all the images once, and mark the ones which were assigned to that category.
Another example is here. So if user is watching category (comune_number) of id=9, show every picture once, and mark the two assigned to it.
Based on your SQLFiddle and all the comments here is the updated query:
SELECT r.*
FROM (SELECT b.*
FROM banner b
WHERE b.comune_number = 9
UNION ALL
SELECT b2.*
FROM banner b2
WHERE b2.comune_number = 0
GROUP BY b2.image) r
GROUP BY r.image;
Live DEMO.
select * from
(SELECT * FROM banner order by
FIELD(comune_number,10,0))
abc group by `image`;
fiddle
Updated:
Query with filter condition:
select * from
(SELECT * FROM banner
where comune_number in (10,0) order by
FIELD(comune_number,10,0))
abc group by `image`;
an important tip. When you use GROUP BY all the field you put in your field list, must be in GRUOP BY clause or must be into an aggragate function. MySql has a strange behaviour, don't sign the error but if you try in another DBMS your query:
SELECT * FROM banner GROUP BY image
You have an error.
MySql applies for fields not present in group by an implicit limit 1.
Solution about your issue:
SELECT * FROM banner b1
WHERE b1.comune_number <= 'YOUR_CATEGORY'
AND NOT EXISTS
(SELECT 'X' FROM banner b2
where b2.image = b1.image
and b2.comune_number > b1.comune_number
AND b2.comune_number <= 'YOUR_CATEGORY')
EDIT
I've changed query, now I put a condition about input category. Where you find YOUR_CATEGORY put the value of category you see (i.e. 9).
I am not sure whether this is the exactly you need, but I think it can be helpful.Try this:
Select if(comune_number = 10, comune_number,0), id, image from table
You may change comune_number as per your convenience. Further nesting in IF condition is also possible.

Temporary Table and Left Joins not showing results as expected

I'm really hoping someone can help me with this. I have a number of product attribute types that users can select from to refine the products that are returned to them on screen. What I'm trying to do is, for each product attribute type, I want to list all attributes that relate to either the selected category or search term, then once they've made their selections, I still want to display each of the attributes that relate to the category or search term, but only display a clickable link if the product count for that particular attribute is greater than 1 and for those that have a product count of zero, I want to list them, but make them unclickable. An example of what I'm trying to achieve can be found on the ASOS website, in the left hand menu
http://www.asos.com/Women/Dresses/Cat/pgecategory.aspx?cid=8799#state=Rf961%3D3340%2C3341%40Rf-200%3D20&parentID=Rf-300&pge=0&pgeSize=20&sort=-1
Initially I tried using just joins to achieve this, but I wasn't able to do it, successfully. So I decided to create a temporary table for each attribute type which held a list of all the attributes that related to the main query and then created a refined query, with a left join. Here's my code:
CREATE TEMPORARY TABLE temp_table
SELECT su_types.id, type AS item FROM su_types
INNER JOIN su_typerefs ON su_types.id=su_typerefs.id
INNER JOIN su_pref ON su_typerefs.mykey = su_pref.mykey
WHERE wp_category_id =40 GROUP BY su_typerefs.id
$sudb->query($query);
if ($sudb->affected_rows > 0) {
SELECT temp_table.id,item,COUNT(su_typerefs.mykey) AS product_count FROM temp_table
LEFT JOIN su_typerefs ON temp_table.id=su_typerefs.id
LEFT JOIN su_pref ON su_typerefs.mykey = su_pref.mykey
LEFT JOIN su_stylerefs ON su_pref.mykey = su_stylerefs.mykey
LEFT JOIN su_productrefs ON su_pref.mykey = su_productrefs.mykey
WHERE wp_category_id =40 AND su_stylerefs.id in (91) AND su_productrefs.id in (54) AND su_typerefs.id in (159) GROUP BY su_typerefs.id
if ($itemresults = $sudb->query($query)) {
while($itemresult = $itemresults->fetch_array(MYSQLI_ASSOC)) {
$id=$itemresult['id'];
$item=$itemresult['item'];
$product_count=$itemresult['product_count'];
build_link($list_type, $item, $product_count, $id);
}
}
In the above example the first query selects all the product types that relate to a particular category, say dresses. And the second query is based on the refinements the user has made on the category, in this example this is product, product type and style. A user can also refine their search by colour, fit, fabric and design.
There are a couple of issues with this:
1) The number of results returned in the second query do not match the results of the first. Using the above as an example, I wish to list all products that relate to the chosen category, then using the second query return the product count for each of these products as I described above. So if the temporary table returns, trousers, jeans and skirts. I expected these three items to be displayed on screen based on the conditions applied in the second query, however my results may only show trousers and jeans, if there is not a match for skirts in the second query. I thought that using a left join would mean that all the results of the temporary table would be displayed.
2)Also I wonder if I'm doing this the most efficient way. I have a total of 8 attribute groups, and therefore need to do the above 8 times. If the user choses to refine the results using all 8 attribute groups then in addition to the temp table join, there will be a total of 9 joins for each type. It's taking a while to execute, is there a better way to do this? There are approximately 1/2 million products in the table, and this will probably be 5 times this, once my site goes live.
I really hope all that I have written makes sense and I'd really appreciate the stackoverflow community's help with this, if anyone can help. I apologise for the essay ;). Thanks in advance
To answer your first question; yes, a LEFT JOIN will indeed keep all data from the initial table. That, however, isn't the problem.
The reason why you lose empty categories, is most likely (I say this because I don't fully know your db structure) because of the where condition filtering out all results based on the data in the joined tables.
If for a category all items get filtered out (possibly including the NULL joined values), you will not get this category back from that query anymore. Also the GROUP BY is done on a joined column, that might also effectively wipe out your other categories.
As for the second question, you already state it's taking long; so it's probably not the way to go if you want things to work fast ;) (okay, obvious answer, low hanging fruit, etc). What you might want to do, is get a collection of keys from the filterable categories first, and use that data to select items.
This prevents that you have to join up your entire products table in a temp table (at least, that's what I think you're doing), which of course will take long with the given number of entries. Selecting a list of matching IDs from the given attributes also gives you the advance of using your indexes (more), which a temp-table probably won't have. If this is possible and feasible mainly depends on your schema's structure; but I hope it might lead you to the direction you want to go :)

Database design for price data

I have a database that stores components in a component table.
There is a second table for component prices. This table has a field that maps to a component id. My price table needs to store information for a bunch of different prices (10, 100, 1000, 10K, 100K, 1M). The thing is, there is a possibility in the future to store other price types such as 25K or 50K.
As of right now, my price table looks like this:
id component_id type price
where the type can take values from 1-6 currently. This is good because it will allow me to add new price types in the future very easily.
The other option is a price table that looks like this:
id component_id price_10 price_100 price_1000 price_10K price_100K price_1M
But in this case, I would need to add a new field every time a new price type is added.
I hope that people here would agree with the first method.
But using the first method, I'm having trouble displaying a page that would display all my components in my database with the 6 prices it may or may not have (should show 0 in this case). Obviously this would be simple using the second method.
This is the query I have so far:
SELECT * FROM `component` LEFT JOIN `component_cost` ON `cmpcst_component` = `cmp_id`
EDIT:
I thought I would show some sample data from the component price table:
The prices are a unit price for an amount X. X ranges from 10 to 1 million. So I might have something like this in my component price table:
id component_id type price
1 1 1 0.50
2 1 2 0.45
3 1 3 0.40
4 1 4 0.35
5 1 5 0.32
6 1 6 0.30
The first option its much better.
For display the data, create a view with a pivot table.
You can found help here
http://en.wikibooks.org/wiki/MySQL/Pivot_table
Create two tables, one for components and one for compontent's prices (for a minimum count). Then you can add as many prices per component you would like. Now and in the future.
This is a variation of your first example, you just don't hardcode types but has the number that is related to a certain price.
Try something like:
select * from component c join compcst_component cc on true and c.id = cc.cmp_id;
I'm not sure I follow you.. Hope you can give us the table structures, but here's one try:
SELECT c.cmp_id, ISNULL(p.price, 0) AS Price
FROM component AS c INNER JOI component_cost AS p
ON p.cmpcst_component = c.cmp_id
Maybe I don't have the column-to-table matchup right, but try that.

Combining multiple rows or results with the same title to form drop down menus with PHP and MySQL

So I am picking up a project that was quit halfway through by the last guy so that I could get some more practice with PHP and databases. I have run into a problem, and I am sure it is common enough that there is a standard solution, but I am unable to find one.
The db I am working with has 4,600, so reorganizing is out of the question. It is a db of liquers for a wholesaler. Here is what the results page looks like currently:
What I am trying to set it up so the results are returned in list form, with only one title and dropdown menus for the different sizes/prices of products that looks like this:
The problem is that there are multiple entries in the db for each product. In this example there are 3, while some have 1, and some have 2.
I am really not sure how to go about this, and any help would be greatly appreciated. Thank you.
I'm not sure about the PHP syntax, but pseudocode here's what you could do:
allProductsReturnedFromMySQL = QueryYourDatabaseForAllProducts()
Hashtable[productId, List[productSizes]] dropDownsByProduct;
Hashtable[productId, commonProductInformation] uniqueProducts;
foreach (product in allProductsReturnedFromMySQL) {
if product.productId not in uniqueProducts
then add it with the product information that does not vary
if product.productId not in dropDownsByProduct
then add it with an empty list
append the size of this product to the corresponding list in dropDownsByProduct
}
After that little bit of logic you'll have all your unique products with the common properties for each one, and a way to fetch the corresponding sizes drop down. If you wanted to do this purely in SQL to minimize the data that's transferred, you could do something like this:
-- this would get you your products
select distinct id, property1, property2 from product
-- this would get you your drop downs by product
select id, size from product order by id
You can then build the same drop down hashtable by iterating through the second result set.
I'm not sure if this is the best way, but I've always approached this by altering the query so that it is sorted by product name. Then as you iterate through the rows, check to see if the product name matches the one you just processed. If it's the same, then this row is a different size of the same project.

Categories