Cómo unirse a la primera fila
Frecuentes
Visto 629,600 veces
818
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.
12 Respuestas
1298
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.
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
126
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
33
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
28
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
17
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
15
, 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
11
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
4
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
4
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
2
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
2
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
1
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 sql sql-server tsql sql-server-2000 or haz tu propia pregunta.
No puedes usar
group by
? - Dariush JafariPienso (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 - Joshua Nelson