Creating mysql many to many query - mysql

Ok I'm a beginner at mysql, but I have been trying to create a query for many to many tables.
My curent structure is:
Products
sku
product_name
price
etc..
Categories
id
category
products_categories
product_id (from sku column)
category_id
How can I query the following:
All products in a category
All categories associated with a product

All products in a category
select p.product_name
from products p
join product_categories pc
on p.sku = pc.product_id
join categories c
on c.id = pc.category_id
where c.category like '%CATEGORY_NAME_HERE%';
All categories associated with a product
select c.category
from category c
join product_categories pc
on c.category_id = pc.category_id
join product p
on p.sku = pc.product_id
where p.product_name like '%PRODUCT_NAME_HERE%';

Related

MySQL: Get products tha only have disabled categories

A product can have multiple categories associated via the pivot table products_categories.
The categories can be enabled (status = 1) or disabled (status = 0).
The id column in the categories table is unique / auto_increment.
I need to find products that ONLY have disabled categories.
So if a product belongs to two categories, one with status = 0 and one with status = 1 that products should not be part of my result.
Obviously this will not work:
SELECT
products.id
FROM
products
JOIN
products_categories
ON
products.id = products_categories.product_id
JOIN
categories
ON
products_categories.category_id = categories.id
WHERE
categories.status = 0
;
Here is my tables:
products
id (AUTO_INCREMENT)
products_categories
product_id (INT)
category_id (INT)
categories
id (AUTO_INCREMENT)
status (INT)
If you need only the product ids then you can use only the tables products_categories and categories.
With NOT EXISTS:
SELECT pc.product_id
FROM products_categories pc
WHERE NOT EXISTS (
SELECT *
FROM categories c
WHERE c.id = pc.category_id AND c.status = 1
);
or, with aggregation:
SELECT pc.product_id
FROM products_categories pc INNER JOIN categories c
ON c.id = pc.category_id
GROUP BY pc.product_id
HAVING MAX(c.status) = 0;
In this case GROUP BY products.id with HAVING MIN(status) = '0' AND MAX(status) = '0' should do the trick.
SELECT products.id
FROM products
JOIN products_categories ON products.id = products_categories.product_id
JOIN categories ON products_categories.category_id = categories.id
GROUP BY products.id
HAVING MIN(status) = '0' AND MAX(status) = '0';
In this fiddle you can find a simple demo.

SQL : multiple table join for data extraction

I have three tables and i want a join to extract data from database.
So that i can get products with names with their category name.
here are three tables.
categories
contains id, name
category_product
contains category_id , product_id,
products
contains id , name, price.
now products.id is fkey in category_product.product_id AND categories.id is fkey in category_product.category_id
You can try below -
select c.name as category_name,p.name as product_name
from category_product cp
inner join categories c on cp.category_id=c.id
inner join products p on cp.product_id=p.id
use join like below
select p.*,pc.*,c.* from product p join category_product pc on p.id= pc.product_id
join categories c on c.id=pc.category_id
select p.name AS product, c.name as category
from products p
inner join category_product cp on cp.product_id = p.id
inner join categories c on c.id = cp.category_id
select products.name as 'Product Name',
categories.name as 'Category Name',*
from category_product
inner join categories on category_product.category_id = categories.id
inner join on products on category_product.product_id = products.id
order by categories.id

How to select records which meet all criteria in linking table?

Given these two tables:
products
product_id name
1 shampoo
2 hairbrush
products_to_categories
id product_id category_id
0 1 100
1 1 200
2 2 100
I want to write the SQL which will give me all the products which are in both category 100 and category 200 (i.e. the product with id 1).
(We can assume the existence of the categories table. products_to_categories is a linking table).
select p.*, pc.* from products as p
inner join products_to_categories as pc
where pc.category_id = 100 and pc.category_id = 200;
Obviously doesn't work because no row has both those values.
I thought about running two selects and then an operation which would find only product_ids in both sets - but UNION is additional rather than subtractive.
This works but is not light-weight;
select products.* from products where products.product_id IN (
select p.product_id from products as p
inner join products_to_categories as pc ON pc.product_id = p.product_id AND pc.category_id = 100
)
AND
products.product_id IN (
select p.product_id from products as p
inner join products_to_categories as pc ON pc.product_id = p.product_id AND pc.category_id = 200
) ;
There must be a nicer way to do this?
Thanks
You can use group by and having:
select p.*
from products p inner join
products_to_categories pc
on p.product_id = pc.product_id
where pc.category_id in (100, 200);
group by p.product_id
having count(distinct category_id) = 2;
Note: This uses an ANSI SQL feature that allows you to group by the primary key of a table and reference the rest of the columns. Not all databases support this feature. So, in many databases, you would need to list the product fields individually in the group by.

SQL 'Not in' query involving three tables

