¿Cómo seleccionar la mayoría de las filas inferiores?

100

Puedo hacer SELECT TOP (200) ... pero ¿por qué no BOTTOM (200)?

Bueno, para no entrar en filosofía, lo que quiero decir es, ¿cómo puedo hacer el equivalente de TOP (200) pero al revés (desde abajo, como esperarías que hiciera BOTTOM ...)?

CloudMeta
fuente

Respuestas:

89
SELECT
    columns
FROM
(
     SELECT TOP 200
          columns
     FROM
          My_Table
     ORDER BY
          a_column DESC
) SQ
ORDER BY
     a_column ASC
Tom H
fuente
2
¿Por qué está utilizando una tabla derivada?
RichardOD
14
Si desea devolver filas en orden A-> Z, pero seleccione los 200 primeros en Z-> A orden, esta es una forma de hacerlo. Las otras respuestas, que sugieren simplemente cambiar el ORDER BY no devolverán los mismos resultados descritos en la pregunta, ya que estarán fuera de orden (a menos que el orden no importe, lo que el OP no dijo).
Tom H
3
@Tom H. Tuve que pensar unos segundos en lo que querías decir (he estado despierto 14 horas). Al principio no podía ver la diferencia entre el tuyo y el orden por respuestas, pero ahora puedo. Entonces +1.
RichardOD
1
Esta y otras respuestas funcionan bien cuando se trabaja en tablas más pequeñas. No creo que valga la pena ordenar la tabla completa por una columna cuando solo está interesado en las últimas filas.
Steadyfish
1
buena respuesta, la mejor en mi humilde opinión ... el problema real es que no puedo hacer esto desde un script ASP, así que creo que necesito reordenar el objRecordset manualmente o con la función que proporciona ASP ....
Andrea_86
99

Es innecesario. Puede usar ORDER BYy simplemente cambiar el orden a DESCpara obtener el mismo efecto.

Justin Ethier
fuente
3
Google dijo lo mismo y ahora 9 de ustedes están de acuerdo, lo suficientemente bien para mí, gracias: D
CloudMeta
8
El uso de DESC devolverá las últimas N filas, pero las filas devueltas también estarán en orden inverso a las primeras N filas.
RickNZ
11
¿Qué pasa si no hay un índice en su mesa para ORDENAR?
Protector uno
8
@Justin: Imagina tener solo una columna, que contiene valores varchar. ORDER BY ordenaría alfabéticamente, que (probablemente) no es lo que queremos.
Protector uno
3
Tom H. es la respuesta correcta; de lo contrario, sus filas estarán en orden inverso.
Pierre-Olivier Goulet
39

Lo siento, pero no creo que vea ninguna respuesta correcta en mi opinión.

La TOPfunción x muestra los registros en orden indefinido. De esa definición se deduce que una BOTTOMfunción no se puede definir.

Independiente de cualquier índice u orden de clasificación. Cuando haces una ORDER BY y DESC, obtienes primero las filas con el valor de y más alto. Si se trata de una identificación generada automáticamente, debería mostrar los registros que se agregaron por última vez a la tabla, como se sugiere en las otras respuestas. Sin embargo:

  • Esto solo funciona si hay una columna de identificación generada automáticamente
  • Tiene un impacto significativo en el rendimiento si lo comparas con la TOPfunción

La respuesta correcta debería ser que no hay, y no puede haber, un equivalente a TOPpara obtener las filas inferiores.

