Cómo unirse a la primera fila

Usaré un ejemplo concreto, pero hipotético.

Cada Reservar normalmente tiene solo uno Elemento en linea:

Pedidos:

OrderGUID   OrderNumber
=========   ============
{FFB2...}   STL-7442-1      
{3EC6...}   MPT-9931-8A

Artículos de línea:

LineItemGUID   Order ID Quantity   Description
============   ======== ========   =================================
{098FBE3...}   1        7          prefabulated amulite
{1609B09...}   2        32         spurving bearing

Pero ocasionalmente habrá un pedido con dos líneas de pedido:

LineItemID   Order ID    Quantity   Description
==========   ========    ========   =================================
{A58A1...}   6,784,329   5          pentametric fan
{0E9BC...}   6,784,329   5          differential girdlespring 

Normalmente al mostrar los pedidos al usuario:

SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
    INNER JOIN LineItems 
    ON Orders.OrderID = LineItems.OrderID

Quiero mostrar el artículo único del pedido. Pero con este pedido ocasional que contiene dos (o más) artículos, los pedidos Aparecer be duplicada:

OrderNumber   Quantity   Description
===========   ========   ====================
STL-7442-1    7          prefabulated amulite
MPT-9931-8A   32         spurving bearing
KSG-0619-81   5          panametric fan
KSG-0619-81   5          differential girdlespring

Lo que realmente quiero es tener SQL Server solo elige uno, como será lo suficientemente bueno:

OrderNumber   Quantity   Description
===========   ========   ====================
STL-7442-1    7          prefabulated amulite
MPT-9931-8A   32         differential girdlespring
KSG-0619-81   5          panametric fan

Si me vuelvo aventurero, es posible que le muestre al usuario una elipsis para indicar que hay más de uno:

OrderNumber   Quantity   Description
===========   ========   ====================
STL-7442-1    7          prefabulated amulite
MPT-9931-8A   32         differential girdlespring
KSG-0619-81   5          panametric fan, ...

Entonces la pregunta es cómo

  • eliminar filas "duplicadas"
  • solo únete a una de las filas, para evitar duplicaciones

Primer intento

Mi primer intento ingenuo fue unirme solo al "TOP 2016" artículos de línea:

SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM Orders
    INNER JOIN (
       SELECT TOP 1 LineItems.Quantity, LineItems.Description
       FROM LineItems
       WHERE LineItems.OrderID = Orders.OrderID) LineItems2
    ON 1=1

Pero eso da el error:

La columna o el prefijo 'Pedidos' no
coincidir con un nombre de tabla o un nombre de alias
utilizado en la consulta.

Presumiblemente porque la selección interna no ve la tabla externa.

preguntado el 11 de enero de 10 a las 13:01

No puedes usar group by? -

Pienso (y corrígeme si me equivoco) group by requeriría enumerar todas las demás columnas, excluyendo aquella en la que no desea duplicados. Source -

12 Respuestas

SELECT   Orders.OrderNumber, LineItems.Quantity, LineItems.Description
FROM     Orders
JOIN     LineItems
ON       LineItems.LineItemGUID =
         (
         SELECT  TOP 1 LineItemGUID 
         FROM    LineItems
         WHERE   OrderID = Orders.OrderID
         )

En SQL Server 2005 y superior, podría simplemente reemplazar INNER JOIN con CROSS APPLY:

SELECT  Orders.OrderNumber, LineItems2.Quantity, LineItems2.Description
FROM    Orders
CROSS APPLY
        (
        SELECT  TOP 1 LineItems.Quantity, LineItems.Description
        FROM    LineItems
        WHERE   LineItems.OrderID = Orders.OrderID
        ) LineItems2

este producto está hecho por encargo con un tiempo de producción de XNUMX a XNUMX semanas TOP 1 sin ORDER BY no es determinista: con esta consulta obtendrá un artículo de línea por pedido, pero no está definido cuál será.

Varias invocaciones de la consulta pueden proporcionarle diferentes líneas de pedido para el mismo pedido, incluso si el subyacente no cambió.

Si desea un orden determinista, debe agregar un ORDER BY cláusula a la consulta más interna.

Ejemplo sqlfiddle

respondido 20 nov., 20:17

Excelente, eso funciona; moviendo TOP 1 de la cláusula de la tabla derivada a la cláusula de unión. - Ian Boyd

y el equivalente "OUTER JOIN" sería "OUTER APPLY" - Alex de Jitbit

¿Qué tal para LEFT OUTER JOIN? - Alex Nolasco

¿Cómo se hace esto si la combinación se realiza a través de una clave compuesta / tiene varias columnas? - Brett Ryan

CROSS APPLY INNER JOIN y OUTER APPLY LEFT JOIN (lo mismo que LEFT OUTER JOIN). - hastrb

