Como selecionar a maioria das linhas inferiores?

100

Posso fazer SELECT TOP (200) ... mas por que não BOTTOM (200)?

Bem, para não entrar na filosofia, o que quero dizer é: como posso fazer o equivalente a TOP (200), mas ao contrário (a partir de baixo, como você esperaria que BOTTOM fizesse ...)?

CloudMeta
fonte

Respostas:

89
SELECT
    columns
FROM
(
     SELECT TOP 200
          columns
     FROM
          My_Table
     ORDER BY
          a_column DESC
) SQ
ORDER BY
     a_column ASC
Tom H
fonte
2
Por que você está usando uma tabela derivada?
RichardOD
14
Se você deseja retornar linhas na ordem A-> Z, mas selecione as 200 primeiras na ordem Z-> A, esta é uma maneira de fazer isso. As demais respostas, sugerindo apenas alterar o ORDER BY, não retornarão os mesmos resultados descritos na pergunta, pois estarão fora de ordem (a menos que a ordem não importe, o que o OP não disse).
Tom H
3
@Tom H. Tive que pensar por alguns segundos no que você queria dizer (estou acordado há 14 horas). No começo eu não conseguia ver a diferença entre a sua e a ordem por respostas, mas agora posso. Então, +1.
RichardOD
1
Esta e outras respostas funcionam bem quando você está trabalhando em tabelas menores. Não acho que vale a pena ordenar a tabela inteira por coluna quando você está interessado apenas nas poucas linhas de baixo.
stablefish
1
boa resposta, a melhor imho ... o verdadeiro problema é que não posso fazer isso a partir de um script ASP, então acho que preciso reordenar o objRecordset manualmente ou com a função que o ASP fornece ...
Andrea_86
99

Isso é desnecessário. Você pode usar um ORDER BYe apenas alterar o tipo para DESCpara obter o mesmo efeito.

Justin Ethier
fonte
3
google disse a mesma coisa e agora 9 de vocês concordam, bom o suficiente para mim, obrigado: D
CloudMeta
8
O uso de DESC retornará as últimas N linhas, mas as linhas retornadas também estarão na ordem inversa das primeiras N linhas.
RickNZ
11
E se não houver nenhum índice em sua tabela para ORDER BY?
Protetor um
8
@ Justin: Imagine ter apenas uma coluna, contendo valores varchar. ORDER BY seria classificado em ordem alfabética, o que (provavelmente) não é o que queremos.
Protetor um
3
Tom H. é a resposta correta, caso contrário, suas linhas estarão na ordem inversa.
Pierre-Olivier Goulet
39

Desculpe, mas acho que não vejo nenhuma resposta correta em minha opinião.

A TOPfunção x mostra os registros em ordem indefinida. Dessa definição segue que uma BOTTOMfunção não pode ser definida.

Independente de qualquer índice ou ordem de classificação. Quando você faz um, ORDER BY y DESCobtém primeiro as linhas com o valor y mais alto. Se for um ID gerado automaticamente, deve mostrar os últimos registros adicionados à tabela, conforme sugerido nas demais respostas. Contudo:

  • Isso só funciona se houver uma coluna de id gerada automaticamente
  • Tem um impacto significativo no desempenho se você comparar com a TOPfunção

A resposta correta deve ser que não há, e não pode haver, um equivalente TOPpara obter as linhas de baixo.

Martijn Burger
fonte
3
É verdade que parece não haver equivalente, apenas soluções alternativas.
cutucar
4
TOP não tem nada a ver com a ordem em que os itens foram adicionados a uma tabela, significa apenas "Forneça os primeiros X registros que correspondam à minha consulta"
Lucas
4
Sim, ele fornece os primeiros registros adicionados à tabela que correspondem à sua consulta.
Martijn Burger
4
Eu concordo com Luke. As tabelas de banco de dados não têm pedidos por definição. Nunca se deve confiar na ordem fornecida pelo RDBMS quando a instrução select não possui uma cláusula ORDER BY. leia aqui no wiki No entanto, o sistema de banco de dados não garante nenhuma ordem das linhas, a menos que uma cláusula ORDER BY seja especificada na instrução SELECT que consulta a tabela.
Zohar Peled
2
Eu concordo com essa resposta, mas na prática a resposta de Tom resolve o problema para todos os usos práticos.
Antonio,
18

