Como selecionar a enésima linha em uma tabela de banco de dados SQL?


399

Eu estou interessado em aprender algumas maneiras (idealmente) de banco de dados agnóstica de selecionar o n º linha de uma tabela de banco de dados. Também seria interessante ver como isso pode ser alcançado usando a funcionalidade nativa dos seguintes bancos de dados:

  • servidor SQL
  • MySQL
  • PostgreSQL
  • SQLite
  • Oráculo

Atualmente, estou fazendo algo como o seguinte no SQL Server 2005, mas estaria interessado em ver as abordagens mais agnósticas de outras pessoas:

WITH Ordered AS (
SELECT ROW_NUMBER() OVER (ORDER BY OrderID) AS RowNumber, OrderID, OrderDate
FROM Orders)
SELECT *
FROM Ordered
WHERE RowNumber = 1000000

Crédito para o SQL acima: Firoz Ansari's Weblog

Atualização: Veja a resposta de Troels Arvin sobre o padrão SQL. Troels, você tem algum link que possamos citar?


3
Sim. Aqui está um link para informações sobre o padrão ISO SQL: troels.arvin.dk/db/rdbms/links/#standards
Troels Arvin

13
Apenas para salientar que, pela definição de uma relação, as linhas de uma tabela não têm ordem, portanto, a Nésima linha de uma tabela não pode ser selecionada. O que pode ser selecionado é a enésima linha em um conjunto de linhas retornado por (o restante de) uma consulta, que é o que seu exemplo e todas as outras respostas realizam. Para muitos, isso pode ser apenas semântica, mas aponta para o problema subjacente da questão. Se você precisar retornar OrderNo N, introduza uma coluna OrderSequenceNo na tabela e gere-a a partir de um gerador de sequência independente ao criar um novo pedido.
Damir Sudarevic 26/10/11

2
O padrão SQL define a opção offset x fetch first y rows only. Atualmente suportado por (pelo menos) Postgres, Oracle12, DB2.
a_horse_with_no_name

Respostas:


349

Existem maneiras de fazer isso em partes opcionais do padrão, mas muitos bancos de dados suportam sua própria maneira de fazê-lo.

Um site realmente bom que fala sobre isso e outras coisas é http://troels.arvin.dk/db/rdbms/#select-limit .

Basicamente, o PostgreSQL e o MySQL suportam o não padrão:

SELECT...
LIMIT y OFFSET x 

Oracle, DB2 e MSSQL suportam as funções de janela padrão:

SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
    columns
  FROM tablename
) AS foo
WHERE rownumber <= n

(que acabei de copiar do site vinculado acima, pois nunca uso esses bancos de dados)

Atualização: A partir do PostgreSQL 8.4, as funções de janelas padrão são suportadas; portanto, espere que o segundo exemplo funcione também para o PostgreSQL.

Atualização: o SQLite adicionou suporte às funções da janela na versão 3.25.0 em 15/09/2018, para que ambos os formulários também funcionem no SQLite.


3
O MySQL também usa a sintaxe OFFSET e LIMIT. O Firebird usa as palavras-chave FIRST e SKIP, mas são colocadas logo após SELECT.
Doug

7
Não deveria ser WHERE rownumber = napenas a enésima linha?
Steve Bennett

MySQL suporta funções da janela desde a versão 8. MariaDB desde a versão 10.2
Paul Spiegel

102

O PostgreSQL suporta funções de janelas, conforme definidas pelo padrão SQL, mas são estranhas; portanto, a maioria das pessoas usa (o não padrão) LIMIT/OFFSET :

SELECT
    *
FROM
    mytable
ORDER BY
    somefield
LIMIT 1 OFFSET 20;

Este exemplo seleciona a 21a linha. OFFSET 20está dizendo ao Postgres para pular os 20 primeiros registros. Se você não especificar uma ORDER BYcláusula, não há garantia de qual registro você receberá, o que raramente é útil.


31

Não tenho certeza de nada, mas sei que o SQLite e o MySQL não têm nenhuma ordem de linha "padrão". Nesses dois dialetos, pelo menos, o seguinte fragmento pega a 15ª entrada da tabela, classificando pela data / hora em que foi adicionado:

