sql >> Database >  >> RDS >> Database

Uw ultieme gids voor SQL Join:INNER JOIN - Deel 1

Inner join, outer join, cross join? Wat geeft?

Het is een geldige vraag. Ik zag ooit een Visual Basic-code met daarin T-SQL-codes. De VB-code haalt tabelrecords op met meerdere SELECT-instructies, één SELECT * per tabel. Vervolgens combineert het meerdere resultaatsets tot een recordset. Absurd?

Voor de jonge ontwikkelaars die het deden, was het dat niet. Maar toen ze me vroegen te evalueren waarom het systeem traag was, was dat het eerste dat mijn aandacht trok. Dat klopt. Ze hebben nog nooit van SQL-joins gehoord. Eerlijk gezegd waren ze eerlijk en stonden ze open voor suggesties.

Hoe beschrijf je SQL-joins? Misschien herinner je je één nummer - Stel je voor door John Lennon:

Je zou kunnen zeggen dat ik een dromer ben, maar ik ben niet de enige.

Ik hoop dat je je ooit bij ons zult voegen, en dat de wereld één zal zijn.

In de context van het nummer is samenvoegen verenigen. In een SQL-database vormt het combineren van records van 2 of meer tabellen in één resultaatset een join .

Dit artikel is het begin van een driedelige serie over SQL-joins:

  • INNER JOIN
  • OUTER JOIN, inclusief LEFT, RIGHT en FULL
  • CROSS JOIN

Maar voordat we INNER JOIN gaan bespreken, laten we de joins in het algemeen beschrijven.

Meer over SQL JOIN

Joins verschijnen direct na de FROM-clausule. In zijn eenvoudigste vorm lijkt het op het gebruik van de SQL-92-standaard:

FROM <table source> [<alias1>]
<join type> JOIN <table source> [<alias2>] [ON <join condition>] 
[<join type> JOIN <table source> [<alias3>] [ON <join condition>]
<join type> JOIN <table source> [<aliasN>] [ON <join condition>]]
[WHERE <condition>]

Laten we de alledaagse dingen rondom de JOIN beschrijven.

Tabelbronnen

Volgens Microsoft kun je maximaal 256 tabellen toevoegen. Het hangt natuurlijk af van uw serverbronnen. Ik heb in mijn leven nog nooit aan meer dan 10 tafels deelgenomen, om nog maar te zwijgen van 256. Hoe dan ook, tabelbronnen kunnen een van de volgende zijn:

  • Tabel
  • Bekijken
  • Tabel of bekijk synoniem
  • Tabelvariabele
  • Tabelwaardefunctie
  • Afgeleide tabel

Tabelalias

Een alias is optioneel, maar het verkort uw code en minimaliseert typen. Het helpt u ook fouten te voorkomen wanneer een kolomnaam voorkomt in twee of meer tabellen die worden gebruikt in een SELECT, UPDATE, INSERT of DELETE. Het voegt ook duidelijkheid toe aan uw code. Het is optioneel, maar ik raad het gebruik van aliassen aan. (Tenzij je graag tabelbronnen op naam typt.)

Voorwaarde voor deelname

Het sleutelwoord ON gaat vooraf aan de join-voorwaarde die een enkele join of kolommen met twee sleutels uit de 2 samengevoegde tabellen kan zijn. Of het kan een samengestelde join zijn die meer dan 2 sleutelkolommen gebruikt. Het definieert hoe tabellen gerelateerd zijn.

We gebruiken de join-voorwaarde echter alleen voor INNER- en OUTER-joins. Als u het op een CROSS JOIN gebruikt, wordt er een fout gegenereerd.

Omdat samenvoegvoorwaarden de relaties definiëren, hebben ze operators nodig.

De meest voorkomende operator voor join-voorwaarden is de operator gelijkheid (=). Andere operators zoals> of

SQL JOIN versus subquery's

De meeste joins kunnen worden herschreven als subquery's en omgekeerd. Bekijk dit artikel voor meer informatie over subquery's in vergelijking met joins.

Joins en afgeleide tabellen

