944 Stimmen

Verbinden mit der ersten Reihe

Ich möchte ein konkretes, aber hypothetisches Beispiel anführen.

Jede Bestellung hat normalerweise nur einen Einzelposten :

Bestellungen:

OrderGUID   OrderNumber
=========   ============
{FFB2...}   STL-7442-1      
{3EC6...}   MPT-9931-8A

Einzelposten:

LineItemGUID   Order ID Quantity   Description
============   ======== ========   =================================
{098FBE3...}   1        7          prefabulated amulite
{1609B09...}   2        32         spurving bearing

Gelegentlich kommt es jedoch vor, dass ein Auftrag zwei Positionen enthält:

LineItemID   Order ID    Quantity   Description
==========   ========    ========   =================================
{A58A1...}   6,784,329   5          pentametric fan
{0E9BC...}   6,784,329   5          differential girdlespring 

Normalerweise, wenn die Bestellungen dem Benutzer angezeigt werden:

SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
    INNER JOIN LineItems 
    ON Orders.OrderID = LineItems.OrderID

Ich möchte den einzelnen Artikel auf der Bestellung anzeigen. Aber mit dieser gelegentlichen Bestellung, die zwei (oder mehr) Artikel enthält, würden die Bestellungen erscheinen sein vervielfältigt :

OrderNumber   Quantity   Description
===========   ========   ====================
STL-7442-1    7          prefabulated amulite
MPT-9931-8A   32         spurving bearing
KSG-0619-81   5          panametric fan
KSG-0619-81   5          differential girdlespring

Was ich wirklich möchte, ist, dass SQL Server Wählen Sie einfach einen aus wie es sein wird gut genug :

OrderNumber   Quantity   Description
===========   ========   ====================
STL-7442-1    7          prefabulated amulite
MPT-9931-8A   32         differential girdlespring
KSG-0619-81   5          panametric fan

Wenn ich abenteuerlustig bin, könnte ich dem Benutzer eine Ellipse zeigen, um anzuzeigen, dass es mehr als eine gibt:

OrderNumber   Quantity   Description
===========   ========   ====================
STL-7442-1    7          prefabulated amulite
MPT-9931-8A   32         differential girdlespring
KSG-0619-81   5          panametric fan, ...

Die Frage ist also, wie man entweder

  • Beseitigung "doppelter" Zeilen
  • nur mit einer der Zeilen verknüpfen, um Doppelungen zu vermeiden

Erster Versuch

Mein erster naiver Versuch bestand darin, sich nur mit dem " TOP 1 Einzelposten":

SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
    INNER JOIN (
       SELECT TOP 1 LineItems.Quantity, LineItems.Description
       FROM LineItems
       WHERE LineItems.OrderID = Orders.OrderID) LineItems2
    ON 1=1

Aber das führt zu dem Fehler:

Die Spalte oder das Präfix 'Orders' bedeutet nicht
Übereinstimmung mit einem Tabellennamen oder einem Aliasnamen
in der Abfrage verwendet.

Vermutlich, weil der innere Select die äußere Tabelle nicht sieht.

4 Stimmen

Können Sie nicht group by ?

4 Stimmen

Ich denke (und korrigieren Sie mich, wenn ich falsch liege) group by müssten alle anderen Spalten aufgelistet werden, mit Ausnahme derjenigen, in der Sie keine Duplikate haben wollen. Quelle

1494voto

Quassnoi Punkte 396418
SELECT   Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM     Orders
JOIN     LineItems
ON       LineItems.LineItemGUID =
         (
         SELECT  TOP 1 LineItemGUID 
         FROM    LineItems
         WHERE   OrderID = Orders.OrderID
         )

In SQL Server 2005 und höher können Sie einfach die INNER JOIN avec CROSS APPLY :

SELECT  Orders.OrderNumber, LineItems2.Quantity, LineItems2.Description
FROM    Orders
CROSS APPLY
        (
        SELECT  TOP 1 LineItems.Quantity, LineItems.Description
        FROM    LineItems
        WHERE   LineItems.OrderID = Orders.OrderID
        ) LineItems2

Bitte beachten Sie, dass TOP 1 ohne ORDER BY ist nicht deterministisch: Mit dieser Abfrage erhalten Sie eine Position pro Auftrag, aber es ist nicht definiert, welche das sein wird.

Mehrere Aufrufe der Abfrage können unterschiedliche Positionen für denselben Auftrag ergeben, auch wenn sich der zugrunde liegende Wert nicht geändert hat.

Wenn Sie eine deterministische Reihenfolge wünschen, sollten Sie eine ORDER BY Klausel an die innerste Abfrage an.

Beispiel sqlfiddle

3 Stimmen

Ausgezeichnet, das funktioniert; Verschieben von TOP 1 aus der abgeleiteten Tabellenklausel in die Verknüpfungsklausel.

133 Stimmen

Und das "OUTER JOIN"-Äquivalent wäre "OUTER APPLY".

10 Stimmen

Wie sieht es bei LEFT OUTER JOIN aus?

149voto

Justin Fisher Punkte 1693

Ich weiß, dass diese Frage schon vor einiger Zeit beantwortet wurde, aber bei großen Datensätzen können verschachtelte Abfragen kostspielig sein. Hier ist eine andere Lösung, bei der die verschachtelte Abfrage nur einmal ausgeführt wird, anstatt für jede zurückgegebene Zeile.