SELECT * FROM the_table ORDER BY added DESC LIMIT 1,15

(é claro, você precisará adicionar um campo DATETIME e defini-lo como a data / hora em que a entrada foi adicionada ...)


Essa é a melhor maneira de limitar a consulta com o valor de deslocamento embutido. Mas não devemos usar 0,14 aqui? 1,15 deixará a primeira linha.
Gladiator

O que os 15 significam? Eu sei que o 1 diz para obter um registro. A vírgula não é usado no exemplo I check-out 1keydata.com/sql/sql-limit.html
committedandroider

11
Na verdade, a partir daqui php.about.com/od/mysqlcommands/g/Limit_sql.htm , se você quiser pegar a 15ª entrada, não faria o LIMIT 14, 1 (0 é o primeiro elemento, 1 de comprimento
commitandroider

SELECT * FROM the_table ORDER BY DESC LIMIT adicionado 15,1
JerryGoyal 27/04

25

O SQL 2005 e superior possuem esse recurso interno. Use a função ROW_NUMBER (). É excelente para páginas da web com navegação no estilo << Anterior e Próximo >>:

Sintaxe:

SELECT
    *
FROM
    (
        SELECT
            ROW_NUMBER () OVER (ORDER BY MyColumnToOrderBy) AS RowNum,
            *
        FROM
            Table_1
    ) sub
WHERE
    RowNum = 23

Prefiro esta solução, pois parece mais direta.
FoxArc 04/12/19

18

Eu suspeito que isso seja extremamente ineficiente, mas é uma abordagem bastante simples, que funcionou em um pequeno conjunto de dados em que eu tentei.

select top 1 field
from table
where field in (select top 5 field from table order by field asc)
order by field desc

Isso obteria o quinto item, altere o segundo número superior para obter um enésimo item diferente

Apenas servidor SQL (acho), mas deve funcionar em versões mais antigas que não suportam ROW_NUMBER ().


Vou usar isso como ROW_NUMBER () não funciona no SQL 2000 (sim, ainda temos um cliente no SQL 2000) Especificamente, vou substituir o '5' por uma variável iteradora de um loop e usar para copiar e modificar cada linha de uma tabela por vez. Talvez alguém veja este comentário e o considere útil
Inversus 17/05


14

Verifique no SQL Server:

Select top 10 * From emp 
EXCEPT
Select top 9 * From emp

Isso lhe dará a 10ª fila de emp emp!


Você já forneceu uma resposta a esta pergunta aqui Excluir essa resposta que você acha que não é right.if você acha que ambas as respostas certas são então postar os ambas as respostas em um só lugar
SpringLearner

11

Ao contrário do que algumas das respostas afirmam, o padrão SQL não é silencioso em relação a esse assunto.

Desde o SQL: 2003, você pode usar "funções da janela" para ignorar linhas e limitar conjuntos de resultados.

E no SQL: 2008, uma abordagem um pouco mais simples foi adicionada, usando
OFFSET skip ROWS FETCH FIRST n ROWS ONLY

Pessoalmente, não acho que a adição do SQL: 2008 fosse realmente necessária; portanto, se eu fosse ISO, o manteria fora de um padrão já bastante amplo.


É bom que há um porém standard, faz as pessoas como a vida me é mais fácil, e tão agradável de microsoft para fazer coisas de uma forma padrão :)
user230910

7

Quando costumávamos trabalhar no MSSQL 2000, fizemos o que chamamos de "triple-flip":

EDITADO

DECLARE @InnerPageSize int
DECLARE @OuterPageSize int
DECLARE @Count int

SELECT @Count = COUNT(<column>) FROM <TABLE>
SET @InnerPageSize = @PageNum * @PageSize
SET @OuterPageSize = @Count - ((@PageNum - 1) * @PageSize)

IF (@OuterPageSize < 0)
    SET @OuterPageSize = 0
ELSE IF (@OuterPageSize > @PageSize)
    SET @OuterPageSize = @PageSize

DECLARE @sql NVARCHAR(8000)

SET @sql = 'SELECT * FROM
(
    SELECT TOP ' + CAST(@OuterPageSize AS nvarchar(5)) + ' * FROM
    (
        SELECT TOP ' + CAST(@InnerPageSize AS nvarchar(5)) + ' * FROM <TABLE> ORDER BY <column> ASC
    ) AS t1 ORDER BY <column> DESC
) AS t2 ORDER BY <column> ASC'