Sé que esta pregunta fue respondida hace un tiempo, pero cuando se trata de grandes conjuntos de datos, las consultas anidadas pueden ser costosas. Aquí hay una solución diferente en la que la consulta anidada solo se ejecutará una vez, en lugar de para cada fila devuelta.

SELECT 
  Orders.OrderNumber,
  LineItems.Quantity, 
  LineItems.Description
FROM 
  Orders
  INNER JOIN (
    SELECT
      Orders.OrderNumber,
      Max(LineItem.LineItemID) AS LineItemID
    FROM
      Orders INNER JOIN LineItems
      ON Orders.OrderNumber = LineItems.OrderNumber
    GROUP BY Orders.OrderNumber
  ) AS Items ON Orders.OrderNumber = Items.OrderNumber
  INNER JOIN LineItems 
  ON Items.LineItemID = LineItems.LineItemID

Respondido 06 Abr '12, 23:04

Esto también es mucho más rápido si tu La columna 'LineItemId' no está indexada correctamente. Comparado con la respuesta aceptada. - GER

Pero, ¿cómo haría esto si Max no se puede utilizar ya que necesita ordenar por una columna diferente a la que desea devolver? - NickG

puede ordenar la tabla derivada de la forma que desee y usar TOP 1 en SQL Server o LIMIT 1 en MySQL - sofocar

Descubrí que esto es mucho más rápido en conjuntos de datos más grandes: DotNetDublín

¿Podrías dar más detalles? En lo que respecta solo a la sintaxis, su respuesta está tan anidada como la de Quassnoi: exactamente una subconsulta. No puede simplemente implicar que uno se ejecutará "por cada fila devuelta" y el otro no solo porque la sintaxis parece entonces. Tienes que incluir un plan. - Jorge Menoutis

La respuesta de @Quassnoi es buena, en algunos casos (especialmente si la tabla exterior es grande), una consulta más eficiente podría ser con el uso de funciones en ventana, como esta:

SELECT  Orders.OrderNumber, LineItems2.Quantity, LineItems2.Description
FROM    Orders
LEFT JOIN 
        (
        SELECT  LineItems.Quantity, LineItems.Description, OrderId, ROW_NUMBER()
                OVER (PARTITION BY OrderId ORDER BY (SELECT NULL)) AS RowNum
        FROM    LineItems

        ) LineItems2 ON LineItems2.OrderId = Orders.OrderID And RowNum = 1

A veces solo Necesito probar qué consulta ofrece un mejor rendimiento.

Respondido 13 Abr '17, 13:04

Esta es la única respuesta que encontré que hace una combinación "Izquierda" real, lo que significa que no agrega más líneas que en la tabla "Izquierda". Solo necesita poner una subconsulta y agregar "donde RowNum no es nulo" - user890332

De acuerdo, esta es la mejor solución. Esta solución tampoco requiere que tenga una identificación única en la tabla a la que se está uniendo y es mucho más rápida que la respuesta más votada. También puede agregar criterios para la fila que prefiere devolver, en lugar de simplemente tomar una fila aleatoria, mediante el uso de una cláusula ORDER BY en la subconsulta. - geoff griswald

Esta es una buena solución. Tenga en cuenta: cuando lo use para su propia situación, tenga mucho cuidado con la forma de PARTICIONAR POR (por lo general, probablemente desee una columna de ID allí) y ORDENAR POR (lo que podría hacerse con casi cualquier cosa, dependiendo de la fila que desee mantener, por ejemplo DateCreated desc sería una opción para algunas tablas, pero dependería de muchas cosas) - joseperrito

Podrías hacerlo:

SELECT 
  Orders.OrderNumber, 
  LineItems.Quantity, 
  LineItems.Description
FROM 
  Orders INNER JOIN LineItems 
  ON Orders.OrderID = LineItems.OrderID
WHERE
  LineItems.LineItemID = (
    SELECT MIN(LineItemID) 
    FROM   LineItems
    WHERE  OrderID = Orders.OrderID
  )

Esto requiere un índice (o clave principal) en LineItems.LineItemID y un índice sobre LineItems.OrderID o será lento.

Respondido el 11 de enero de 10 a las 16:01

Esto no funciona si un pedido no tiene elementos de línea. La subexpresión luego evalúa LineItems.LineItemID = null y elimina las órdenes de entidad izquierda completamente del resultado. - León

Ese también es el efecto de la unión interna, así que ... sí. - Tomalak

Solución que se puede adaptar para LEFT OUTER JOIN: stackoverflow.com/a/20576200/510583 - León

@leo Sí, pero el OP usó una combinación interna él mismo, así que no entiendo tu objeción. - Tomalak

Desde SQL Server 2012 en adelante, creo que esto hará el truco:

SELECT DISTINCT
    o.OrderNumber ,
    FIRST_VALUE(li.Quantity) OVER ( PARTITION BY o.OrderNumber ORDER BY li.Description ) AS Quantity ,
    FIRST_VALUE(li.Description) OVER ( PARTITION BY o.OrderNumber ORDER BY li.Description ) AS Description