Het gebruik van afgeleide tabellen in een join ziet er als volgt uit:

FROM table1 a
INNER JOIN (SELECT y.column3 from table2 x
            INNER JOIN table3 y on x.column1 = y.column1) b ON a.col1 = b.col2

Het voegt zich bij het resultaat van een andere SELECT-instructie, en het is volkomen geldig.

U zult meer voorbeelden hebben, maar laten we nog een laatste ding over SQL JOINS behandelen. Het is hoe de Query Optimizer-processen van SQL Server joins verwerken.

Hoe SQL Server-processen samenkomen

Om te begrijpen hoe het proces werkt, moet u de twee soorten bewerkingen kennen:

  • Logische bewerkingen komen overeen met de typen joins die in een query worden gebruikt:INNER, OUTER of CROSS. U, als ontwikkelaar, definieert dit deel van de verwerking bij het vormen van de query.
  • Fysieke handelingen – de Query Optimizer kiest de beste fysieke bewerking die van toepassing is op uw deelname. De beste betekent de snelste om resultaten te produceren. Het uitvoeringsplan van uw query toont de gekozen fysieke join-operators. Deze bewerkingen zijn:
    • Geneste loop-join. Deze bewerking is snel als een van de twee tabellen klein is en de tweede groot en geïndexeerd. Het vereist de minste I/O met de minste vergelijkingen, maar het is niet goed voor grote resultatensets.
    • Samenvoegen samen. Dit is de snelste bewerking voor grote en gesorteerde resultatensets op basis van kolommen die in de samenvoeging worden gebruikt.
    • Hash Join. De query-optimizer gebruikt het wanneer de resultatenset te groot is voor een geneste lus en de invoer ongesorteerd is voor een samenvoeg-join. Een hash is efficiënter dan deze eerst te sorteren en een Merge-join toe te passen.
    • Adaptieve deelname. Vanaf SQL Server 2017 maakt het de keuze tussen een geneste lus of hash mogelijk . De join-methode wordt uitgesteld totdat de eerste invoer is gescand. Deze bewerking schakelt dynamisch over naar een betere fysieke join zonder opnieuw te compileren.

Waarom moeten we ons hier druk over maken?

Eén woord:prestatie.

Eén ding is om te weten hoe u query's met joins moet vormen om correcte resultaten te produceren. Een andere is om het zo snel mogelijk te laten lopen. U moet zich hier extra zorgen over maken als u een goede reputatie bij uw gebruikers wilt hebben.

Dus, waar moet je op letten in het Uitvoeringsplan voor deze logische bewerkingen?

  • Stel dat een sorteeroperator voorafgaat aan de samenvoegverbinding . Deze sorteerbewerking is duur voor grote tabellen (Figuur 2). U kunt dit oplossen door de invoertabellen in de join vooraf te sorteren.
  • Stel dat er duplicaten zijn in de invoertabellen van een Merge-join . SQL Server schrijft de duplicaten van de tweede tabel naar een werktabel in tempdb. Dan zal het de vergelijkingen daar maken. De STATISTICS IO zal alle betrokken werktabellen onthullen.
  • Als er enorme hoeveelheden gegevens naar tempdb worden gemorst in een hash-jo in, zal de STATISTICS IO een grote logische read op WorkFiles of WorkTables onthullen. Er verschijnt ook een waarschuwing in het Uitvoeringsplan (Figuur 3). U kunt twee dingen toepassen:de invoertabellen voorsorteren of de joins verminderen, indien mogelijk. Als gevolg hiervan kan de Query Optimizer een andere fysieke join kiezen.

Doe mee met hints

Deelname-hints zijn nieuw in SQL Server 2019. Wanneer u deze in uw joins gebruikt, vertelt het de query-optimizer om te stoppen met beslissen wat het beste is voor de query. Jij bent de baas als het gaat om de te gebruiken fysieke join.

Stop nu, daar. De waarheid is dat de query-optimizer doorgaans de beste fysieke join voor uw query selecteert. Als je niet weet wat je doet, gebruik dan geen hints om mee te doen.