Logicamente,

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

Por exemplo, selecione 1000 inferiores do funcionário:

Em 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
fonte
1
Olá shadi2014, sem o uso de "ORDER BY" seu resultado será de alguma forma aleatório.
bummi
5
bummi, você está certo, mas é isso que torna essa resposta correta. O próprio Select TOP é "aleatório" em teoria, e esta é a implementação correta do Select BOTTOM. Em uma tabela de 5.000 registros, os 1.000 inferiores são tudo, exceto os 4.000 superiores.
tzachs
9

Parece que qualquer uma das respostas que implementam uma cláusula ORDER BY na solução está perdendo o ponto ou não entende o que TOP retorna para você.

TOP retorna um conjunto de resultados de consulta não ordenado que limita o conjunto de registros aos primeiros N registros retornados. (Da perspectiva do Oracle, é semelhante a adicionar um where ROWNUM <(N + 1).

Qualquer solução que usa um pedido, pode retornar linhas que também são retornadas pela cláusula TOP (desde que esse conjunto de dados foi desordenado em primeiro lugar), dependendo de quais critérios foram usados ​​na ordem por

A utilidade do TOP é que uma vez que o conjunto de dados atinge um certo tamanho N, ele para de buscar linhas. Você pode ter uma ideia da aparência dos dados sem precisar buscá-los todos.

Para implementar o BOTTOM com precisão, seria necessário buscar todo o conjunto de dados não ordenado e, em seguida, restringir o conjunto de dados aos N registros finais. Isso não será particularmente eficaz se você estiver lidando com mesas enormes. Nem vai necessariamente dar o que você pensa que está pedindo. O final do conjunto de dados pode não ser necessariamente "as últimas linhas inseridas" (e provavelmente não será para a maioria dos aplicativos DML intensivos).

Da mesma forma, as soluções que implementam um ORDER BY são, infelizmente, potencialmente desastrosas ao lidar com grandes conjuntos de dados. Se eu tenho, digamos, 10 bilhões de registros e quero os últimos 10, é uma grande tolice solicitar 10 bilhões de registros e selecionar os últimos 10.

O problema aqui é que BOTTOM não tem o significado que pensamos ao compará-lo com TOP.

Quando os registros são inseridos, excluídos, inseridos, excluídos repetidamente, algumas lacunas aparecerão no armazenamento e, posteriormente, as linhas serão encaixadas, se possível. Mas o que vemos frequentemente, quando selecionamos TOP, parece ser dados classificados, porque podem ter sido inseridos no início da existência da tabela. Se a tabela não apresentar muitas exclusões, pode aparecer ordenada. (por exemplo, as datas de criação podem ser tão antigas quanto a própria criação da tabela). Mas a realidade é que, se esta for uma tabela com muitas exclusões, as linhas TOP N podem não se parecer com nada.

Então - o ponto principal aqui (trocadilho intencional) é que alguém que está pedindo os registros BOTTOM N não sabe realmente o que está pedindo. Ou, pelo menos, o que eles estão pedindo e o que BOTTOM realmente significa não são a mesma coisa.

Portanto - a solução pode atender à necessidade real de negócios do solicitante ... mas não atende aos critérios para ser o BOTTOM.

user9323238
fonte
1
Excelente explicação. Votos positivos por esclarecer melhor o assunto.
rohrl77
Meu caso de uso é este. Fiz uma grande insertdeclaração para colocar linhas em uma grande tabela não indexada. (Estou preenchendo a tabela antes de começar a indexá-la.) Perdi minha sessão de cliente devido a uma reinicialização ou algo assim, e agora quero ver se minhas linhas recém-adicionadas estão lá. Se a linha 'inferior' da tabela for uma das minhas mais recentes, sei que a operação foi concluída. Se a linha 'inferior' for outra coisa, bem, não há garantias e eu tenho que verificar toda a tabela para ter certeza ... mas provavelmente eu poderia economizar algum tempo verificando rapidamente a 'parte inferior' assim como você pode topo'.
Ed Avis
Boa explicação, mas ainda implica a existência de um fundo, que requer apenas que os dados sejam lidos / recuperados ao contrário. No caso (inadvertidamente borda) de uma inserção abortada em uma nova tabela, verificar o último registro inserido (a parte inferior) sem recuperar tudo seria útil. Existe uma razão técnica pela qual os dados da tabela não podem ser recuperados na ordem inversa?
James
3

A resposta atualmente aceita por "Justin Ethier" não é uma resposta correta como apontado por "Protetor um".

Até onde posso ver, até o momento, nenhuma outra resposta ou comentário fornece o equivalente a BOTTOM (x) que o autor da pergunta fez.

Primeiro, vamos considerar um cenário em que essa funcionalidade seja necessária:

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

Isso retorna uma tabela de uma coluna e cinco registros:

  • maçã
  • laranja
  • banana
  • maçã
  • Lima

Como você pode ver: não temos uma coluna de ID; não podemos ordenar pela coluna retornada; e não podemos selecionar os dois últimos registros usando o SQL padrão como podemos fazer para os dois primeiros registros.

Aqui está minha tentativa de fornecer uma solução:

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

E aqui está uma solução mais 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

Não estou de forma alguma afirmando que esta é uma boa idéia para usar em todas as circunstâncias, mas fornece os resultados desejados.

Tomosius
fonte
1

Tudo que você precisa fazer é reverter seu ORDER BY. Adicione ou remova DESC-o.

Justin Swartsel
fonte
1

O problema de ordenar de outra forma é que muitas vezes não faz bom uso dos índices. Também não é muito extensível se você precisar selecionar um número de linhas que não estão no início ou no final. Uma forma alternativa é a seguinte.

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;
Paulo
fonte
2
1) Se mudar a direção de sua cláusula ORDER BY "não faz bom uso de índices", então obtenha um RDBMS decente! O RDBMS nunca deve se preocupar se ele navega no índice para frente ou para trás. 2) Você está preocupado com o uso de índices, mas sua solução anexa uma sequência a cada linha da tabela ... Essa é uma maneira de garantir que um índice apropriado NÃO seja usado.
Desiludido em
1