I have three tables, product, category and product_to_category. Product has the primary key product_id, category category_id and product_to_category p2c_id. Product_to_ category links products to categories in a many-to-many relationship using their respective ID's.
Basically I want to write a query that would select all products from categories that do not exist in the category table. This is due to products being migrated across from another database.
I had something like this but was a little lost.
SELECT *
FROM product AS p
LEFT JOIN product_to_category AS p2c ON p.product_id = p2c.product_id
LEFT JOIN category AS c ON c.category_id
Basically that is as far as I have got. I need to join the category table to the product_to_category table where the product_to_category category_id is not in the category table. I may be completely on the wrong path but am stuck!
Thanks in advance.
Assumption: A product can be part of categories that exist, categories that do not exist, or no categories at all. You have not asked for products that belong to no categories at all, so the first LEFT JOIN from product to procduct_to_category should be an INNER JOIN.
Caveat: I am rusty at mysql so I am using SQL SERVER syntax. I forget if mysql has ON clauses or uses where clauses for JOINs. If ON clause is not supported, change them into WHERE clauses.
There are two common approaches: OUTER JOIN or a NOT IN clause (or a NOT EXISTS clause, which often behaves the same performance-wise as the NOT IN clause.)
OUTER JOIN
select p.*, p2c.category_id
from product p
INNER JOIN product_to_category p2c ON (p.product_id = p2c.product_id)
LEFT JOIN category c ON p2c.category_id = c.category_id
WHERE c.category_id IS NULL
The test for null will find the unmatched records.
NOT IN clause
SELECT p.*, p2c.category_id
FROM product p
INNER JOIN product_to_category p2c ON (p.product_id = p2c.product_id)
WHERE p2c.category_id NOT IN (SELECT c.category_id FROM category c)
If you're looking for products from nonexistent categories, I'd suggest
Select p.*,p2c.category_id
from product p
join product_to_category p2c
on p.product_id=p2c.product_id
left outer join category c
on p2c.category_id=c.category_id
where c.category_id is null
SELECT p.*
FROM product AS p
LEFT JOIN product_to_category AS p2c ON p.product_id = p2c.product_id
WHERE NOT EXISTS (
SELECT 1
FROM category c
WHERE c.category_id = p2c.category_id
)
SELECT *
FROM product AS p
JOIN product_to_category AS p2c ON p.product_id = p2c.product_id
JOIN category AS c ON c.category_id != as.category.
Try this?

MySQL Join Question

Hi i'm struggling to write a particular MySQL Join Query.
I have a table containing product data, each product can belong to multiple categories. This m:m relationship is satisfied using a link table.
For this particular query I wish to retrieve all products belonging to a given category, but with each product record, I also want to return the other categories that product belongs to.
Ideally I would like to achieve this using an Inner Join on the categories table, rather than performing an additional query for each product record, which would be quite inefficient.
My simplifed schema is designed roughly as follows:
products table:
product_id, name, title, description, is_active, date_added, publish_date, etc....
categories table:
category_id, name, title, description, etc...
product_category table:
product_id, category_id
I have written the following query, which allows me to retrieve all the products belonging to the specified category_id. However, i'm really struggling to work out how to retrieve the other categories a product belongs to.
SELECT p.product_id, p.name, p.title, p.description
FROM prod_products AS p
LEFT JOIN prod_product_category AS pc
ON pc.product_id = p.product_id
WHERE pc.category_id = $category_id
AND UNIX_TIMESTAMP(p.publish_date) < UNIX_TIMESTAMP()
AND p.is_active = 1
ORDER BY p.name ASC
I'd be happy just retrieving the category id's releated to each returned product row, as I will have all category data stored in an object, and my application code can take care of the rest.
Many thanks,
Richard
SELECT p.product_id, p.name, p.title, p.description,
GROUP_CONCAT(otherc.category_id) AS other_categories
FROM prod_products AS p
JOIN prod_product_category AS pc
ON pc.product_id = p.product_id
LEFT JOIN prod_product_category AS otherc
ON otherc.product_id = p.product_id AND otherc.category_id != pc.category_id
WHERE pc.category_id = $category_id
AND UNIX_TIMESTAMP(p.publish_date) < UNIX_TIMESTAMP()
AND p.is_active = 1
GROUP BY p.product_id
ORDER BY p.name ASC
You would use an inner join to the product_category table, doing a left join there is pointless as you are using the value from it in the condition. Then you do a left join on the product_category table to get the other categories, and join in the categories for the data:
select
p.product_id, p.name, p.title, p.description,
c.category_id, c.name, c.title
from
prod_products p
inner join prod_product_category pc on pc.product_id = p.product_id
left join prod_product_category pc2 on pc2.product_id = p.product_id
left join prod_categories c on c.category_id = pc2.category_id
where
pc.category_id = #category_id and
unix_timestamp(p.publish_date) < unix_timestamp() and
p.is_active = 1
order by
p.name