Hamburguesa Martijn
fuente
3
Es cierto que no parece haber equivalente, solo soluciones.
Empuje el
4
TOP no tiene nada que ver con el orden en el que se agregaron los elementos a una tabla, solo significa "Dar los primeros registros X que coincidan con mi consulta"
Lucas
4
Sí, le brinda los primeros registros agregados a la tabla que coinciden con su consulta.
Hamburguesa Martijn
4
Estoy de acuerdo con Luke. Las tablas de la base de datos no tienen orden por definición. Nunca se debe confiar en el orden proporcionado por el RDBMS cuando la instrucción select no tiene cláusula ORDER BY. leer aquí en wiki Sin embargo, el sistema de base de datos no garantiza ningún orden de las filas a menos que se especifique una cláusula ORDER BY en la instrucción SELECT que consulta la tabla.
Zohar Peled
2
Estoy de acuerdo con esta respuesta, pero en la práctica, la respuesta de Tom resuelve el problema para todos los usos prácticos.
Antonio
18

Lógicamente

BOTTOM (x) is all the records except TOP (n - x), where n is the count; x <= n

Por ejemplo, seleccione los 1000 inferiores de Empleado:

En T-SQL,

DECLARE 
@bottom int,
@count int

SET @bottom = 1000 
SET @count = (select COUNT(*) from Employee)

select * from Employee emp where emp.EmployeeID not in 
(
SELECT TOP (@count-@bottom) Employee.EmployeeID FROM Employee
)
Shadi Namrouti
fuente
1
Hola shadi2014, sin el uso de "ORDER BY", el resultado será de alguna manera aleatorio.
bummi
5
bummi, tienes razón, pero eso es lo que hace que esta respuesta sea correcta. Select TOP en sí mismo es "aleatorio" en teoría, y esta es la implementación correcta para Select BOTTOM. En una tabla de 5000 registros, los últimos 1000 son todo excepto los primeros 4000.
tzachs
9

Parecería que a cualquiera de las respuestas que implementan una cláusula ORDER BY en la solución le falta el punto o no entiende realmente lo que TOP le devuelve.