PRINT @sql
EXECUTE sp_executesql @sql

Não era elegante, nem rápido, mas funcionou.


Digamos que você tenha 25 linhas e deseje a terceira página com 10 páginas, ou seja, as linhas 21 a 25. A consulta mais interna obtém as 30 principais linhas (linhas 1 a 25). A consulta do meio obtém as últimas 10 linhas (linhas 25-16). A consulta externa os reordena e retorna as linhas 16-25. Isso está claramente errado se você quisesse as linhas 21-25.
Bill Karwin

Agora não funciona se queremos uma página do meio. Digamos que temos 25 linhas e queremos a segunda página, ou seja, as linhas 11-20. A consulta interna obtém as 2 * 10 principais = 20 linhas ou as linhas 1-20. A consulta do meio obtém as últimas 15 linhas: 25 - ((2-1) * 10) = 15, o que gera as linhas 20-6. A última consulta reverte a ordem e retorna as linhas 6 a 20. Essa técnica não funciona, a menos que o número total de linhas seja um múltiplo do tamanho de página desejado.
Bill Karwin

Talvez a melhor conclusão seja que devemos atualizar as instâncias remanescentes do MS SQL Server 2000. :-) É quase 2012, e esse problema foi resolvido de maneiras melhores por muitos anos!
Bill Karwin

@ Bill Karwin: Observe os IF / ELSE IFblocos abaixo do OuterPageSizecálculo - nas páginas 1 e 2, eles OuterPageSizeretornarão o valor para 10. Na página 3 (linhas 21 a 25), o cálculo retornará corretamente 5 e, em todas as páginas 4 e superiores, o resultado negativo do cálculo será substituído por 0 (embora provavelmente seja mais rápido retornar uma linha de dados vazia imediatamente nesse ponto).
Adam V

Oh, eu vejo agora. Bem, mantenho minha opinião de que o uso do MS SQL Server 2000 hoje não vale a pena.
Bill Karwin

6

SERVIDOR SQL


Selecione o nono registro da parte superior

SELECT * FROM (
SELECT 
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID) AS ROW
FROM TABLE 
) AS TMP 
WHERE ROW = n

selecione o nono registro da parte inferior

SELECT * FROM (
SELECT 
ID, NAME, ROW_NUMBER() OVER(ORDER BY ID DESC) AS ROW
FROM TABLE 
) AS TMP 
WHERE ROW = n

5

Oráculo:

select * from (select foo from bar order by foo) where ROWNUM = x

11
where ROWNUM = xfuncionará apenas para x = 1 no Oracle DB. ou seja where ROWNUM = 2, não retornará nenhuma linha.
aff

5

No Oracle 12c, você pode usar a OFFSET..FETCH..ROWS opção comORDER BY

Por exemplo, para obter o terceiro registro de cima:

SELECT * 
FROM   sometable
ORDER BY column_name
OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY;

4

Aqui está uma solução rápida para sua confusão.

SELECT * FROM table ORDER BY `id` DESC LIMIT N, 1

Aqui você pode obter a última linha preenchendo N = 0, a segunda última preenchendo N = 1, a quarta última preenchendo N = 3 e assim por diante.

Essa é uma pergunta muito comum durante a entrevista e é muito simples.

Além disso, se você quiser Quantidade, ID ou alguma ordem de classificação numérica que você possa ir para a função CAST no MySQL.

SELECT DISTINCT (`amount`) FROM cart ORDER BY CAST( `amount` AS SIGNED ) DESC LIMIT 4 , 1

Aqui Ao preencher N = 4, você poderá obter o Quinto Último Registro de Maior Valor da tabela CART. Você pode ajustar o nome do seu campo e tabela e encontrar a solução.


3

ADICIONAR:

LIMIT n,1

Isso limitará os resultados a um resultado começando no resultado n.


3

Por exemplo, se você quiser selecionar a cada 10 linhas no MSSQL, poderá usar;

SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY ColumnName1 ASC) AS rownumber, ColumnName1, ColumnName2
  FROM TableName
) AS foo
WHERE rownumber % 10 = 0