FROM    Orders AS o
    INNER JOIN LineItems AS li ON o.OrderID = li.OrderID

respondido 28 nov., 18:11

La mejor respuesta si me preguntas. - Thomas

Creo que esta es la mejor respuesta: hoang tran

, Otro enfoque que usa una expresión de tabla común:

with firstOnly as (
    select Orders.OrderNumber, LineItems.Quantity, LineItems.Description, ROW_NUMBER() over (partiton by Orders.OrderID order by Orders.OrderID) lp
    FROM Orders
        join LineItems on Orders.OrderID = LineItems.OrderID
) select *
  from firstOnly
  where lp = 1

o, al final, ¿tal vez le gustaría mostrar todas las filas unidas?

versión separada por comas aquí:

  select *
  from Orders o
    cross apply (
        select CAST((select l.Description + ','
        from LineItems l
        where l.OrderID = s.OrderID
        for xml path('')) as nvarchar(max)) l
    ) lines

contestado el 10 de mayo de 17 a las 11:05

Las subconsultas correlacionadas son subconsultas que dependen de la consulta externa. Es como un bucle for en SQL. La subconsulta se ejecutará una vez para cada fila de la consulta externa:

select * from users join widgets on widgets.id = (
    select id from widgets
    where widgets.user_id = users.id
    order by created_at desc
    limit 1
)

Respondido el 17 de Septiembre de 16 a las 11:09

EDITAR: no importa, Quassnoi tiene una mejor respuesta.

Para SQL2K, algo como esto:

SELECT 
  Orders.OrderNumber
, LineItems.Quantity
, LineItems.Description
FROM (  
  SELECT 
    Orders.OrderID
  , Orders.OrderNumber
  , FirstLineItemID = (
      SELECT TOP 1 LineItemID
      FROM LineItems
      WHERE LineItems.OrderID = Orders.OrderID
      ORDER BY LineItemID -- or whatever else
      )
  FROM Orders
  ) Orders
JOIN LineItems 
  ON LineItems.OrderID = Orders.OrderID 
 AND LineItems.LineItemID = Orders.FirstLineItemID

Respondido el 11 de enero de 10 a las 17:01

Mi forma favorita de ejecutar esta consulta es con una cláusula no existe. Creo que esta es la forma más eficiente de ejecutar este tipo de consulta:

select o.OrderNumber,
       li.Quantity,
       li.Description
from Orders as o
inner join LineItems as li
on li.OrderID = o.OrderID
where not exists (
    select 1
    from LineItems as li_later
    where li_later.OrderID = o.OrderID
    and li_later.LineItemGUID > li.LineItemGUID
    )

Pero no he probado este método con otros métodos sugeridos aquí.

contestado el 09 de mayo de 17 a las 19:05

Probé la cruz, funciona bien, pero tarda un poco más. Columnas de línea ajustadas para tener un grupo máximo y agregado que mantuvo la velocidad y eliminó el registro adicional.

Aquí está la consulta ajustada:

SELECT Orders.OrderNumber, max(LineItems.Quantity), max(LineItems.Description)
FROM Orders
    INNER JOIN LineItems 
    ON Orders.OrderID = LineItems.OrderID
Group by Orders.OrderNumber

Respondido 14 Feb 13, 22:02

Pero tener un máximo por separado en dos columnas significa que la cantidad podría no estar relacionada con la descripción. Si el pedido fuera 2 widgets y 10 gadgets, la consulta devolvería 10 widgets. - Brianorca

prueba este

SELECT
   Orders.OrderNumber,
   LineItems.Quantity, 
   LineItems.Description
FROM Orders
   INNER JOIN (
      SELECT
         Orders.OrderNumber,
         Max(LineItem.LineItemID) AS LineItemID
       FROM Orders 
          INNER JOIN LineItems
          ON Orders.OrderNumber = LineItems.OrderNumber
       GROUP BY Orders.OrderNumber
   ) AS Items ON Orders.OrderNumber = Items.OrderNumber
   INNER JOIN LineItems 
   ON Items.LineItemID = LineItems.LineItemID

Respondido 20 Feb 20, 02:02

Considere explicar qué hace su consulta para resolver el problema del OP: Simas Joneliunas

CROSS APPLY al rescate:

SELECT Orders.OrderNumber, topline.Quantity, topline.Description
FROM Orders
cross apply
(
    select top 1 Description,Quantity
    from LineItems 
    where Orders.OrderID = LineItems.OrderID
)topline

También puede agregar el order by de su elección.

Respondido el 12 de enero de 21 a las 08:01

Creo que esta respuesta es un duplicado de la respuesta aceptada. - Justin Fisher

join y apply no son lo mismo - Jorge Menoutis

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.