De mogelijke hints die je kunt specificeren zijn LOOP, MERGE, HASH of REMOTE.

Ik heb geen join-hints gebruikt, maar hier is de syntaxis:


<join type> <join hint> JOIN <table source> [<alias>] ON <join condition>

Alles over INNER JOIN

INNER JOIN retourneert de rijen met overeenkomende records in beide tabellen, op basis van een voorwaarde. Het is ook de standaard join als u het trefwoord INNER niet opgeeft:

Zoals u ziet, overeenkomende rijen uit Tabel1 en Tabel2 worden geretourneerd met Sleutel1 als de verbindingsvoorwaarde. De Tabel1 opnemen met Sleutel1 ='C' is uitgesloten omdat er geen overeenkomende records zijn in Tabel2 .

Wanneer ik een vraag stel, is mijn eerste keuze INNER JOIN. OUTER JOIN komt alleen wanneer de vereisten het dicteren.

INNER JOIN-syntaxis

Er worden twee INNER JOIN-syntaxis ondersteund in T-SQL:SQL-92 en SQL-89.

SQL-92 INNER JOIN

FROM <table source1> [<alias1>]
INNER JOIN <table source2> [<alias2>] ON <join condition1>
[INNER JOIN <table source3> [<alias3>] ON <join condition2>
 INNER JOIN <table sourceN> [<aliasN>] ON <join conditionN>]
[WHERE <condition>]

SQL-89 INNER JOIN

FROM <table source1> [alias1], <table source2> [alias2] [, <table source3> [alias3], <table sourceN> [aliasN]]
WHERE (<join condition1>)
[AND (<join condition2>)
AND (<join condition3>)
AND (<join conditionN>)]

Welke INNER JOIN-syntaxis is beter?

De eerste join-syntaxis die ik leerde was SQL-89. Toen SQL-92 eindelijk arriveerde, vond ik het te lang. Ik dacht ook dat de uitvoer hetzelfde was, waarom zou ik dan nog meer zoekwoorden typen? Een grafische ontwerper van query's had de gegenereerde code SQL-92, en ik veranderde het terug naar SQL-89. Maar vandaag geef ik de voorkeur aan SQL-92, zelfs als ik meer moet typen. Dit is waarom:

  • De bedoeling van het type join is duidelijk. De volgende jongen of meid die mijn code zal onderhouden, weet wat de bedoeling is van de zoekopdracht.
  • Het vergeten van de join-voorwaarde in een SQL-92-syntaxis leidt tot een fout. Ondertussen wordt het vergeten van de join-voorwaarde in SQL-89 behandeld als een CROSS JOIN. Als ik een INNER of OUTER join bedoelde, zou het een onmerkbare logische bug zijn totdat de gebruikers klagen.
  • Nieuwe tools zijn meer geneigd tot SQL-92. Als ik ooit weer een grafische queryontwerper gebruik, hoef ik die niet te wijzigen in SQL-89. Ik ben niet langer koppig, dus mijn hartslag is weer normaal. Proost op mij.

Bovenstaande redenen zijn van mij. Je hebt misschien je redenen waarom je de voorkeur geeft aan SQL-92 of waarom je er een hekel aan hebt. Ik vraag me af wat die redenen zijn. Laat het me weten in het gedeelte Opmerkingen hieronder.

Maar we kunnen dit artikel niet beëindigen zonder voorbeelden en uitleg.

10 INNER JOIN-voorbeelden

1. 2 tafels samenvoegen

Hier is een voorbeeld van 2 tabellen die samengevoegd zijn met INNER JOIN in SQL-92-syntaxis.

-- Display Vests, Helmets, and Light products

USE AdventureWorks
GO

SELECT
 p.ProductID
,P.Name AS [Product]
,ps.ProductSubcategoryID
,ps.Name AS [ProductSubCategory]
FROM Production.Product p
INNER JOIN Production.ProductSubcategory ps ON P.ProductSubcategoryID = ps.ProductSubcategoryID
WHERE P.ProductSubcategoryID IN (25, 31, 33);  -- for vest, helmet, and light 
                                            -- product subcategories

