2011-01-14 17 views
19

Tengo una tabla de facturas y una tabla secundaria de datos relacionados relacionados por clave. En particular, para cada factura, me interesa solo la primera fila relacionada de la tabla secundaria. Dado que quiero una fila relacionada para cada clave de factura, ¿cómo puedo lograr esto?¿Cómo me uno a la primera fila de una subconsulta?

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

supongo que semánticamente hablando, lo que estoy buscando algo similar al concepto de Top 1 c.CarrierName Group by InvoiceKey (o lo que sería el concepto de que si eso fuera posible en T-SQL.)

I' He pensado en hacer una combinación de izquierda en una subconsulta, pero eso no parece muy eficiente. ¿Alguien tiene trucos de T-SQL para lograr esto de manera eficiente?

Editar: Lo siento chicos, olvidé mencionar que esto es SQL Server 2000, así que mientras voy a dar votaciones por las respuestas actuales de SQL Server 2005/2008 que funcionarán, no puedo aceptarlas I 'tengo miedo.

+0

¿Tiene segunda tabla tienen ningún atributo que dice qué fila es el primer segundo, etc. – Chandu

+0

@Cybernate No, aparte de la secuencia del índice – BenAlabaster

Respuesta

24

Siempre que Carriers tiene un PRIMARY KEY llamada 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 
     ) 
+0

¡Eso es realmente genial! – abatishchev

+0

El rendimiento de esto es probable que sea malo en comparación con un grupo por y que tenga una cláusula. Está ejecutando una subconsulta correlacionada con la tabla Transportistas para cada fila en Factura. –

+4

@Chris: ¿comparado con qué? No proporcionó un ejemplo de trabajo. – Quassnoi

1

Esto funciona para 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 

o

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

1 harían el trabajo si estuviese en SQL Server 2005+, pero olvidé mencionar que necesito esta ejecución en un cuadro de SQL Server 2000. – BenAlabaster

0
group by carriername having max(invoicenumber) 

para conseguir el primer portador para cada factura:

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

Esto no funcionará; esto solo me proporcionará el nombre del transportista con el número de factura más alto. Lo que necesito es el primer transportista para cada factura. – BenAlabaster

+0

No, esto debería darle el número de factura más alto para cada proveedor. Al invertir el grupo por/having, puede obtener el primer operador para cada factura. –

+0

Recuerde: las cláusulas se aplican DESPUÉS de agrupar por cláusulas :-) –

1
;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 
+0

No funciona. Se une a todos los registros, no solo primero – abatishchev

+0

@abatishchev: No lo veo. Si incluyo 'rn.RowNum = 1' como parte de mi condición de unión, eso debería unir solo al" primer "(como se define mediante el orden de la función de ventana). –

+0

+1 Técnicamente correcto, pero olvidé mencionar que estaba buscando SQL Server 2000, por lo que CTE no es una opción. – BenAlabaster

1

Esta es la forma en que lo haría, utilizando una sintaxis ligeramente diferente a la suya (estilo MySQL), pero supongo que se podría aplicar a su solución, así:

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

Esto se llevará todos los registros de facturas, y únase a uno (o cero) registro de Carriers, específicamente el registro que tiene la misma invoiceKey y solo la primera.

Siempre que tenga un índice en Carriers.invoiceKey, el rendimiento de esta consulta debería ser aceptable.

Sebastián

1

En tales casos que a menudo emplean un dispositivo que lo que aquí se aplican a su ejemplo y describo a continuación:

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 

Creo que esto es más o menos lo que Quassnoi ha publicado, solo trato de evitar usar SELECT TOP así.

Invoice se une con Carriers en función de su expresión de enlace (InvoiceKey en este caso). Ahora, Carriers puede tener varias filas para el mismo InvoiceKey, por lo que debemos limitar el resultado. Y eso se hace usando una tabla derivada.

La tabla derivada agrupa las filas de Carrier basadas en la misma expresión que se utiliza para vincular las dos tablas (InvoiceKey).

Y hay otra manera: en lugar de unirse a la tabla derivada, puede usar IN (subquery) con el mismo efecto. Es decir, la consulta completa sería el siguiente aspecto:

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) 
Cuestiones relacionadas