I created a database for a clothing brand. The products consists of 3 tables: products, product_photos, inventory.
The issue I'm having pertains to the amount of results in the product_photos, inventory tables that are returned. So say there should be 4 results in the inventory table (ex. size: s, m, l, xl AND quantity: 20, 20, 20, 20) and 1 result in the product_photos table (ex. photos: url). With my current query the product_photos data is duplicated 4 times (ex. photos: url, url, url, url).
My current query looks like this:
SELECT
products.id,
products.type,
products.collection,
products.title,
products.price,
products.thumbnail,
GROUP_CONCAT(product_photos.id) AS photoId,
GROUP_CONCAT(product_photos.photo) AS photos,
GROUP_CONCAT(inventory.size) AS size,
GROUP_CONCAT(inventory.quantity) as quantity
FROM `products`
RIGHT JOIN
`product_photos` ON products.id = product_photos.product_id
RIGHT JOIN `inventory` ON products.id = inventory.product_id
WHERE
products.id = ?
GROUP BY
products.id
I have played around with some things such as changing the right's to inner join's and left join's but right join seems to be the technical choice for what I'm trying to do.
Here is some sample data:
product:
id: 1
product_photo:
id: 1,
product_id: 1,
photo: url
inventory:
id: 1,
product_id: 1,
size: s,
quantity: 20
id: 2,
product_id: 1,
size: l,
quantity: 14
The reason for the many results is that both the product_photos as and the inventory table can have multiple records for the same product_id and so all pairwise combinations of records from both tables will be iterated (Cartesian product).
You can solve this by first selecting all records from both these tables with a union and then join that result with the products table:
SELECT products.id,
products.type,
products.collection,
products.title,
products.price,
products.thumbnail,
GROUP_CONCAT(photoId) AS photoId,
GROUP_CONCAT(photos) AS photos,
GROUP_CONCAT(size) AS size,
GROUP_CONCAT(quantity) as quantity
FROM products
LEFT JOIN (
SELECT product_id,
id AS photoId,
photo AS photos,
null AS size,
null AS quantity
FROM product_photos
UNION
SELECT product_id,
null,
null,
size,
quantity
FROM inventory
) combi
ON products.product_id = combi.product_id
WHERE products.id = ?
GROUP BY products.id
This problem would normally be handled by aggregating along each dimension independently:
SELECT p.*
pp.photoIds, pp.photos,
i.sizes, i.quantitys
FROM products p LEFT JOIN
(SELECT pp.product_id,
GROUP_CONCAT(pp.id) AS photoIds,
GROUP_CONCAT(pp.photo) AS photos
FROM product_photos pp
GROUP BY pp.product_id
) pp
ON p.id = pp.product_id LEFT JOIN
(SELECT i.product_id,
GROUP_CONCAT(i.size) AS sizes,
GROUP_CONCAT(i.quantity) as quantitys
FROM inventory i
GROUP BY i.product_id
) i
ON p.id = i.product_id
WHERE p.id = ?;
Notes:
You want LEFT JOIN from the products table, not RIGHT JOIN. Presumably, you want information about a product if it is available, not only if it has inventory.
You should include an ORDER BY so the GROUP_CONCAT() values are guaranteed to be in the same order. That is, the first id matches the first image, for instance.
Table aliases make the query easier to write and to read.
The above should be more efficient (and simpler to code and follow0 than using UNION -- or UNION ALL). That is, two aggregations on "n" rows should be more efficient than one aggregation on "2 * n" rows. In more sophisticated databases, this formulation also gives the optimizer more information.
Because you only want this for one product, it is actually more efficient to filter before the aggregation:
SELECT p.*
pp.photoIds, pp.photos,
i.sizes, i.quantitys
FROM products p LEFT JOIN
(SELECT pp.product_id,
GROUP_CONCAT(pp.id) AS photoIds,
GROUP_CONCAT(pp.photo) AS photos
FROM product_photos pp
WHERE pp.product_id = ?
GROUP BY pp.product_id
) pp
ON p.id = pp.product_id LEFT JOIN
(SELECT i.product_id,
GROUP_CONCAT(i.size) AS sizes,
GROUP_CONCAT(i.quantity) as quantitys
FROM inventory i
WHERE i.product_id = ?
GROUP BY i.product_id
) i
ON p.id = i.product_id
WHERE p.id = ?;
In this case, you need to pass the parameter three times.
I have two tables products and categories. For products I have the ff fields:
id
pname
category_id
date
And for the cateogries id I have:
id
name
So using inner join I am trying to select all the names of categories that are equal to the category_id inside the products table.
Here's my take:
SELECT
c.name
FROM
categories AS c
INNER JOIN products AS p ON c.id = p.category_id
However this one did not work out and it's just sending me an empty array.
Any idea how can I do this? thanks!
If you just want category names, then exists or in is more appropriate than join:
SELECT c.name
FROM categories c
WHERE EXISTS (SELECT 1 FROM products p WHERE c.id = p.category_id);
You will not have to worry about eliminating duplicates, unless two categories have the same name.
This is also much more efficient than using SELECT DISTINCT on your query, especially if products has an index where category_id is the first key.
I have four tables that I need to pull data from. Three of the tables have a common value (product_id) the fourth table has a value in common with the third table (category_id). So they look something like this:
Products:
product_id, name, quantity, image, weight
Product_Description:
product_id, Description
Product_to_Category:
product_id, category_id
Category:
category_id, category_name
I need to pull all of the data and combine it into a result that looks like this
Name, Quantity, Image, Weight, Description, Category name
I know how to do a JOIN that works for the first three tables but I don't know how to add the into the results.
Just add an additional join on the category_id:
select p.name, p.quantity, p.image, p.weight, pd.description, c.category_name
from products p
join product_description pd on p.product_id = pd.product_id
join Product_to_Category pc on p.product_id = pc.product_id
join category c on pc.category_id = c.category_id
A Visual Explanation of SQL Joins
I'd like to be able to return a thumbnail (first image of a set) with my product data - what is the best way to accomplish this (performance)?
I have the following tables:
products (product data)
products_images (relation between products and images)
images (image data)
So for every product, it'd return the first image of a set associated with that product.
Two things:
I'm trying to get the first image but the order depends on an 'order'
field.
If no image found just return null for image data but still
get product.
I'm thinking of doing a subquery since I don't know how to about ordering and limiting image results in a join.
What do you suggest?
Edit:
The image order field is present in the products_images table. That table has the following fields (product_id, image_id, order)
SELECT
p.*
, i.thumbnail
FROM
products AS p
LEFT JOIN
images AS i
ON i.image_id =
( SELECT image_id
FROM products_images AS pi
WHERE pi.product_id = p.product_id
ORDER BY `order` ASC --- or DESC
LIMIT 1
)
An index on (product_id, order, image_id) in table products_images would be useful for the subquery to run faster.
This should give you the first image by the order column in the product_images table:
SELECT *
FROM products p
LEFT JOIN (
SELECT pi.product_id, i.*
FROM images i
INNER JOIN (
SELECT product_id, image_id
FROM products_images pi
ORDER BY `order`
) pi ON i.image_id = i.id
GROUP BY product_id
) i ON i.product_id = p.id
SELECT *
FROM
productinfo as p ,
category as c
WHERE
c.id IN (p.category) AND
p.pid='T3'
WHERE p.category will return (1,2,3,4,5) from product info table which the id of the category.
Now i need category name used for T3 [product Id] ,but i am getting only the first category name.
Your base query is the following
SELECT * FROM productinfo as p WHERE p.pid = 'T3';
Now you need to pull in categories, per product. This is a many to one relationship, so you need a LEFT JOIN.
SELECT * FROM productinfo as p
LEFT JOIN category as c ON c.id = p.category
WHERE p.id = 'T3'
You need to learn the different types of joins and how they are used. Whenever I see someone use 'FROM table1, table2' 90% of the times it means they don't understand joins and they need a LEFT JOIN instead.
Edit based on your comment
Your datamodel is flawed. Since a product can contain multiple categories, this is really a many-to-many relationship. You should create a product_category table that connects product id's with category id's.