Multi INNER JOIN gives unexpected duplicate records - mysql

Running the following SELECT query gives unexpectedly two times the same record while there is only 1 product in the database. The are however multiple subcategories linked to the same category, but I still don't understand why this would give two results.
The ERD:
The full contents of the DB:
SELECT p.id AS productId, p.name AS productName FROM product p
INNER JOIN product_base AS pb ON pb.id = p.product_base_id
INNER JOIN product_category AS pc ON pc.id = pb.product_category_id
INNER JOIN product_subcategory AS psc ON psc.product_category_id = pc.id;
Returns:
Why is this product returned two times?
Appending WHERE psc.id = 2 will still give one product as a result, while the intention is that this product should only be found when psc.id = 1.
What am I missing here? Is there something wrong with the structure? How would I get all products that have a certain subcategory?
Would I need to store product_category_id and product_subcategory_id directly in product as well?

#barmar made me realize I am simply missing a direct FK from product to product_subcategory. Otherwise there is of course a missing link between the product and subcategory.

DISTINCT will filter out the duplicates.
SELECT DISTINCT p.id AS productId, p.name AS productName
FROM product p
INNER JOIN product_base AS pb ON pb.id = p.product_base_id
INNER JOIN product_category AS pc ON pc.id = pb.product_category_id
INNER JOIN product_subcategory AS psc ON psc.product_category_id = pc.id;

Related

SQL Query to see all productName and which category its belong

So I have a task all I need to do is to write Query to
select all the product names and which category they belong to.
I Using the tables from W3SCHOOL - https://www.w3schools.com/sql/trysql.asp?filename=trysql_select_all
I Would like to know why this Query -
SELECT p.ProductName
, c.CategoryName
From Products p
Join Categories c
ON p.ProductID = c.CategoryID
not working for me, it gives me only 8 Products with 8 different category names and I need all of the products name from each category.
thank you.
Your query is almost correct, you are joining on the wrong column,
SELECT Products.ProductName, Categories.CategoryName
FROM Products
JOIN Categories ON Categories.CategoryID = Products.CategoryID
should do.
The issue with your join query
You are joining Products.ProductID when you should join Products.CategoryID
The correct query should be as the following:
SELECT p.ProductName, c.CategoryName
FROM Products p
JOIN Categories c ON p.CategoryID = c.CategoryID

Join many to many table with others

I have a query that joins 3 tables on product id, here is the code.
SELECT oc_product.product_id, oc_product.image, oc_product.price, oc_product_description.name
FROM oc_product
JOIN oc_product_description
ON oc_product.product_id = oc_product_description.product_id;
I get the product_id, image, price and description, but I need to take the category name too, the product is in a many to many relationships with category table. Looks like this.
[oc_product_to_category * pivot table][1]
And I need to take category name from this table.
[oc_category_description][2]
[1] https://i.stack.imgur.com/5WAKd.png
[2] https://i.stack.imgur.com/YNcLn.png
Thank you so much, I am very stuck with this!
PS: I want to take category name and join with my code with other columns.
You only need to append 2 more JOINs to your query:
Join oc_product → oc_product_to_category to link products with their categories
Then Join oc_product_to_category → oc_category_description to grab the
category details
This way you can include to your query whichever fields you need from any of the joined tables.
SELECT p.product_id, p.image, p.price, d.name, c.name, c.description
FROM oc_product AS p
JOIN oc_product_description AS d
ON p.product_id = d.product_id
JOIN oc_product_to_category AS pc
ON pc.product_id = p.product_id
JOIN oc_category_description AS c
ON c.category_id = pc.category_id;
As you noted, the use of aliases for the actual table names helps to simplify a bit both the SELECT and the JOIN-ON segments of the query.
I've created a quick example here to show the results.

Mysql (doctrine) - count inner join having count > X

I have SQL to count products with specific properties. I am using it in the products filter. SQL is very long, but here is the primary part:
SELECT COUNT(products.id) as products_count, property_items.description, property_items.id as id
FROM property_items
INNER JOIN product_properties ON property_items.id = product_properties.property_item_id
INNER JOIN products ON product_properties.product_id
INNER JOIN product_properties pp ON products.id = pp.product_id AND (pp.property_item_id IN ($ids))
GROUP BY property_items.id
HAVING COUNT(pp.id) >= $countIds
This works perfectly when I have only the one element in $ids, but when i choose one more, the result is bad. It looks like the sql returns count of all products with any property from $ids, but I need to count only products that contains all properties.
First get all available properties. On each property join products that contains this property and go back to all properties of this product to check, if product contains already checked properties too. Or it is bad idea? I need to keep primary table (FROM table) as property_items.
I need to get result in this format:
=============================
id|description|products_count
=============================
1 |lorem ipsum|10
-----------------------------
2 |dolore sit |2
Thanks for any idea.
Try to use SELECT COUNT (DISTINCT products.id) as cnt
You can get the product ids that have all the properties by doing:
SELECT pp.property_id
FROM property_items pi INNER JOIN
product_properties pp
ON pi.id = pp.property_item_id INNER JOIN
products p
ON pp.product_id = p.id
WHERE pp.property_item_id IN ($ids)
GROUP BY pp.property_id
HAVING COUNT(DISTINCT pp.property_item_id) = $countIds -- has all of them
Note that I rationalized the joins. I think your simplification of the query wasn't quite right. I also added table aliases, so the query is easier to write and to read.
If you want the count of such products, use a subquery:
SELECT COUNT(*)
FROM (SELECT pp.property_id
FROM property_items pi INNER JOIN
product_properties pp
ON pi.id = pp.property_item_id INNER JOIN
products p
ON pp.product_id = p.id
WHERE find_in_set(pp.property_item_id, $ids)
GROUP BY pp.property_id
HAVING COUNT(DISTINCT pp.property_item_id) = $countIds -- has all of them
) ;
Your problem is probably because of this line:
WHERE pp.property_item_id IN ($ids)
If you are passing $ids as a comma-separated string, then your query will not work. Note the replacement above.

