I have following tables
Tags
id | tag_name | slug |
1 | tag1 |tag1
2 | tag1 |tag1
products
id | proudct_name
1|product1
2|product2
product_tags
id | product_id | tag_id
1|1||1
2|1|2
3|2|1
i need retrieve only those product which belongs to both tag1 and tag2
select * from `products` INNER JOIN product_tags ON products.id=product_tags.product_id
INNER JOIN tags ON tags.id=product_tags.tag_id WHERE product_tags.tag_id=1 AND product_tags.tag_id=2
But my query return empty result
This will look the product that in both tables:
select * from `products` a where exists(select 1 from product_tags b where b.tag_id = 1 and a.product_id = b.product_id) and exists(select 1 from product_tags b where b.tag_id = 2 and a.product_id = b.product_id)
You can do what you want with inner join, which appears to be what you are trying. You just need to inner join twice:
select p.*
from products p join
product_tags pt1
on pt1.product_id = p.id and pt1.tag_id = 1 join
product_tags pt2
on pt2.product_id = p.id and pt2.tag_id = 2;
This might be easier to code in Laravel.
Your version doesn't work because the tag cannot be both "1" and "2" at the same time -- it can have different rows with those values but only one value per row.
Also, the tags table is not needed for the query.
Related
I have 3 tables in my php based system.
Those tables are product, category, product_categories.
Product
pid | product_name | price
1 | Nike T-Shirt | 23
Category
cid | category_name
1 | Men
2 | Women
Product_categories
pcid | cid | pid
1 | 1 | 1
2 | 2 | 1
That means, 1 product may be in both multiple categories.
Now I am developing the product search section with filter.
If a user select both categories, all the products in selected categories should display.
Example : If a user select both Men & Women, Nike T-Shirt should be displayed.
The query I used:
select p.*
from products p
left join product_categories pc on pc.pid=p.pid
WHERE pc.cid ='1' AND pc.cid = '2'
But it not returning correct products.
Where is the error?
You want conditions that span over multiple rows, which suggests aggregation. You can join the tables, group by product, and use the having clause for filtering:
select p.id, p.product_name, p.price
from product p
inner join product_category pc on pc.pid = p.pid
inner join category c on c.cid = pc.cid
where c.category_name in ('Men', 'Women')
group by p.pid, p.product_name, p.price
having max(c.category_name = 'Men') = 1 and max(c.category_name = 'Women') = 1
If you can filter by category id rather than by category name, then you need one less join:
select p.id, p.product_name, p.price
from product p
inner join product_category pc on pc.pid = p.pid
where pc.cid in (1, 2)
group by p.pid, p.product_name, p.price
having max(pc.cid= 1) = 1 and max(pc.cid = 2) = 1
Here are few alternatives:
-- All products that are at lest in one of the desired categories
select * from product where product.pid = any (select product_category.pid from product_category where cid = 1 or cid = 2);
-- Products that are in both categories
select * from product where product.pid = any (
select product_category.pid from product_category where cid = 1 or cid = 2 group by pid having count(cid) = 2
);
-- Products with additional information "in how many desired categories they are"
-- You can order by it and/or filter on it
with product_category_matches as (
select pid, count(*) as category_count from product_category where cid in (1, 2) group by pid
)
select *, product_category_matches.category_count from
product inner join product_category_matches on product.pid = product_category_matches.pid
where product_category_matches.category_count > 0 -- Product must be at least in one desired category
order by product_category_matches.category_count desc
I am trying to write a sql query to get the categories which does not have any of the products from particular category. Let say I have a,b,c,d,e categories and each category have some products. Now I need to get all the categories which done not include products of category a.
Categories table:
id name
1 A
2 B
3 C
4 D
5 E
category_products table:
product_id category_id
1 1
1 2
2 3
2 1
4 3
3 2
3 4
3 5
4 5
Query I used is below which gives B,C,D,E (not as expected)
SELECT DISTINCT c.name FROM category_products AS p
LEFT JOIN categories AS c ON c.id = p.category_id
WHERE p.product_id NOT IN (SELECT DISTINCT product_id FROM category_products where category_id = 1)
ORDER BY c.name
But I need results to be categories D,E which don't have any products from category A.
You need to do one more inner query, e.g.:
SELECT name
FROM categories
where id NOT IN (
SELECT DISTINCT category_id
FROM category_products WHERE
product_id IN (
SELECT product_id FROM category_products WHERE category_id = 1
)
);
This would return D and E.
Here's the SQL Fiddle.
You should use inner join
select distinct t2.name
from category_products t1
inner join Categories t2 on t2.id = t1.category_id
where t1.product_id not in
(select p.product_id
from category_products p
inner join Categories c on c.id = p.category_id
where c.name ='A')
I would be inclined to do this using group by and having:
select pc.product_id
from category_products pc join
categories c
on c.id = pc.category_id
group by pc.product_id
having sum(c.name = 'A') = 0;
I have 3 tables, products, productscategories and categories.
The productscategories is a many to many table with only id numbers of the other two tables.
The result without the group by looks like this:
id | Url | Category
-------------------------------------
1 | http://example.com/12 | hat
2 | http://example.com/12 | shoe
3 | http://example.com/13 | hat
4 | http://example.com/13 | jacket
5 | http://example.com/14 | hat
6 | http://example.com/14 | socks
Now I want to exclude every row with the same url if it contains any of the choosen categories, in this case jacket and shoe.
The unwanted result looks like this:
id | Url | Category
-------------------------------------
1 | http://example.com/12 | hat
3 | http://example.com/13 | hat
5 | http://example.com/14 | hat
Because url with id 13 includes jacket I don't want it there. Same goes for url with 14 which includes shoe.
This accur because I have multiple categories and multiple urls that are not aware of each other.
The sql for the above:
SELECT * FROM products
JOIN productscategories ON products.id = productscategories.product_id
JOIN categories ON categories.id = productscategories.category_id
WHERE categories.slug NOT IN (
'shoe',
'jacket',
)
GROUP BY products.image_url
The wanted result:
id | Url | Category
-------------------------------------
5 | http://example.com/14 | hat
How can I make an sql query that makes url aware of the category, like above?
SELECT * FROM products
JOIN productscategories ON products.id = productscategories.product_id
JOIN categories ON categories.id = productscategories.category_id
GROUP BY products.image_url
HAVING sum( categories.slug IN('shoe','jacket') )=0
categories.slug IN('shoe','jacket') - return 1 if category in set or 0 if not. sum() - return count of shoe/jacket in group. HAVING filter group with shoe/jacket in it.
Suggestion 1: WHERE NOT EXISTS
SELECT * FROM products
JOIN productscategories ON products.id = productscategories.product_id
JOIN categories ON categories.id = productscategories.category_id
WHERE NOT EXISTS(
SELECT 1
FROM products p2
JOIN productscategories pc2 ON p2.id = pc2.product_id
JOIN categories c2 ON c2.id = pc2.category_id
WHERE c2.slug IN ('shoe','jacket')
AND p2.url = products.url
)
Suggestion 2: OUTER JOIN
SELECT * FROM products
JOIN productscategories ON products.id = productscategories.product_id
JOIN categories ON categories.id = productscategories.category_id
LEFT OUTER JOIN products p2 ON products.url = p2.url
LEFT OUTER JOIN productscategories pc3 ON p2.id = pc2.product_id
LEFT OUTER JOIN categories c2 ON c2.id = pc2.category_id AND c2.slug IN ('shoe','jacket')
WHERE c2.id IS NULL
Try this:
SELECT * FROM (
SELECT * FROM products
JOIN productscategories ON products.id = productscategories.product_id
JOIN categories ON categories.id = productscategories.category_id
) AS A
LEFT JOIN
(
SELECT Url FROM products
JOIN productscategories ON products.id = productscategories.product_id
JOIN categories ON categories.id = productscategories.category_id
WHERE Categories.slug IN ('jacket', 'shoe')
GROUP BY url
) B ON B.url = A.url
WHERE B.url IS NULL
I have tables
table_order
product_name
Below is the SQL I have to query table_order which works perfectly in outputting the product_id but I need it to show it's name and not number. I need to modify mySQL with a LEFTJOIN to somehow connect with table product_name but I keep getting an error. So instead of
99 = 15, 99 = 16
I need
99 = name1, 99 = name2
SELECT table_order.order_id, table_order.product_id
FROM table_order
WHERE table_order.order_id=99
ORDER BY table_order.order_product_id
table table_order
_________________________
order_id | product_id |
_________________________
99 | 15 |
99 | 16 |
_________________________
table product_name
___________________________
product_id | product_name |
___________________________
15 | name1 |
16 | name2 |
___________________________
Use
SELECT a.product_id, b.product_name
FROM table_order AS a LEFT JOIN product_name AS b ON a.product_id = b.product_id
WHERE a.order_id = '99'
ORDER BY a.product_id
You need to join the tables, as you say, on product_id.
SELECT table_order.order_id, product_name.product_name
FROM table_order
INNER JOIN product_name on table_order.product_id = product_name.product_id
WHERE table_order.order_id=99
ORDER BY table_order.product_id
Select t1.product_id,t2.product_name from table_order as t1
inner join(
select * from product_name
) as t2
on t1.product_id = t2.product_name
where t1.product_id = //some Number
See if that works.
If you need to output all the orders regardless of whether the product id of that table_order has a connection to product_table, you need to use LEFT JOIN.
SELECT o.order_id, p.product_name
FROM table_order o
LEFT JOIN product_name p ON o.product_id = p.product_id
ORDER BY o.product_id
However if you just need to get all the rows that exists in both tables, you can use an INNER JOIN
SELECT o.order_id, p.product_name
FROM table_order o
INNER JOIN product_name p ON o.product_id = p.product_id
ORDER BY o.product_id
UPDATE
There is a database model in sqfiddle: http://sqlfiddle.com/#!2/8dbb0/10
And I updated the question according to the annotations.
Original Post
I have three tables:
posts
tags
tag_to_post
Lets asume a tag_id 1 that has been used by user 2. Now I want to show user 2 all posts, that another user has tagged with tag_id 1, but user 2 has not tagged with tag_id 1 so far.
The query:
SELECT posts.id AS post_id, tags.id AS tag_id, tag_to_post.user_id AS
user_tagged_post
FROM posts
LEFT JOIN tag_to_post ON tag_to_post.post_id = posts.id
LEFT JOIN tags ON tags.id = tag_to_post.tag_id
WHERE tags.id =1
Produces something like:
post_id | tags_id | user_tagged_post
1 | 1 | 1
1 | 1 | 2
2 | 1 | 2
3 | 1 | 1
So there should only be left post id 3.
First I tried with where-statement like:
WHERE tags.id = 1 AND tag_to_post.user_id != '2'
But this of course doesn't exclude post_id 1 cause it is a douplicate. I think there should be a DISTINCT or GROUPED BY before the WHERE clause, but this seems not to be allowed. So the only way is a sub-query? I didn't find a solution so far. Any ideas?
If I understand you correctly, it would seem like a straight forward LEFT JOIN;
SELECT t1.post_id, p.title, t1.tag_id, t1.user_id
FROM tag_to_post t1
JOIN posts p ON t1.post_id = p.id
LEFT JOIN tag_to_post t2
ON t1.tag_id = t2.tag_id AND t1.post_id = t2.post_id AND t2.user_id = 2
WHERE t1.user_id <> 2 AND t2.user_id IS NULL
An SQLfiddle to test with.
May be you need something like this
SELECT posts.id, posts.title, tags.tag, tag_to_post.user_id
FROM posts
INNER JOIN tag_to_post ON tag_to_post.post_id = posts.id
INNER JOIN tags ON tags.id = tag_to_post.tag_id
WHERE tags.id = 1 AND tag_to_post.user_id <> 2
Based on comments:
SELECT DISTINCT posts.id, posts.title, tags.tag, A.user_id
FROM posts
INNER JOIN tag_to_post A ON A.post_id = posts.id
INNER JOIN tags ON tags.id = A.tag_id
WHERE tags.id = 1
AND A.post_id NOT IN (SELECT post_id FROM tag_to_post WHERE tags.id = 1 AND user_id = 2)