TOP devuelve un conjunto de resultados de consulta desordenado que limita el conjunto de registros a los primeros N registros devueltos. (Desde una perspectiva de Oracle, es similar a agregar un donde ROWNUM <(N + 1).

Cualquier solución que use un orden, puede devolver filas que también son devueltas por la cláusula TOP (ya que ese conjunto de datos estaba desordenado en primer lugar), dependiendo de qué criterio se usó en el pedido por

La utilidad de TOP es que una vez que el conjunto de datos alcanza un cierto tamaño N, deja de buscar filas. Puede tener una idea de cómo se ven los datos sin tener que buscarlos todos.

Para implementar BOTTOM con precisión, necesitaría buscar todo el conjunto de datos desordenado y luego restringir el conjunto de datos a los N registros finales. Eso no será particularmente efectivo si se trata de mesas enormes. Tampoco necesariamente le dará lo que cree que está pidiendo. El final del conjunto de datos puede no ser necesariamente "las últimas filas insertadas" (y probablemente no lo será para la mayoría de las aplicaciones intensivas de DML).

De manera similar, las soluciones que implementan un ORDER BY son, desafortunadamente, potencialmente desastrosas cuando se trata de grandes conjuntos de datos. Si tengo, digamos, 10 mil millones de registros y quiero los últimos 10, es una tontería pedir 10 mil millones de registros y seleccionar los últimos 10.

El problema aquí es que BOTTOM no tiene el significado que pensamos cuando lo comparamos con TOP.

Cuando los registros se insertan, eliminan, insertan, eliminan una y otra y otra vez, aparecerán algunos espacios en el almacenamiento y, más tarde, se colocarán filas, si es posible. Pero lo que vemos a menudo, cuando seleccionamos TOP, parecen ser datos ordenados, porque es posible que se hayan insertado al principio de la existencia de la tabla. Si la tabla no experimenta muchas eliminaciones, puede aparecer que está ordenada. (por ejemplo, las fechas de creación pueden ser tan antiguas como la propia creación de la tabla). Pero la realidad es que, si se trata de una tabla con muchas eliminaciones, es posible que las N filas TOP no se parezcan a eso en absoluto.

Entonces, la conclusión aquí (juego de palabras) es que alguien que está pidiendo los registros de BOTTOM N no sabe realmente lo que está pidiendo. O, al menos, lo que están pidiendo y lo que realmente significa BOTTOM no son lo mismo.

Entonces, la solución puede satisfacer la necesidad comercial real del solicitante ... pero no cumple con los criterios para ser la PARTE INFERIOR.

usuario9323238
fuente
1
Excelente explicación. Voto a favor por arrojar más luz sobre el tema.
rohrl77
Mi caso de uso es este. Ejecuté una insertdeclaración grande para poner filas en una tabla grande sin indexar. (Primero estoy llenando la tabla antes de comenzar a indexarla). Perdí mi sesión de cliente debido a un reinicio o lo que sea, y ahora quiero ver si mis filas recién agregadas están allí. Si la fila 'inferior' de la tabla es una de mis últimas, sé que la operación se completó. Si la fila 'inferior' es otra cosa, bueno, no hay garantías y tengo que escanear toda la tabla para asegurarme ... pero lo más probable es que pueda ahorrar algo de tiempo comprobando rápidamente la 'inferior' al igual que usted puede ' parte superior'.
Ed Avis
Buena explicación, pero aún implica la existencia de un fondo, que solo requiere que los datos se lean / recuperen al revés. En el caso (ciertamente borde) de una inserción abortada en una nueva tabla, sería útil verificar el último registro insertado (la parte inferior) sin recuperarlo todo. ¿Existe una razón técnica por la que los datos de la tabla no se pueden recuperar en orden inverso?
James
3

La respuesta actualmente aceptada por "Justin Ethier" no es una respuesta correcta como lo señaló "Protector uno".

Por lo que puedo ver, hasta ahora, ninguna otra respuesta o comentario proporciona el equivalente de BOTTOM (x) que pidió el autor de la pregunta.

Primero, consideremos un escenario en el que se necesitaría esta funcionalidad:

SELECT * FROM Split('apple,orange,banana,apple,lime',',')

Esto devuelve una tabla de una columna y cinco registros:

  • manzana
  • naranja
  • plátano
  • manzana
  • Lima

Como puede ver: no tenemos una columna de ID; no podemos ordenar por la columna devuelta; y no podemos seleccionar los dos registros inferiores usando SQL estándar como podemos hacer para los dos registros superiores.

Aquí está mi intento de proporcionar una solución:

SELECT * INTO #mytemptable FROM Split('apple,orange,banana,apple,lime',',')
ALTER TABLE #mytemptable ADD tempID INT IDENTITY
SELECT TOP 2 * FROM #mytemptable ORDER BY tempID DESC
DROP TABLE #mytemptable

Y aquí hay una solución más completa:

SELECT * INTO #mytemptable FROM Split('apple,orange,banana,apple,lime',',')
ALTER TABLE #mytemptable ADD tempID INT IDENTITY
DELETE FROM #mytemptable WHERE tempID <= ((SELECT COUNT(*) FROM #mytemptable) - 2)
ALTER TABLE #mytemptable DROP COLUMN tempID
SELECT * FROM #mytemptable
DROP TABLE #mytemptable

De ninguna manera estoy afirmando que esta sea una buena idea para usar en todas las circunstancias, pero proporciona los resultados deseados.

tomosius
fuente
1

Todo lo que necesita hacer es invertir su ORDER BY. Agréguelo o elimínelo DESC.

Justin Swartsel
fuente
1

El problema de ordenar al revés es que a menudo no hace un buen uso de los índices. Tampoco es muy ampliable si alguna vez necesita seleccionar una cantidad de filas que no están al principio o al final. Una forma alternativa es la siguiente.

DECLARE @NumberOfRows int;
SET @NumberOfRows = (SELECT COUNT(*) FROM TheTable);

SELECT col1, col2,...
FROM (
    SELECT col1, col2,..., ROW_NUMBER() OVER (ORDER BY col1) AS intRow
    FROM TheTable
) AS T
WHERE intRow > @NumberOfRows - 20;
Pablo
fuente
2
1) Si cambiar la dirección de su cláusula ORDER BY "no hace un buen uso de los índices", ¡obtenga un RDBMS decente! Al RDBMS nunca debería importarle si navega por el índice hacia adelante o hacia atrás. 2) Le preocupa el uso de índices, pero su solución adjunta una secuencia a cada fila de la tabla ... Esa es una forma de garantizar que NO se utilizará un índice apropiado.
Desilusionado
1

