Search code examples
mysqljoinunion

How to get all records from a third table discriminating by the join of another even when there are no matches


I have a problem getting all the records from a table, even if there is no relationship. My first table (warehouse) has a warehouse type, which is a foreign key from another table. There is another table that contains all the types of products that can exist in the different warehouses, and they are defined based on the type of warehouse (my relation table between types of warehouse and types of products). My goal is to obtain all the types of products, indicating which ones are assigned to the warehouse based on its type and which ones are not.

I have tried my query in different ways: with UNION, subqueries but I do not achieve my goal. I include the fiddle with the query that is closest to the expected result:

SQL Fiddle

SELECT DISTINCT w.id, w.name, wht.type AS whouse_type, APT.product_type_id, APT.product_type,
CASE
    WHEN w.type_id = APT.warehouse_type_id
        THEN "YES"
    WHEN w.type_id <> APT.warehouse_type_id
        THEN "NO"
    WHEN APT.warehouse_type_id IS NULL
        THEN "NO"
    ELSE NULL
END AS assigned
FROM warehouse w
LEFT JOIN warehouse_types wht ON w.type_id = wht.id
CROSS JOIN (
    SELECT DISTINCT pt.id AS product_type_id, pt.product_type, rel.warehouse_type_id
    FROM product_types pt 
    LEFT JOIN products_type_by_wh_types rel ON pt.id = rel.product_type_id
  ) AS APT -- all products types 
where w.id = 1
ORDER BY w.name, APT.product_type_id, type_of_warehouse;

This gave me:

+----+-------------+-------+--------------+----------+
| id | whouse_type | pt_id | product_type | assigned |
+----+-------------+-------+--------------+----------+
| 1  | NORTH       |   1   | Industry     | YES      |
+----+-------------+-------+--------------+----------+
| 1  | NORTH       |   1   | Industry     | NO       |
+----+-------------+-------+--------------+----------+
| 1  | NORTH       |   2   | Transport    | NO       |
+----+-------------+-------+--------------+----------+
| 1  | NORTH       |   3   | Chemicals    | NO       |
+----+-------------+-------+--------------+----------+
| 1  | NORTH       |   4   | Food and B   | YES      |
+----+-------------+-------+--------------+----------+
| 1  | NORTH       |   4   | Food and B   | NO       |
+----+-------------+-------+--------------+----------+

And the expect result should be:

+----+-------------+-------+--------------+----------+
| id | whouse_type | pt_id | product_type | assigned |
+----+-------------+-------+--------------+----------+
| 1  | NORTH       |   1   | Industry     | YES      |
+----+-------------+-------+--------------+----------+
| 1  | NORTH       |   2   | Transport    | NO       |
+----+-------------+-------+--------------+----------+
| 1  | NORTH       |   3   | Chemicals    | NO       |
+----+-------------+-------+--------------+----------+
| 1  | NORTH       |   4   | Food and B   | YES      |
+----+-------------+-------+--------------+----------+

Solution

  • As @Barmar suggested (once again, thank you!) the solution is adding a GROUP BY and make use of the MAX function for my CASE. This is the final query:

    SELECT DISTINCT w.id, w.name, wht.type AS whouse_type, APT.product_type_id, APT.product_type,
    MAX(CASE
        WHEN w.type_id = APT.warehouse_type_id
            THEN 1
        WHEN w.type_id <> APT.warehouse_type_id
            THEN 0
        WHEN APT.warehouse_type_id IS NULL
            THEN 0
        ELSE NULL
    END) AS assigned
    FROM warehouse w
    LEFT JOIN warehouse_types wht ON w.type_id = wht.id
    CROSS JOIN (
        SELECT DISTINCT pt.id AS product_type_id, pt.product_type, rel.warehouse_type_id
        FROM product_types pt 
        LEFT JOIN products_type_by_wh_types rel ON pt.id = rel.product_type_id
      ) AS APT -- all products types 
    GROUP BY w.id, APT.product_type_id
    ORDER BY w.name, APT.product_type_id, assigned;