Как мне присоединиться к первому ряду подзапроса? - PullRequest
23 голосов
/ 14 января 2011

У меня есть таблица счетов и дочерняя таблица связанных данных, связанных по ключу. В частности, для каждого счета меня интересует только первая связанная строка из дочерней таблицы. Учитывая, что я хочу одну связанную строку для каждого ключа счета-фактуры - как мне это сделать?

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, которые будут работать, я не могу Я боюсь их принять.

Ответы [ 6 ]

30 голосов
/ 14 января 2011

При условии, что 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
        )
2 голосов
/ 14 января 2011

Вот как я бы это сделал, используя немного другой синтаксис, чем ваш (стиль 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))

Это займет все записииз счета-фактуры и объедините его с одной (или нулевой) записью от Carriers, в частности, с записью, имеющей тот же invoiceKey и только первый.

Пока у вас есть индекс на Carriers.invoiceKey, производительностьэтот запрос должен быть приемлемым.

Себастьян

1 голос
/ 15 января 2011

В таких случаях я часто использую устройство, которое я здесь применяю к вашему примеру и описываю ниже:

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 TOPS подобным образом.

Invoice объединяется с Carriers на основе выражения их связывания (в данном случае InvoiceKey). Теперь Carriers может иметь несколько строк для одного и того же InvoiceKey, поэтому нам нужно ограничить вывод. И это делается с использованием производной таблицы.

Производная таблица группирует строки из Carrier на основе того же выражения, которое используется для связи двух таблиц (InvoiceKey).

И есть другой способ: вместо соединения с производной таблицей вы можете использовать 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 голос
/ 14 января 2011
;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 голос
/ 14 января 2011

Это работает для меня:

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
0 голосов
/ 14 января 2011
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'
...