La respuesta de "Tom H" anterior es correcta y me funciona para obtener las 5 filas inferiores.

SELECT [KeyCol1], [KeyCol2], [Col3]
FROM
(SELECT TOP 5 [KeyCol1],
       [KeyCol2],
       [Col3]
  FROM [dbo].[table_name]
  ORDER BY [KeyCol1],[KeyCol2] DESC) SOME_ALAIS
  ORDER BY [KeyCol1],[KeyCol2] ASC

Gracias.

usuario3598017
fuente
0

prueba esto.

declare @floor int --this is the offset from the bottom, the number of results to exclude
declare @resultLimit int --the number of results actually retrieved for use
declare @total int --just adds them up, the total number of results fetched initially

--following is for gathering top 60 results total, then getting rid of top 50. We only keep the last 10
set @floor = 50 
set @resultLimit = 10
set @total = @floor + @resultLimit

declare @tmp0 table(
    --table body
)

declare @tmp1 table(
    --table body
)

--this line will drop the wanted results from whatever table we're selecting from
insert into @tmp0
select Top @total --what to select (the where, from, etc)

--using floor, insert the part we don't want into the second tmp table
insert into @tmp1
select top @floor * from @tmp0

--using select except, exclude top x results from the query
select * from @tmp0
except 
select * from @tmp1
HumbleWebDev
fuente
¿Qué hace su código para el OP? Agregue un poco más de explicación sobre cómo intenta resolver el problema
techspider
Hice una edición. Espero que lo explique un poco mejor, agregando más comentarios. La idea principal es seleccionar la parte superior x de una tabla, luego seleccionar la parte superior x - número deseado, luego usar una declaración excepto para excluir los resultados no necesarios.
HumbleWebDev
0

Se me ocurrió una solución para esto que no requiere que sepas el número de filas devueltas.

Por ejemplo, si desea obtener todas las ubicaciones registradas en una tabla, excepto la última 1 (o 2, o 5, o 34)

SELECT * 
FROM
    (SELECT ROW_NUMBER() OVER (ORDER BY CreatedDate) AS Row, * 
    FROM Locations
    WHERE UserId = 12345) AS SubQuery
WHERE Row > 1 -- or 2, or 5, or 34
rojo
fuente
0

La consulta de una subconsulta simple ordenada de forma descendente, seguida de una ordenación ascendente en la misma columna, funciona.

SELECT * FROM 
    (SELECT TOP 200 * FROM [table] t2 ORDER BY t2.[column] DESC) t1
    ORDER BY t1.[column]
Sheppe
fuente
0
SELECT TOP 10*from TABLE1 ORDER BY ID DESC

Donde ID es la clave principal de TABLE1.

Er. Binod Mehta
fuente
0

Primero, cree un índice en una subconsulta de acuerdo con el orden original de la tabla usando:

ROW_NUMBER () OVER (ORDER BY (SELECT NULL) ) AS RowIndex

Luego, ordena la tabla descendiendo por la RowIndexcolumna que has creado en la consulta principal:

ORDER BY RowIndex DESC

Y finalmente úselo TOPcon la cantidad deseada de filas:

    SELECT TOP 1 * --(or 2, or 5, or 34)
    FROM   (SELECT ROW_NUMBER() OVER (ORDER BY  (SELECT NULL) ) AS RowIndex, * 
            FROM MyTable) AS SubQuery
    ORDER BY RowIndex DESC
Thiago Marques
fuente