J'ai un tableau des factures et un tableau enfant des données associées liées par clé. En particulier, pour chaque facture, je ne m'intéresse qu'à la première ligne associée de la table enfant. Étant donné que je veux une ligne associée pour chaque clé de facture, comment puis-je accomplir cela?
Select i.[Invoice Number],
c.[Carrier Name]
From Invoice i
Left Join Carriers c on i.[InvoiceKey] = c.[InvoiceKey]
Where -- what?
Je suppose sémantiquement parlant que je cherche quelque chose qui ressemble au concept de Top 1 c.CarrierName Group by InvoiceKey
(ou quel serait le concept de cela si cela était possible dans T-SQL).
J'ai pensé faire une jointure gauche sur une sous-requête, mais cela ne semble pas très efficace. Quelqu'un at-il des astuces T-SQL pour y parvenir efficacement?
Edit : Désolé les gars, j'ai oublié de mentionner qu'il s'agit de SQL Server 2000. Par conséquent, même si je vais donner des votes positifs pour les réponses actuelles de SQL Server 2005/2008 qui fonctionneront, je ne peux pas les accepter. peur.
Pourvu que Carriers
ait un PRIMARY KEY
appelé id
:
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
)
Voici comment je le ferais, en utilisant une syntaxe légèrement différente de la vôtre (style MySQL), mais je suppose que vous pouvez également l'appliquer à votre solution:
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))
Cela prendra tous les enregistrements de Invoice et les joindra à un (ou zéro) enregistrement des transporteurs, en particulier à l'enregistrement qui a la même factureKey et seulement le premier.
Tant que vous avez un index sur Carriers.invoiceKey, les performances de cette requête doivent être acceptables.
Sebastian
;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
Dans de tels cas, j’utilise souvent un dispositif que j’applique ici à votre exemple et que je décris ci-dessous:
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
Je pense que c'est à peu près ce que Quassnoi a posté, seulement j'essaie d'éviter d'utiliser SELECT TOPs comme ça.
Invoice
est associé à Carriers
en fonction de leur expression de liaison (InvoiceKey
dans ce cas). Maintenant, Carriers
peut avoir plusieurs lignes pour la même InvoiceKey
, nous devons donc limiter la sortie. Et cela se fait en utilisant une table dérivée.
La table dérivée regroupe les lignes de Carrier en fonction de la même expression utilisée pour lier les deux tables (InvoiceKey
).
Et il existe un autre moyen: au lieu de joindre la table dérivée, vous pouvez utiliser IN (subquery)
avec le même effet. C'est-à-dire que la requête complète ressemblerait alors à ceci:
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)
Cela fonctionne pour moi:
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
ou
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
group by carriername having max(invoicenumber)
pour obtenir le premier transporteur pour chaque facture:
group by invoicenumber having max(carriername)
-- substitute the column you want to order by for carrier name to change which is 'first'