'join table with itself to create more row

me has a table which has following data in TABLE1

enter image description here

me wanted to make sure all teh name has all "work type" available for all those dates only which is in teh data. Output should like TABLE2

enter image description here

me tried to put a query like

select t1.name,t1.date,mt.work_type,t1.minutes 
from table1 t1
right join (select distinct work_type from t1) mt on t1.work_type=mt.work_type

however it didn't work. Please halp



Solution 1:[1]

You need to join with a subquery that cross joins all the names, dates and work types.

SELECT mt.name, mt.date, mt.work_type, COALESCE(t1.minutes, 0) AS minutes
FROM (
    SELECT DISTINCT t1.name, t1.date, t2.work_type
    FROM table1 AS t1
    CROSS JOIN table1 AS t2
) AS mt
LEFT JOIN table1 AS t1 
    ON t1.name = mt.name AND t1.date = mt.date AND t1.work_type = mt.work_type
ORDER BY mt.name, mt.date, mt.work_type

Note that in the subquery you have to specify the actual table name, not the alias from the main query.

DEMO

Solution 2:[2]

In Oracle, you can use a partitioned outer join:

SELECT t.name,
       t."DATE",
       w.work_type,
       COALESCE(t.minutes, 0) AS minutes
FROM   (SELECT DISTINCT work_type FROM table1) w
       LEFT OUTER JOIN table1 t
       PARTITION BY (t.name, t."DATE")
       ON (w.work_type = t.work_type)

Which, for the sample data:

CREATE TABLE table1 (name, "DATE", work_type, minutes) AS
SELECT 'a', DATE '2011-01-01', 'labor',   53 FROM DUAL UNION ALL
SELECT 'a', DATE '2011-01-01', 'private', 58 FROM DUAL UNION ALL
SELECT 'a', DATE '2011-01-01', 'other',   19 FROM DUAL UNION ALL
SELECT 'b', DATE '2011-01-02', 'labor',   31 FROM DUAL UNION ALL
SELECT 'b', DATE '2011-01-02', 'other',   24 FROM DUAL UNION ALL
SELECT 'b', DATE '2011-01-01', 'private', 19 FROM DUAL UNION ALL
SELECT 'c', DATE '2011-01-03', 'labor',   25 FROM DUAL UNION ALL
SELECT 'c', DATE '2011-01-03', 'private', 50 FROM DUAL UNION ALL
SELECT 'c', DATE '2011-01-01', 'private', 23 FROM DUAL UNION ALL
SELECT 'd', DATE '2011-01-01', 'other',   20 FROM DUAL;

Outputs:

NAME DATE WORK_TYPE MINUTES
a 01-JAN-11 labor 53
a 01-JAN-11 other 19
a 01-JAN-11 private 58
b 01-JAN-11 labor 0
b 01-JAN-11 other 0
b 01-JAN-11 private 19
b 02-JAN-11 labor 31
b 02-JAN-11 other 24
b 02-JAN-11 private 0
c 01-JAN-11 labor 0
c 01-JAN-11 other 0
c 01-JAN-11 private 23
c 03-JAN-11 labor 25
c 03-JAN-11 other 0
c 03-JAN-11 private 50
d 01-JAN-11 labor 0
d 01-JAN-11 other 20
d 01-JAN-11 private 0

db<>fiddle here


In MySQL, you can use:

SELECT nd.name,
       nd.date,
       w.work_type,
       COALESCE(t.minutes, 0) AS minutes
FROM   (SELECT DISTINCT work_type FROM table1) w
       CROSS JOIN
       (SELECT DISTINCT name, date FROM table1) nd
       LEFT OUTER JOIN table1 t
       ON (t.name = nd.name AND t.date = nd.date AND t.work_type = w.work_type);

db<>fiddle here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2