MySQL JOIN two lookups - mysql

I have two tables
products
id
code
description
category
subcategory
product_categorys
id
category_name
category_master
Data in products table
1, UK001, Description Text, 1, 2
Data in product_category table
1, Network Cable, 0
2, CAT6, 1
I want to lookup the products and return the category and subcategory i have reserached the best way to do this butt canot get my head around it, i have managed to return one lookup using the following.
SELECT products.product_code, products.description, product_category.category_name, product_category.category_name
FROM products
LEFT JOIN product_category ON product_category.id = products.category AND product_category.ID = products.subcategory
ORDER BY description ASC
Which returns as expected
UK001, Description Text, Network Cables
I also want to return the subcategory, adding another join does not work and this is where i am stuck,

You just need two joins:
SELECT p.product_code, p.description, pc.category_name, pcs.category_name
FROM products p LEFT JOIN
product_category pc
ON pc.id = p.category LEFT JOIN
product_category pcs
ON pcs.ID = p.subcategory
ORDER BY description ASC;
Note also the use of table aliases. These make the query easier to write and to read.

Related

duplicate data being return with varied # of results in cross join

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.

select names that is equal to the id of other tables - SQL

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.

Joining four mySQL tables without a single common value

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

MySQL join or subquery - get product data along with first product image

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

problem with the join query

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.