A resposta "Tom H" acima está correta e funciona para mim ao obter as 5 linhas 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

Obrigado.

user3598017
fonte
0

tente isso.

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
fonte
o que torna seu código para o OP? Adicione um pouco mais de explicação sobre como você tenta resolver o problema
techspider
Eu fiz uma edição. Espero que isso explique um pouco melhor, adicionando mais comentários. A ideia principal é selecionar o topo x de uma tabela, então selecionar o topo x - num desejado e, em seguida, usar uma instrução except para excluir os resultados desnecessários.
HumbleWebDev
0

Eu encontrei uma solução para isso que não exige que você saiba o número de linhas retornadas.

Por exemplo, se você deseja obter todos os locais registrados em uma tabela, exceto o mais recente 1 (ou 2, ou 5 ou 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
Vermelho
fonte
0

Consultar uma subconsulta simples classificada em ordem decrescente, seguida pela classificação na mesma coluna em ordem crescente, resolve o problema.

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

Onde ID é a chave primária da TABELA1.

Er. Binod Mehta
fonte
0

Primeiro, crie um índice em uma subconsulta de acordo com a ordem original da tabela usando:

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

Em seguida, ordene a tabela em ordem decrescente pela RowIndexcoluna que você criou na consulta principal:

ORDER BY RowIndex DESC

E, finalmente, use TOPcom a quantidade desejada de linhas:

    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
fonte