U geeft alleen de kolommen op die u nodig heeft. In het bovenstaande voorbeeld zijn 4 kolommen gespecificeerd. Ik weet dat het te lang is dan SELECT * maar onthoud dit:het is de beste methode.

Let ook op het gebruik van tabelaliassen. Zowel het Product en ProductSubcategory tabellen hebben een kolom met de naam [Naam ]. Als u de alias niet opgeeft, wordt er een fout geactiveerd.

Ondertussen is hier de equivalente SQL-89-syntaxis:

-- Display Vests, Helmets, and Light products

USE AdventureWorks
GO

SELECT
 p.ProductID
,P.Name AS [Product]
,ps.ProductSubcategoryID
,ps.Name AS [ProductSubCategory]
FROM Production.Product p, Production.ProductSubcategory ps 
WHERE P.ProductSubcategoryID = ps.ProductSubcategoryID
AND P.ProductSubcategoryID IN (25, 31, 33);

Ze zijn hetzelfde, behalve de join-voorwaarde die in de WHERE-component is gemengd met een AND-sleutelwoord. Maar onder de motorkap, zijn ze echt hetzelfde? Laten we de resultatenset, de STATISTICS IO en het uitvoeringsplan eens bekijken.

Bekijk de resultatenset van 9 records:

Het zijn niet alleen de resultaten, maar ook de resources die SQL Server nodig heeft, zijn hetzelfde.

Zie de logische waarden:

Ten slotte onthult het uitvoeringsplan hetzelfde queryplan voor beide query's wanneer hun QueryPlanHashes zijn gelijk. Let ook op de gemarkeerde bewerkingen in het diagram:

Op basis van de bevindingen is de SQL Server-queryverwerking hetzelfde, of het nu SQL-92 of SQL-89 is. Maar zoals ik al zei, de duidelijkheid in SQL-92 is veel beter voor mij.

Afbeelding 7 toont ook een geneste lusverbinding die in het plan wordt gebruikt. Waarom? De resultaatset is klein.

2. Meerdere tabellen samenvoegen

Bekijk de onderstaande zoekopdracht met behulp van 3 samengevoegde tabellen.

-- Get the total number of orders per Product Category
USE AdventureWorks
GO

SELECT
 ps.Name AS ProductSubcategory
,SUM(sod.OrderQty) AS TotalOrders
FROM Production.Product p
INNER JOIN Sales.SalesOrderDetail sod ON P.ProductID = sod.ProductID
INNER JOIN Sales.SalesOrderHeader soh ON sod.SalesOrderID = soh.SalesOrderID
INNER JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID
WHERE soh.OrderDate BETWEEN '1/1/2014' AND '12/31/2014'
AND p.ProductSubcategoryID IN (1,2)
GROUP BY ps.Name
HAVING ps.Name IN ('Mountain Bikes', 'Road Bikes')

3. Samengestelde deelname

Je kunt ook 2 tabellen samenvoegen met 2 sleutels om het te relateren. Bekijk het voorbeeld hieronder. Het gebruikt 2 voorwaarden voor samenvoegen met een AND-operator.

SELECT
 a.column1
,b.column1
,b.column2
FROM Table1 a
INNER JOIN Table2 b ON a.column1 = b.column1 AND a.column2 = b.column2

4. INNER JOIN Fysieke join met geneste lus gebruiken

In het onderstaande voorbeeld is het Product tafel heeft 9 records - een kleine set. De samengevoegde tabel is SalesOrderDetail - een grote set. De Query Optimizer gebruikt een geneste lus-join, zoals weergegeven in afbeelding 8.

USE AdventureWorks
GO

SELECT
 sod.SalesOrderDetailID
,sod.ProductID
,P.Name
,P.ProductNumber
,sod.OrderQty
FROM Sales.SalesOrderDetail sod
INNER JOIN Production.Product p ON sod.ProductID = p.ProductID
WHERE P.ProductSubcategoryID IN(25, 31, 33);

