top string_agg left inner first ejemplos sql sql-server tsql sql-server-2000

string_agg - SQL Server: Cómo unirse a la primera fila



select sql ejemplos (10)

Usaré un ejemplo concreto, pero hipotético.

Cada orden normalmente tiene solo una línea de pedido :

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 artículos de línea:

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 único artículo en el pedido. Pero con este pedido ocasional que contiene dos (o más) artículos, los pedidos aparecerían duplicados :

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 que SQL Server solo elija uno , ya que 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 aventuro, podría mostrarle 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, ...

Así que la pregunta es cómo

  • eliminar filas "duplicadas"
  • Solo unirse a una de las filas, para evitar la duplicación.

Primer intento

Mi primer intento ingenuo fue unirme solo a las líneas de " TOP 1 ":

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 coincide con un nombre de tabla o alias utilizado en la consulta.

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


, Otro enfoque utilizando la 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 te 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


EDIT: 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


La respuesta de @Quassnoi es buena, en algunos casos (especialmente si la tabla exterior es grande), una consulta más eficiente podría ser el uso de funciones de 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 necesitas probar qué consulta da mejor rendimiento.


Las consultas secundarias correlacionadas son consultas secundarias que dependen de la consulta externa. Es como un bucle for en SQL. La subconsulta se ejecutará una vez para cada fila en 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 )


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í.


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 en LineItems.OrderID o será lento.


Probado la cruz, funciona bien, pero tarda un poco más. Las columnas de línea ajustadas para tener un grupo máximo y agregado que mantuvieron la velocidad y eliminaron 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


Resuelvo un problema similar utilizando LEFT JOIN y GROUP BY Orders.OrderNumber. ¿Hay alguna razón para no hacerlo de esta manera?

SELECT Orders.OrderNumber, LineItems.Quantity, LineItems.Description FROM Orders LEFT JOIN LineItems ON Orders.OrderID = LineItems.OrderID GROUP BY Orders.OrderNumber

Responderé a tu pregunta de respuesta con una respuesta en tu propia pregunta:

Orders LineItems +-------------+ +---------+----------+---------------+ | OrderNumber | | OrderID | Quantity | Description | +-------------+ +---------+----------+---------------+ | 22586 | | 22586 | 17 | Trunion | +-------------+ | 22586 | 3 | Girdle Spring | +---------+----------+---------------+

Unir los dos en OrderNumber da:

OrderNumber Quantity Description ----------- -------- ------------- 22586 17 Trunion 22586 3 Girdle Spring 2 row(s) affected

Donde queríamos que volviera solo una fila:

OrderNumber Quantity Description ----------- -------- ------------- 22586 17 Trunion 1 row(s) affected

Es por eso que uso GROUP BY Orders.OrderNumber, que solo devuelve una fila por OrderNumber.


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


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, simplemente puede 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

Tenga en cuenta que TOP 1 sin ORDER BY no es determinista: esta consulta le proporcionará una línea de pedido por orden, pero no se define cuál será.

Las múltiples invocaciones de la consulta pueden darle diferentes artículos de línea para el mismo orden, incluso si el subyacente no cambió.

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