Using two inner join tables

I have come up with two queries, both use an inner join on two different tables.
Query 1
SELECT PRODUCTS.CODE, PRODUCTS.REFERENCE, PRODUCTS.TAXCAT, PRODUCTS.DISPLAY,PRODUCTS.NAME, PRODUCTS.PRICEBUY, PRODUCTS.PRICESELL, CATEGORIES.NAME AS CATEGORY
FROM PRODUCTS INNER JOIN CATEGORIES ON PRODUCTS.CATEGORY = CATEGORIES.ID;
Query 2
SELECT PRODUCTS.CODE, PRODUCTS.REFERENCE, PRODUCTS.TAXCAT, PRODUCTS.DISPLAY,PRODUCTS.NAME, PRODUCTS.PRICEBUY, PRODUCTS.PRICESELL,STOCKCURRENT.UNITS AS UNIT FROM PRODUCTS INNER JOIN STOCKCURRENT ON STOCKCURRENT.PRODUCT = PRODUCTS.ID;
Both queries run fine on their own, when I try to use both inner joins together I get errors. This is what I came up with on my own. I'm having trouble understanding the syntax to achieve this.
SELECT PRODUCTS.CODE, PRODUCTS.REFERENCE, PRODUCTS.TAXCAT,
PRODUCTS.DISPLAY,PRODUCTS.NAME, PRODUCTS.PRICEBUY,
PRODUCTS.PRICESELL,STOCKCURRENT.UNITS AS UNIT FROM PRODUCTS INNER JOIN
STOCKCURRENT ON STOCKCURRENT.PRODUCT = PRODUCTS.ID, CATEGORIES.NAME AS
CATEGORY FROM PRODUCTS INNER JOIN CATEGORIES ON PRODUCTS.CATEGORY =
CATEGORIES.ID;
Thank you.
Your attempted query has several syntax problems. Assuming you just want to join together the three tables, you may try the following query:
SELECT
p.CODE,
p.REFERENCE,
p.TAXCAT,
p.DISPLAY,
p.NAME,
p.PRICEBUY,
p.PRICESELL,
s.UNITS AS UNIT,
c.NAME AS CATEGORY
FROM PRODUCTS p
INNER JOIN STOCKCURRENT s
ON s.PRODUCT = p.ID
INNER JOIN CATEGORIES c
ON p.CATEGORY = c.ID;
Note that I introduced table aliases here. These aliases can be used elsewhere in the query to avoid having to repeat the entire table name.
By the way, I can also see taking a union of your two original queries. But without expected output, it was not entirely clear what you want.

SQL Join involving 3 tables, how to?

SQL newbie here.
So we have 3 tables:
categories(cat_id,name);
products(prod_id,name);
relationships(prod_id,cat_id);
It is a one-to-many relationship.
So, given a category name say "Books". How do I find all the products that come under books?
As an example,
categories(1,Books);
categories(2,Phones);
products(302,Sherlock Holmes);
relationships(302,1);
You need to JOIN the three tables.
SELECT p.*
FROM relationships r
INNER JOIN products p
ON p.prod_id = r.prod_id
INNER JOIN categories c
ON c.cat_d = r.cat_id
WHERE c.name = 'Books'
You have to join tables on related columns and specify WHERE clause to select all records where category name = 'Books'
SELECT p.*
FROM categories c
JOIN relationships r ON c.cat_id = r.cat_id
JOIN products p ON r.prod_id = p.prod_id
WHERE c.name = 'Books' -- or specify parameter like #Books
In SQL you often join related tables and beginners tend to join, whatever the situation. I would not recommend this. In your case you want to select products. If you only want to show products data, select from products only. You want to select products that are in the category 'Books' (or for which exists an entry in category 'Books'). Hence use an IN or EXISTS clause in order to find them:
select * from products
where prod_id in
(
select prod_id
from relationships
where cat_id = (select cat_id from categories where name = 'Books')
);
Thus you get a well structured query that tells the reader easily how the tables are related and what data you are actually interested in. Later, with different tables and data to select, this may keep you from duplicate result rows that you must get rid of by using DISTINCT or from getting wrong aggregates (sums, counts, etc.), because of mistakenly considering records multifold.
try this:
select p.Prod_id,p.name
from products p inner join relationships r on
p.prod_id = r.prod_id
where r.cat_id = (select cat_id from categories where name = 'books')
or
select p.Prod_id,p.name
from products p inner join relationships r on
p.prod_id = r.prod_id inner join categories c on c.cat_id = r.cat_id
where c.name = 'books'