5. INNER JOIN Een fysieke samenvoeging gebruiken

In het onderstaande voorbeeld wordt een Merge Join gebruikt omdat beide invoertabellen zijn gesorteerd op SalesOrderID.

SELECT
 soh.SalesOrderID
,soh.OrderDate
,sod.SalesOrderDetailID
,sod.ProductID
,sod.LineTotal
FROM Sales.SalesOrderHeader soh
INNER JOIN sales.SalesOrderDetail sod ON soh.SalesOrderID = sod.SalesOrderID

6. INNER JOIN Een fysieke hash-join gebruiken

In het volgende voorbeeld wordt een hash-join gebruikt:

SELECT
 s.Name AS Store
,SUM(soh.TotalDue) AS TotalSales
FROM Sales.SalesOrderHeader soh
INNER JOIN Sales.Store s ON soh.SalesPersonID = s.SalesPersonID
GROUP BY s.Name

7. INNER JOIN Adaptive Physical Join gebruiken

In het onderstaande voorbeeld is de SalesPerson tabel heeft een niet-geclusterde ColumnStore-index op de TerritoryID kolom. De Query Optimizer heeft gekozen voor een geneste lusverbinding, zoals weergegeven in afbeelding 11.

SELECT
sp.BusinessEntityID
,sp.SalesQuota
,st.Name AS Territory
FROM Sales.SalesPerson sp
INNER JOIN Sales.SalesTerritory st ON sp.TerritoryID = st.TerritoryID
WHERE sp.TerritoryID BETWEEN 1 AND 5

8. Twee manieren om een ​​subquery te herschrijven naar een INNER JOIN

Overweeg deze verklaring met een geneste subquery:

SELECT [SalesOrderID], [OrderDate], [ShipDate], [CustomerID]
FROM Sales.SalesOrderHeader 
WHERE [CustomerID] IN (SELECT [CustomerID] FROM Sales.Customer
			WHERE PersonID IN (SELECT BusinessEntityID FROM Person.Person
                                          WHERE lastname LIKE N'I%' AND PersonType='SC'))

Dezelfde resultaten kunnen verschijnen als je het verandert in een INNER JOIN, zoals hieronder:

SELECT o.[SalesOrderID], o.[OrderDate], o.[ShipDate], o.[CustomerID]
FROM Sales.SalesOrderHeader o
INNER JOIN Sales.Customer c on o.CustomerID = c.CustomerID
INNER JOIN Person.Person p ON c.PersonID = p.BusinessEntityID
WHERE p.PersonType = 'SC'
AND p.lastname LIKE N'I%'

Een andere manier om het te herschrijven is door een afgeleide tabel te gebruiken als tabelbron voor de INNER JOIN:

SELECT o.[SalesOrderID], o.[OrderDate], o.[ShipDate], o.[CustomerID]
FROM Sales.SalesOrderHeader o
INNER JOIN (SELECT c.CustomerID, P.PersonType, P.LastName
            FROM Sales.Customer c
            INNER JOIN Person.Person p ON c.PersonID = P.BusinessEntityID
	      WHERE p.PersonType = 'SC'
	        AND p.LastName LIKE N'I%') AS q ON o.CustomerID = q.CustomerID

Alle 3 de zoekopdrachten leveren dezelfde 48 records op.

9. Deelnametips gebruiken

De volgende query gebruikt een geneste lus:

SELECT
 sod.SalesOrderDetailID
,sod.ProductID
,P.Name
,P.ProductNumber
,sod.OrderQty
FROM Sales.SalesOrderDetail sod
INNER JOIN Production.Product p ON sod.ProductID = p.ProductID
WHERE P.ProductSubcategoryID IN(25, 31, 33);

Als je het tot een hash-join wilt forceren, gebeurt dit als volgt:

SELECT
 sod.SalesOrderDetailID
,sod.ProductID
,P.Name
,P.ProductNumber
,sod.OrderQty
FROM Sales.SalesOrderDetail sod
INNER HASH JOIN Production.Product p ON sod.ProductID = p.ProductID
WHERE P.ProductSubcategoryID IN(25, 31, 33);

