請求書テーブルと、キーで関連付けられた関連データの子テーブルがあります。特に、各請求書について、子テーブルの最初の関連行のみに関心があります。すべての請求書キーに関連する1つの行が必要だとすると、どうすればこれを達成できますか?
Select i.[Invoice Number],
c.[Carrier Name]
From Invoice i
Left Join Carriers c on i.[InvoiceKey] = c.[InvoiceKey]
Where -- what?
意味論的に言えば、Top 1 c.CarrierName Group by InvoiceKey
の概念に似たものを探していると思います(またはT-SQLでそれが可能である場合、その概念は何でしょうか。)
サブクエリで左結合を行うことを考えましたが、それはあまり効率的ではないようです。これを効率的に達成するためのT-SQLトリックはありますか?
編集:申し訳ありませんが、これはSQL Server 2000であることを忘れていたため、機能する現在のSQL Server 2005/2008応答に賛成票を投じますが、それらを受け入れることはできません。私は怖いです。
Carriers
にPRIMARY KEY
が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
)
これは私があなたのそれ(MySQLスタイル)とは少し異なる構文を使用してそれを行う方法ですが、あなたのソリューションにもそれを適用できると思います:
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))
これにより、Invoiceからすべてのレコードが取得され、Carriersからの1つ(またはゼロ)のレコード、特に同じinvoiceKeyを持ち最初のレコードのみを持つレコードと結合されます。
Carriers.invoiceKeyにインデックスがある限り、このクエリのパフォーマンスは許容範囲です。
セバスチャン
;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
そのような場合、私はここであなたの例に適用し、以下で説明するデバイスをよく使用します。
_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
_
これは大まかにQuassnoiが投稿したものだと思いますが、私はそのようなSELECT TOPの使用を避けようとしています。
Invoice
は、リンク式(この場合はCarriers
)に基づいてInvoiceKey
と結合されます。これで、Carriers
は同じInvoiceKey
に対して複数の行を持つことができるため、出力を制限する必要があります。そして、それは派生テーブルを使用して行われます。
派生テーブルは、2つのテーブル(InvoiceKey
)をリンクするために使用される同じ式に基づいて、Carrierからの行をグループ化します。
また、別の方法もあります。派生テーブルを結合する代わりに、IN (subquery)
を使用して同じ効果を得ることができます。つまり、完全なクエリは次のようになります。
_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)
_
これは私にとってはうまくいきます:
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
または
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
または、OUTER APPLY
同じように。不明なフィールド名の山括弧の使用に注意してください:
Select i.[Invoice Number], c.[Carrier Name], x.<Carrier_field1>
From Invoice i
OUTER APPLY
(
SELECT TOP 1
FROM Carriers c
WHERE c.[InvoiceKey] = i.[InvoiceKey]
ORDER BY <order _clause>
) x
group by carriername having max(invoicenumber)
各請求書の最初の運送業者を取得するには:
group by invoicenumber having max(carriername)
-- substitute the column you want to order by for carrier name to change which is 'first'