Basta pegar o MOD e alterar o número 10 aqui, qualquer número que você quiser.


3

Para o SQL Server, uma maneira genérica de usar o número da linha é:

SET ROWCOUNT @row --@row = the row number you wish to work on.

Por exemplo:

set rowcount 20   --sets row to 20th row

select meat, cheese from dbo.sandwich --select columns from table at 20th row

set rowcount 0   --sets rowcount back to all rows

Isso retornará as informações da 20ª linha. Certifique-se de inserir o número de linhas 0 depois.


2

LIMITE n, 1 não funciona no MS SQL Server. Eu acho que é apenas o único banco de dados importante que não suporta essa sintaxe. Para ser justo, não faz parte do padrão SQL, embora seja tão amplamente suportado que deveria. Em tudo, exceto no SQL Server, o LIMIT funciona muito bem. Para o SQL server, não consegui encontrar uma solução elegante.


11
Exceto para Oracle, DB2, bem, praticamente todos os bancos de dados de nível corporativo do mundo inteiro. O PostgreSQL é o único banco de dados com capacidade para empresas que suporta a palavra-chave LIMIT, e isso ocorre porque, como sendo de código aberto, ele precisa ser acessível pela multidão ignorante do MySQL do ACID.
David

3
@AlexD Esta "resposta" foi postada nos velhos tempos do Stackoverflow antes dos comentários serem implementados. Eu teria postado isso como um comentário em outra resposta, mas, no momento, não existiam comentários.
Kibbee

2

Aqui está uma versão genérica de um sproc que escrevi recentemente para o Oracle que permite paginação / classificação dinâmica - HTH

-- p_LowerBound = first row # in the returned set; if second page of 10 rows,
--                this would be 11 (-1 for unbounded/not set)
-- p_UpperBound = last row # in the returned set; if second page of 10 rows,
--                this would be 20 (-1 for unbounded/not set)

OPEN o_Cursor FOR
SELECT * FROM (
SELECT
    Column1,
    Column2
    rownum AS rn
FROM
(
    SELECT
        tbl.Column1,
        tbl.column2
    FROM MyTable tbl
    WHERE
        tbl.Column1 = p_PKParam OR
        tbl.Column1 = -1
    ORDER BY
        DECODE(p_sortOrder, 'A', DECODE(p_sortColumn, 1, Column1, 'X'),'X'),
        DECODE(p_sortOrder, 'D', DECODE(p_sortColumn, 1, Column1, 'X'),'X') DESC,
        DECODE(p_sortOrder, 'A', DECODE(p_sortColumn, 2, Column2, sysdate),sysdate),
        DECODE(p_sortOrder, 'D', DECODE(p_sortColumn, 2, Column2, sysdate),sysdate) DESC
))
WHERE
    (rn >= p_lowerBound OR p_lowerBound = -1) AND
    (rn <= p_upperBound OR p_upperBound = -1);

2

Mas, sério, não é tudo isso apenas truques de salão para um bom design de banco de dados? Nas poucas vezes em que precisei de funcionalidades como essa, foi para uma consulta simples e simples para fazer um relatório rápido. Para qualquer trabalho real, usar truques como esses é um problema. Se a seleção de uma linha específica for necessária, basta ter uma coluna com um valor seqüencial e concluir com ela.


2

Para o SQL server, o seguinte retornará a primeira linha da tabela de fornecimento.

declare @rowNumber int = 1;
    select TOP(@rowNumber) * from [dbo].[someTable];
EXCEPT
    select TOP(@rowNumber - 1) * from [dbo].[someTable];

Você pode percorrer os valores com algo assim:

WHILE @constVar > 0
BEGIN
    declare @rowNumber int = @consVar;
       select TOP(@rowNumber) * from [dbo].[someTable];
    EXCEPT
       select TOP(@rowNumber - 1) * from [dbo].[someTable];  

       SET @constVar = @constVar - 1;    
END;

1

No Sybase SQL Anywhere:

SELECT TOP 1 START AT n * from table ORDER BY whatever

Não se esqueça da ordem ou não faz sentido.


1

T-SQL - Selecionando o nono número de registro de uma tabela

