I am trying to create a dynamic query that joins my tables and filters the filters selected. In my example, the products_bind_filters are the filter items that belong to each product.
I'm trying to find the products that have the following criteria as an example in one result:
Products that are Red, Green OR Blue
that are also available in Small or Medium
My query is:
SELECT *,
p.name as productName,
c.name as categoryName,
p.price as productPrice,
p.hookName as productHook,
c.hookName as categoryHook,
p.imageMain as productThumb
FROM products p
JOIN products_bind_category pbc ON pbc.productsId = p.productsId
JOIN category c ON pbc.categoryId = c.categoryId
JOIN products_bind_value pbv ON pbv.productsId = p.productsId
WHERE p.productsId != '0'
AND pbc.categoryId = '10'
AND (pbv.valueId = '54' ) AND (pbv.valueId = '167' OR pbv.valueId = '186' OR pbv.valueId = '175' )
GROUP BY p.productsId
ORDER BY p.Price ASC
However, it's not returning the correct results. There are products that match the criteria but it's not showing them.
Any ideas?
I did try use an IN query but it also didn't work.
You are working with a key/value table, which is always kind of hard.
This condition:
AND (pbv.valueId = '54')
AND (pbv.valueId = '167' OR pbv.valueId = '186' OR pbv.valueId = '175')
is never met, because no row in the table can have a value of 54 and not 54 at the same time.
One way to deal with key/values is aggregation:
select *
from products p
join products_bind_category pbc on pbc.productsid = p.productsid
join category c on pbc.categoryid = c.categoryid
where p.productsid in
(
select productsid
from products_bind_value
group by productsid
having sum(valueid = 54) > 0
and sum(valueid in (167, 196, 175)) > 0
)
order by p.productsid;
The SUM(<expression>) > 0) uses the fact that in MySQL true = 1, false = 0 by the way.
Related
I have three tables, many to many relationship
the tables will be like the following if I insert data
I want to get the product name where (propertyValue='ios' or propertyValue = 'android') and (propertyValue='black')
In another way, we can say
I want to get the product name where (propertyid='2' or propertyid = '4') and (propertyid='3')
I tried second way and there is a SQL:
Select DISTINCT(p.productname),p.productid
from Products p
left join Product_propertyvalue ppv on p.productid=ppv.productid
where ( ppv.propertyvalueid=2 or ppv.propertyvalueid=4) and ( ppv.propertyvalueid=3 )
You can use having:
select p.productname, p.productid
from Products p left join
Product_propertyvalue ppv
on p.productid = ppv.productid
group by p.productname, p.productid
having sum( ppv.propertyvalueid in (2, 4) ) > 0 and
sum( ppv.propertyvalueid = 3 ) > 0;
The sum() conditions check that each property is there for a given product.
I've got a script where checked in employee's can see the stock inventory from each other, linked with their personal stock location, so each checked in employee can see which items are in stock at different locations. However, I want the main stock (id of 1, which is not attached to an employee) to be showed always, but I can't get the query right because one of the where statements is clearly not correct:
`stock_locations`.`location_id` = 1 AND
`workschedule`.`checkedIn` = 1 AND
Rememeber, the main stock is not linked to an employee, so it doesn't show up at the workschedule table. If I remove the first statement, It clearly shows up all the checked in employee's with their location, but that doesn't give me the main stock. If I remove the second statement, it only shows me the main stock.
How can I solve this issue within SQL? This is btw the full statement:
SELECT
`item_quantities`.`item_id`,
`stock_locations`.`location_name`,
`item_quantities`.`quantity`,
`people`.`first_name`
FROM
`item_quantities`
JOIN `stock_locations` ON `item_quantities`.`location_id` = `stock_locations`.`location_id`
JOIN `items` ON `item_quantities`.`item_id` = `items`.`item_id`
LEFT JOIN `workschedule` ON `workschedule`.`linked_storage` = `stock_locations`.`location_id`
LEFT JOIN `people` ON `workschedule`.`employee_id` = `people`.`person_id`
WHERE
`stock_locations`.`location_id` = 1 AND
`workschedule`.`checkedIn` = 0 AND
`items`.`unit_price` != 0 AND
`items`.`deleted` = 0 AND
`stock_locations`.`deleted` = 0 NULL
Thanks in advance!
Make it an OR statement inside of parens.
(`stock_locations`.`location_id` = 1 OR `workschedule`.`checkedIn` = 1) AND
This will return all records that match either the main stock or the employee.
You need to use the OR operator. Clearly both things can't happen at the same time, so you need to specify each set of acceptable conditions.
SELECT
`item_quantities`.`item_id`,
`stock_locations`.`location_name`,
`item_quantities`.`quantity`,
`people`.`first_name`
FROM
`item_quantities`
JOIN `stock_locations`
ON `item_quantities`.`location_id` = `stock_locations`.`location_id`
JOIN `items`
ON `item_quantities`.`item_id` = `items`.`item_id`
LEFT JOIN `workschedule`
ON `workschedule`.`linked_storage` = `stock_locations`.`location_id`
LEFT JOIN `people`
ON `workschedule`.`employee_id` = `people`.`person_id`
WHERE
`stock_locations`.`location_id` = 1
OR (
AND `workschedule`.`checkedIn` = 1
AND `items`.`unit_price` != 0
AND `items`.`deleted` = 0
AND `stock_locations`.`deleted` = 0
NULL
)
You have LEFT JOIN, but your WHERE clause turns them into inner joins. Fixing that will probably fix your problem:
SELECT . . .
FROM item_quantities iq JOIN
stock_locations sl
ON iq.`location_id` = sl.`location_id` JOIN
items i
ON iq.`item_id` = i.`item_id` LEFT JOIN
workschedule ws
ON ws.`linked_storage` = sl.`location_id` AND
ws.`checkedIn` = 0 LEFT JOIN
--------^
people p
ON ws.`employee_id` = p.`person_id`
WHERE sl.`location_id` = 1 AND
i.`unit_price` != 0 AND
i.`deleted` = 0 AND
sl.`deleted` = 0
It was hard to come up with a good title for this one. I have a query selecting products from my table. Each product may belong to multiple categories. So when I add in the 'category' to my DISTINCT query, it sometimes returns multiple of the same product because that product belongs to more than one category. I want my query to just pick one of the categories and return that, doesn't matter which one.
Here is my query:
SELECT DISTINCT ci.NAME,
ci.pid,
ci.description,
ci.price,
ci.saleprice,
ci.ingredients,
ci.allergens AS allergens,
ci.isfood,
ci.quantity,
ci.ismostpopular,
ci.activedate,
ci.isfrozen,
cc.NAME AS category
FROM cart_category cc
JOIN cart_item_category cic
ON cc.id = cic.catid
JOIN cart_item ci
ON cic.itemref = ci.itemref
WHERE cc.active = 1
AND ci.active = 1
AND ci.isdeleted = 0
AND ci.isfrozen = 0
AND ( ci.NAME LIKE '%dark%'
OR ci.pid = '%dark%' )
AND ci.quantity > 5
AND cc.NAME <> "nutritionals";
When I add in cc.name as category is when it returns multiple of the same product.
try just to limit it:
... LIMIT 1
I am trying to only get rows from video_index that belongs to a specific category from category_video_rel and order the result by COUNT of view count. This is the query I'm using:
SELECT
COUNT(DISTINCT view_count.id) AS count,
view_count.remove,
view_count.video_id,
video_index.id AS video_id,
video_index.active,
video_index.remove,
video_index.title AS title,
video_index.date_published AS date_published,
category_video_rel.active,
category_video_rel.remove,
category_video_rel.video_id AS cv_video_id,
category_video_rel.category_id AS category_id
FROM
view_count JOIN video_index
ON view_count.video_id = video_index.id,
category_video_rel JOIN video_index AS v
ON category_video_rel.video_id = v.id
WHERE
view_count.remove = '0' AND
video_index.active = '1' AND
video_index.remove = '0' AND
video_index.date_published <= '$current_time' AND
category_video_rel.category_id = '$category_id' AND
category_video_rel.active = '1' AND
category_video_rel.remove = '0'
GROUP BY
video_index.id
ORDER BY
count DESC
The problem is it outputs all the rows from video_index with a view count higher than 0 regardless of the category. Basically, it's ignoring "category_video_rel.category_id = '$category_id'" in the WHERE condition.
I have no idea what I'm doing wrong, please help.
Your FROM clause is mixing old style joins and new style joins
Instead try:
FROM
view_count JOIN video_index
ON view_count.video_id = video_index.id
JOIN category_video_rel
ON category_video_rel.video_id = video_index.id
I have query that joins Products table on Product_Attributes table. I' would like to get products which meat exact condition ex.
Products which have attribute id 9 AND 16 selected, but when I run this query I'll get only product which meets first ID
WHERE ("AttributeValueID" = 9 AND "AttributeValueID" = 16)
I need that products to have both attributes selected AttributeValueID = 9 AND AttributeValueID = 9 not only this or that attribute ID. Using OR / IN is'not solution for me.
Update:
below is a part of query with join / where:
LEFT JOIN "Product_Attributes" ON "Product_Attributes"."ProductID" = "SiteTree_Live"."ID" WHERE ("PriceTax" >= '100' AND "PriceTax" <= '200') AND ("AttributeValueID" IN ('9','8','7')) AND ("AttributeValueID" IN ('5','4'))
thank you in advance for your help
This is an example of a "set-within-sets" query. I like to solve these using group by and having, because that is a very flexible approach:
select pa.ProductId
from ProductAttributes pa
group by pa.ProductId
having sum(AttributeValueId = 9) > 0 and
sum(AttributeValueId = 16) > 0;
Gordon's answer showed the most flexible way to get the list of ProductId values that meet the specified criteria.
You could use a query like Gordon's (that gets the set of ProductId) as an inline view, and do a JOIN operation against the products table, like this:
SELECT p.ProductID
, p.ProductName
, ...
FROM products p
JOIN (
SELECT a.ProductID
FROM ProductAttributes a
GROUP BY a.ProductId
HAVING SUM(a.AttributeValueID=9) > 0
AND SUM(a.AttributeValueID=16) > 0
) s
ON s.ProductID = p.ProductID
UPDATE
based on the edit to the question adding a snippet of SQL text:
JOIN ( SELECT a.ProductID
FROM Product_Attributes a
HAVING SUM(a.AttributeValueID IN ('9','8','7')) > 0
AND SUM(a.AttributeValueID IN ('5','4')) > 0
GROUP BY a.ProductID
) s
ON s.ProductID = SiteTree_Live.ID
WHERE SiteTree_Live.PriceTax >= '100'
AND SiteTree_Live.PriceTax <= '200'