Houd er echter rekening mee dat STATISTICS IO laat zien dat de prestaties slecht zullen worden als je het dwingt tot een hash-join.

Ondertussen gebruikt de onderstaande query een Merge Join:

SELECT
 soh.SalesOrderID
,soh.OrderDate
,sod.SalesOrderDetailID
,sod.ProductID
,sod.LineTotal
FROM Sales.SalesOrderHeader soh
INNER JOIN sales.SalesOrderDetail sod ON soh.SalesOrderID = sod.SalesOrderID

Dit wordt het als je het dwingt tot een geneste lus:

SELECT
 soh.SalesOrderID
,soh.OrderDate
,sod.SalesOrderDetailID
,sod.ProductID
,sod.LineTotal
FROM Sales.SalesOrderHeader soh
INNER LOOP JOIN sales.SalesOrderDetail sod ON soh.SalesOrderID = sod.SalesOrderID

Na het controleren van de STATISTICS IO van beide, vereist het forceren van een geneste lus meer middelen om de query te verwerken:

Het gebruik van join-hints zou dus je laatste redmiddel moeten zijn bij het tweaken voor prestaties. Laat uw SQL Server het voor u afhandelen.

10. INNER JOIN gebruiken in UPDATE

U kunt INNER JOIN ook gebruiken in een UPDATE-statement. Hier is een voorbeeld:

UPDATE Sales.SalesOrderHeader
SET ShipDate = getdate() 
FROM Sales.SalesOrderHeader o
INNER JOIN Sales.Customer c on o.CustomerID = c.CustomerID
INNER JOIN Person.Person p ON c.PersonID = p.BusinessEntityID
WHERE p.PersonType = 'SC'

Aangezien het mogelijk is om een ​​join in een UPDATE te gebruiken, waarom probeert u het dan niet met DELETE en INSERT?

SQL Join en INNER JOIN Takeaways

Dus, wat is er zo belangrijk aan SQL-join?

  • Een SQL JOIN combineert records van 2 of meer tabellen om één resultaatset te vormen.
  • Er zijn typen joins in SQL:INNER, OUTER en CROSS.
  • Als ontwikkelaar of beheerder bepaalt u welke logische bewerkingen of join-types u wilt gebruiken voor uw vereisten.
  • Aan de andere kant bepaalt de Query Optimizer welke fysieke join-operators het beste kunnen worden gebruikt. Dit kan een geneste lus, samenvoegen, hash of adaptief zijn.
  • Je kunt join-hints gebruiken om te forceren welke fysieke join je moet gebruiken, maar dit zou je laatste redmiddel moeten zijn. In de meeste gevallen is het beter om uw SQL Server het af te laten handelen.
  • Het kennen van de fysieke join-operators helpt je ook bij het afstemmen van de queryprestaties.
  • Bovendien kunnen subquery's worden herschreven met behulp van joins.

Ondertussen liet dit bericht 10 voorbeelden van INNER JOINs zien. Het zijn niet alleen voorbeeldcodes. Sommige bevatten ook een inspectie van hoe de code van binnenuit werkt. Het is niet alleen bedoeld om u te helpen coderen, maar om u te helpen bewust te zijn van de prestaties. Uiteindelijk moeten de resultaten niet alleen correct zijn, maar ook snel worden geleverd.

We zijn nog niet klaar. Het volgende artikel gaat over OUTER JOINS. Blijf op de hoogte.

Zie ook

Met SQL-joins kunt u gegevens uit meer dan één tabel ophalen en combineren. Bekijk deze video voor meer informatie over SQL Joins.


  1. Hoe items uit de sqlite-database te verwijderen met de SQLiteOpenHelper-klasse

  2. Een zeer beschikbare Nextcloud implementeren met MySQL Galera Cluster en GlusterFS

  3. SQLAlchemy meerdere externe sleutels in één toegewezen klasse aan dezelfde primaire sleutel

  4. Hasj Wat? Hash-indexen begrijpen