it-swarm.com.de

Wie trete ich der ersten Zeile einer Unterabfrage bei?

Ich habe eine Tabelle mit Rechnungen und eine untergeordnete Tabelle mit zugehörigen Daten, die sich auf Schlüssel beziehen. Insbesondere interessiert mich für jede Rechnung nur die erste zugehörige Zeile aus der untergeordneten Tabelle. Angesichts der Tatsache, dass ich für jeden Rechnungsschlüssel eine zugehörige Zeile haben möchte - wie kann ich das erreichen?

Select i.[Invoice Number],
       c.[Carrier Name]
From Invoice i
    Left Join Carriers c on i.[InvoiceKey] = c.[InvoiceKey]
Where -- what?

Ich vermute semantisch, wonach ich suche, was dem Konzept von Top 1 c.CarrierName Group by InvoiceKey ähnelt (oder was wäre das Konzept davon, wenn das in T-SQL möglich wäre).

Ich habe darüber nachgedacht, einen Link-Join für eine Unterabfrage durchzuführen, aber das scheint nicht sehr effizient zu sein. Hat jemand irgendwelche T-SQL-Tricks, um dies effizient zu erreichen?

Bearbeiten : Tut mir leid, ich habe vergessen zu erwähnen, dass dies SQL Server 2000 ist. Daher kann ich die aktuellen SQL Server 2005/2008-Antworten, die funktionieren werden, nicht akzeptieren, obwohl ich sie nicht akzeptiere ängstlich.

23
BenAlabaster

Vorausgesetzt, dass Carriers einen PRIMARY KEY namens id hat:

SELECT  i.[Invoice Number],
        c.[Carrier Name]
FROM    Invoice i
JOIN    Carriers c
ON      c.id = 
        (
        SELECT  TOP 1 ID
        FROM    Carriers ci
        WHERE   ci.InvoiceKey = i.InvoiceKey
        ORDER BY
                id -- or whatever
        )
30
Quassnoi

So würde ich es mit einer etwas anderen Syntax machen (MySQL-Stil), aber ich denke, Sie könnten es auch auf Ihre Lösung anwenden:

SELECT i.invoiceNumber, c.carrierName
FROM Invoice as i
LEFT JOIN Carriers as c ON (c.id = (SELECT id FROM Carriers WHERE invoiceKey = i.invoiceKey ORDER BY id LIMIT 1))

Dadurch werden alle Datensätze von Invoice abgerufen und mit einem (oder null) Datensatz von Spediteuren verbunden, insbesondere mit dem Datensatz, der den gleichen Rechnungsschlüssel und nur den ersten hat.

Solange Sie einen Index für Carriers.invoiceKey haben, sollte die Leistung dieser Abfrage akzeptabel sein.

Sebastian

2
;with cteRowNumber as (
    select c.InvoiceKey, c.[Carrier Name], ROW_NUMBER() over (partition by c.InvoiceKey order by c.[Carrier Name]) as RowNum
        from Carriers c
)
select i.[Invoice Number],
       rn.[Carrier Name]
    from Invoice i
        left join cteRowNumber rn
            on i.InvoiceKey = rn.InvoiceKey
                and rn.RowNum = 1
1
Joe Stefanelli

In solchen Fällen verwende ich oft ein Gerät, das ich hier auf Ihr Beispiel anwende und im Folgenden beschreibe:

SELECT
  i.[Invoice Number],
  c.[Carrier Name]
FROM Invoice i
  INNER JOIN Carriers c ON i.InvoiceKey = c.InvoiceKey
  INNER JOIN (
    SELECT MIN(ID) AS ID
    FROM Carriers
    GROUP BY InvoiceKey
  ) c_top ON c.ID = c_top.ID

Ich denke, das ist ungefähr das, was Quassnoi veröffentlicht hat, nur ich versuche zu vermeiden, SELECT TOPs so zu verwenden.

Invoice ist basierend auf ihrem Verknüpfungsausdruck (Carriers) mit InvoiceKey verknüpft. Jetzt kann Carriers mehrere Zeilen für dieselbe InvoiceKey haben, daher müssen wir die Ausgabe begrenzen. Und das geschieht mit einer abgeleiteten Tabelle.

Die abgeleitete Tabelle gruppiert Zeilen aus Carrier basierend auf demselben Ausdruck, der für die Verknüpfung der beiden Tabellen verwendet wird (InvoiceKey).

Und noch eine andere Möglichkeit: Anstatt die abgeleitete Tabelle zu verbinden, können Sie IN (subquery) mit demselben Effekt verwenden. Das heißt, die vollständige Abfrage würde dann so aussehen:

SELECT
  i.[Invoice Number],
  c.[Carrier Name]
FROM Invoice i
  INNER JOIN Carriers c ON i.InvoiceKey = c.InvoiceKey
    AND c.ID IN (SELECT MIN(ID) FROM Carriers GROUP BY InvoiceKey)
1
Andriy M

Das funktioniert für mich:

select ir.[Invoice Number], c.[Carrier Name]
from 
    (select ROW_NUMBER() over (order by i.[Invoice Number] asc) AS RowNumber, i.[Invoice Number], i.InvoiceKey
    from Invoice i) AS ir
left join Carriers c
on ir.InvoiceKey = c.InvoiceKey
where RowNumber = 1
union all
select ir.[Invoice Number], NULL as [Carrier Name]
from 
    (select ROW_NUMBER() over (order by i.[Invoice Number] asc) AS RowNumber, i.[Invoice Number]
    from Invoice i) AS ir
where RowNumber > 1

oder

select TOP 1 i.[Invoice Number], c.[Carrier Name]
from Invoice i
left join Carriers c
on i.InvoiceKey = c.InvoiceKey
union all
select ir.[Invoice Number], NULL as [Carrier Name]
from 
    (select ROW_NUMBER() over (order by i.[Invoice Number] asc) AS RowNumber, i.[Invoice Number]
    from Invoice i) AS ir
where RowNumber > 1
1
abatishchev
group by carriername having max(invoicenumber)

um den ersten Spediteur für jede Rechnung zu erhalten:

group by invoicenumber having max(carriername)
-- substitute the column you want to order by for carrier name to change which is 'first'
0
Chris Shain