web-dev-qa-db-ja.com

サブクエリの最初の行を結合するにはどうすればよいですか?

請求書テーブルと、キーで関連付けられた関連データの子テーブルがあります。特に、各請求書について、子テーブルの最初の関連行のみに関心があります。すべての請求書キーに関連する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応答に賛成票を投じますが、それらを受け入れることはできません。私は怖いです。

23
BenAlabaster

CarriersPRIMARY KEYidを呼び出しました:

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
        )
31
Quassnoi

これは私があなたのそれ(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にインデックスがある限り、このクエリのパフォーマンスは許容範囲です。

セバスチャン

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

そのような場合、私はここであなたの例に適用し、以下で説明するデバイスをよく使用します。

_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)
_
1
Andriy M

これは私にとってはうまくいきます:

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
1
abatishchev

または、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
0
Mario Vázquez
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'
0
Chris Shain