SELECT 
  Orders.OrderNumber,
  LineItems.Quantity, 
  LineItems.Description
FROM 
  Orders
  INNER JOIN (
    SELECT
      Orders.OrderNumber,
      Max(LineItem.LineItemID) AS LineItemID
    FROM
      Orders INNER JOIN LineItems
      ON Orders.OrderNumber = LineItems.OrderNumber
    GROUP BY Orders.OrderNumber
  ) AS Items ON Orders.OrderNumber = Items.OrderNumber
  INNER JOIN LineItems 
  ON Items.LineItemID = LineItems.LineItemID

3 Stimmen

Dies ist auch viel schneller, wenn Ihr Die Spalte "LineItemId" ist nicht richtig indiziert. Verglichen mit der akzeptierten Antwort.

5 Stimmen

Aber wie würden Sie dies tun, wenn Max nicht verwendbar ist, da Sie nach einer anderen Spalte als der, die Sie zurückgeben möchten, bestellen müssen?

2 Stimmen

Sie können die abgeleitete Tabelle so anordnen, wie Sie wollen, und TOP 1 in SQL Server oder LIMIT 1 in MySQL verwenden.

62voto

BornToCode Punkte 8577

@Quassnoi Antwort ist gut, in einigen Fällen (vor allem, wenn die äußere Tabelle groß ist), könnte eine effizientere Abfrage mit gefensterten Funktionen, wie diese sein:

SELECT  Orders.OrderNumber, LineItems2.Quantity, LineItems2.Description
FROM    Orders
LEFT JOIN 
        (
        SELECT  LineItems.Quantity, LineItems.Description, OrderId, ROW_NUMBER()
                OVER (PARTITION BY OrderId ORDER BY (SELECT NULL)) AS RowNum
        FROM    LineItems

        ) LineItems2 ON LineItems2.OrderId = Orders.OrderID And RowNum = 1

Manchmal muss man einfach Notwendigkeit der Prüfung welche Abfrage eine bessere Leistung bringt.

4 Stimmen

Dies ist die einzige Antwort, die ich gefunden habe, die eine echte "linke" Verknüpfung durchführt, d. h. sie fügt nicht mehr Zeilen hinzu, als in der "linken" Tabelle vorhanden sind. Sie müssen lediglich eine Unterabfrage einfügen und "where RowNum is not null" hinzufügen.

2 Stimmen

Ich stimme zu, dass dies die beste Lösung ist. Diese Lösung erfordert auch keine eindeutige ID in der Tabelle, mit der Sie verknüpft werden, und ist viel schneller als die am häufigsten gewählte Antwort. Mit einer ORDER BY-Klausel in der Unterabfrage können Sie außerdem Kriterien dafür festlegen, welche Zeile Sie zurückgeben möchten, anstatt einfach eine zufällige Zeile zu nehmen.

0 Stimmen

Dies ist eine gute Lösung. Bitte beachten Sie: bei der Verwendung für Ihre eigene Situation, sehr vorsichtig sein, wie Sie PARTION BY (in der Regel wollen Sie wahrscheinlich einige ID-Spalte gibt) und ORDER BY (die durch die meisten alles getan werden könnte, je nachdem, welche Zeile Sie behalten wollen, z. B. DateCreated desc wäre eine Wahl für einige Tabellen, aber es würde auf eine Menge Dinge abhängen)

30voto

Tomalak Punkte 320467

Das könnten Sie tun:

SELECT 
  Orders.OrderNumber, 
  LineItems.Quantity, 
  LineItems.Description
FROM 
  Orders INNER JOIN LineItems 
  ON Orders.OrderID = LineItems.OrderID
WHERE
  LineItems.LineItemID = (
    SELECT MIN(LineItemID) 
    FROM   LineItems
    WHERE  OrderID = Orders.OrderID
  )

Dies erfordert einen Index (oder Primärschlüssel) auf LineItems.LineItemID und einen Index auf LineItems.OrderID oder es wird langsam sein.

2 Stimmen

Dies funktioniert nicht, wenn ein Auftrag keine LineItems hat. Der Unterausdruck wertet dann LineItems.LineItemID = null und entfernt die linken Entitätsaufträge vollständig aus dem Ergebnis.

8 Stimmen

Das ist auch der Effekt der inneren Verbindung, also... ja.

1 Stimmen

Lösung, die für LEFT OUTER JOIN angepasst werden kann: stackoverflow.com/a/20576200/510583

27voto

P. Olesen Punkte 221

Ab SQL Server 2012 denke ich, dass dies der richtige Weg ist:

SELECT DISTINCT
    o.OrderNumber ,
    FIRST_VALUE(li.Quantity) OVER ( PARTITION BY o.OrderNumber ORDER BY li.Description ) AS Quantity ,
    FIRST_VALUE(li.Description) OVER ( PARTITION BY o.OrderNumber ORDER BY li.Description ) AS Description
FROM    Orders AS o
    INNER JOIN LineItems AS li ON o.OrderID = li.OrderID

4 Stimmen

Die beste Antwort, wenn Sie mich fragen.

0 Stimmen

Ich denke, dies ist die beste Antwort

1 Stimmen

Führt der "INNER JOIN LineItems" nicht dazu, dass mehrere Zeilen zurückgegeben werden, wenn eine Bestellung mehr als eine Position hat?

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X