SQL Server Join – Show Missing Data from 2 Tables with JOIN

sql servert-sql

I have 3 DB tables, a Computer table, a Patch table, and a ComputerPatched table. Below is a table mock up to assist my explanation….

DECLARE @Computer AS TABLE (
    [ComputerID] int
    , [ComputerName] nvarchar(50)
);

INSERT INTO @Computer ([ComputerID], [ComputerName])
VALUES (1, 'Computer A'), (2, 'Computer B'), (3, 'Computer C'), (4, 'Computer D');

DECLARE @Patch AS TABLE (
    [PatchID] int
    , [PatchName] nvarchar(50)
);

INSERT INTO @Patch ([PatchID], [PatchName])
VALUES (1, 'Security A'), (2, 'Security B'), (3, 'Security C'), (4, 'Security D');

DECLARE @ComputerPatched AS TABLE (
    [ComputerID] int
    , [PatchID] int
    , [InstallDate] datetime
);

INSERT INTO @ComputerPatched ([ComputerID], [PatchID], [InstallDate])
VALUES 
    (1, 1, '20200708'), (1, 3, '20200115')
    , (2, 1, '20200615'), (2, 3, '20200615'), (2, 4, '20200215')
    , (3, 4, '20200427')
;

What I am trying to achieve is to JOIN all results from the Computer table AND Patch table to find out what patches have been installed on what computers and what patches have not been installed. The result I would be expecting would look like something as below…

+------------+--------------+---------+------------+-----------+
| ComputerID | ComputerName | PatchID | PatchName  | Installed |
+------------+--------------+---------+------------+-----------+
|          1 | Computer A   |       1 | Security A | 20200708  |
|          1 | Computer A   |       2 | Security B | NULL      |
|          1 | Computer A   |       3 | Security C | 20200115  |
|          1 | Computer A   |       4 | Security D | NULL      |
|          2 | Computer B   |       1 | Security A | 20200615  |
|          2 | Computer B   |       2 | Security B | NULL      |
|          2 | Computer B   |       3 | Security C | 20200615  |
|          2 | Computer B   |       4 | Security D | 20200215  |
|          3 | Computer C   |       1 | Security A | NULL      |
|          3 | Computer C   |       2 | Security B | NULL      |
|          3 | Computer C   |       3 | Security C | 20200427  |
|          3 | Computer C   |       4 | Security D | NULL      |
|          4 | Computer D   |       1 | Security A | NULL      |
|          4 | Computer D   |       2 | Security B | NULL      |
|          4 | Computer D   |       3 | Security C | NULL      |
|          4 | Computer D   |       4 | Security D | NULL      |
+------------+--------------+---------+------------+-----------+

I am having trouble envisioning how to write this query I believe I would need some form of OUTER JOIN but everything I have tried has failed. Any help would be much appreciated!

Best Answer

You will need a CARTESIAN PRODUCT between computer and patch. You can then left join to get an eventual installdate:

SELECT c.[ComputerID], c.[ComputerName], p.[PatchID], p.[PatchName], cp.[InstallDate]
FROM @Computer c
CROSS JOIN @Patch p
LEFT JOIN @ComputerPatched cp
   ON c.[ComputerID] = cp.[ComputerID]
   AND p.[PatchID] = cp.[PatchID];

Fiddle

I would advise not to quote identifiers unless necessary.