Für Leute, die parallel mit mehreren RDBMS zu arbeiten haben - so wie ich -, ist es wichtig, die kleinen Unterschiede in der Implentierung im Blick zu behalten. Dabei sind Artikel wie die Untersuchung "NESTED LOOP and full/right outer join in modern RDBMS" von Mohamed Houri ausgesprochen nützlich. Darin erfährt man unter anderem Folgendes:
- weder Oracle, noch der SQL Server, noch Postgres können einen Nested Loop Join verwenden, um einen RIGHT OUTER JOIN auszuführen: sie alle behelfen sich damit, die Mengen in der Operation umzudrehen und einen LEFT OUTER JOIN durchzuführen. Da ich persönlich auch immer nur Left Outer Joins schreibe, habe ich mit dieser Strategie keine Probleme.
- auch mit dem FULL OUTER JOIN gibt es Beschränkungen: Postgres verwendet grundsätzlich keinen Nested Loop Join für diese Operation, sondern immer nur einen HAST bzw. MERGE JOIN.
- Oracle und der SQL Server wandeln die Operation intern um in eine Operation der Form
T1 LEFT OUTER JOIN T2
UNION ALL
T2 ANTI JOIN T1
Für Postgres könnte das ein Grund sein, eine solche Query umzubauen, um manuell die Nested Loop Operation zu ermöglichen (das passende Beispiel zur Umformulierung fände man im Artikel).
Die Umwandlung von FULL OUTER JOIN in (LEFT OUTER JOIN) UNION ALL (ANTI JOIN) für SQL Server ist eine Optimizer-Entscheidung.
AntwortenLöschenZ.B. wird auf einer Tabelle mit 100K Zeilen
CREATE TABLE dbo.Numbers(Number INT NOT NULL);
ein Ausführungsplan mit Hash Match produziert.
SELECT * FROM dbo.Numbers n1
FULL OUTER JOIN dbo.Numbers n2
ON n1.Number = n2.Number
|--Hash Match(Full Outer Join, HASH:([n1].[Number])=([n2].[Number]))
|--Table Scan(OBJECT:([dbo].[Numbers] AS [n1]))
|--Table Scan(OBJECT:([dbo].[Numbers] AS [n2]))
Wenn die Werte günstig vorsortiert sind (z.B. durch einen Clustered Index), bekommt man einen Merge Join:
ALTER TABLE dbo.Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number ASC);
|--Merge Join(Full Outer Join, MERGE:([n1].[Number])=([n2].[Number]), RESIDUAL:([dbo].[Numbers].[Number] as [n1].[Number]=[dbo].[Numbers].[Number] as [n2].[Number]))
|--Clustered Index Scan(OBJECT:([dbo].[Numbers].[PK_Numbers] AS [n1]), ORDERED FORWARD)
|--Clustered Index Scan(OBJECT:([dbo].[Numbers].[PK_Numbers] AS [n2]), ORDERED FORWARD)