select * from
 (select row_number() over (order by Rand() desc) as Rno,* from TableName) T where T.Rno = RecordNumber

Where  RecordNumber --> Record Number to Select
       TableName --> To be Replaced with your Table Name

Por exemplo, para selecionar o 5º registro de uma tabela Employee, sua consulta deve ser

select * from
 (select row_number() over (order by Rand() desc) as Rno,* from Employee) T where T.Rno = 5

1
SELECT * FROM emp a
WHERE  n = (SELECT COUNT( _rowid)
              FROM emp b
             WHERE a. _rowid >= b. _rowid);

1
SELECT
    top 1 *
FROM
    table_name
WHERE
    column_name IN (
        SELECT
            top N column_name
        FROM
            TABLE
        ORDER BY
            column_name
    )
ORDER BY
    column_name DESC

Escrevi esta consulta para encontrar a enésima linha. Exemplo com esta consulta seria

SELECT
    top 1 *
FROM
    Employee
WHERE
    emp_id IN (
        SELECT
            top 7 emp_id
        FROM
            Employee
        ORDER BY
            emp_id
    )
ORDER BY
    emp_id DESC

0

inacreditável que você possa encontrar um mecanismo SQL executando este ...

WITH sentence AS
(SELECT 
    stuff,
    row = ROW_NUMBER() OVER (ORDER BY Id)
FROM 
    SentenceType
    )
SELECT
    sen.stuff
FROM sentence sen
WHERE sen.row = (ABS(CHECKSUM(NEWID())) % 100) + 1

0

Nada extravagante, sem funções especiais, caso você use Caché como eu ...

SELECT TOP 1 * FROM (
  SELECT TOP n * FROM <table>
  ORDER BY ID Desc
)
ORDER BY ID ASC

Dado que você tem uma coluna de ID ou uma coluna de carimbo de data e hora em que pode confiar.


0

É assim que eu faria no DB2 SQL, acredito que o RRN (número de registro relativo) é armazenado na tabela pelo O / S;

SELECT * FROM (                        
   SELECT RRN(FOO) AS RRN, FOO.*
   FROM FOO                         
   ORDER BY RRN(FOO)) BAR             
 WHERE BAR.RRN = recordnumber

0
select * from 
(select * from ordered order by order_id limit 100) x order by 
x.order_id desc limit 1;

Primeiro, selecione as 100 principais linhas ordenando em ordem crescente e, em seguida, selecione a última linha ordenando em ordem decrescente e limite a 1. No entanto, essa é uma declaração muito cara, pois acessa os dados duas vezes.


0

Parece-me que, para ser eficiente, é necessário 1) gerar um número aleatório entre 0 e um a menos que o número de registros do banco de dados e 2) poder selecionar a linha nessa posição. Infelizmente, bancos de dados diferentes têm geradores de números aleatórios diferentes e maneiras diferentes de selecionar uma linha em uma posição em um conjunto de resultados - geralmente você especifica quantas linhas ignorar e quantas linhas deseja, mas isso é feito de maneira diferente para bancos de dados diferentes. Aqui está algo que funciona para mim no SQLite:

select * 
from Table 
limit abs(random()) % (select count(*) from Words), 1;

Depende da capacidade de usar uma subconsulta na cláusula limit (que no SQLite é LIMIT <recs para pular>, <recs para pular>) A seleção do número de registros em uma tabela deve ser particularmente eficiente, sendo parte do banco de dados. metadados, mas isso depende da implementação do banco de dados. Além disso, não sei se a consulta realmente criará o conjunto de resultados antes de recuperar o Nésimo registro, mas espero que não seja necessário. Observe que não estou especificando uma cláusula "order by". Talvez seja melhor "ordenar por" algo como a chave primária, que terá um índice - obter o N-ésimo registro de um índice pode ser mais rápido se o banco de dados não puder obter o N-ésimo registro do próprio banco de dados sem criar o conjunto de resultados .


0

Resposta mais adequada que eu vi neste artigo para sql server

WITH myTableWithRows AS (
    SELECT (ROW_NUMBER() OVER (ORDER BY myTable.SomeField)) as row,*
    FROM myTable)
SELECT * FROM myTableWithRows WHERE row = 3
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.