Equivalente a LIMIT e OFFSET para SQL Server?

172

No PostgreSQL, existem as palavras Limit- Offsetchave e que permitirão uma paginação muito fácil dos conjuntos de resultados.

Qual é a sintaxe equivalente para o SQL Server?

Earlz
fonte
Para o sql server 2012, esse recurso é implementado de maneira fácil. Veja minha resposta
Somnath Muluk
Obrigado por fazer esta pergunta, nós estamos sendo forçados a transição do MySQL para MSSQL :(
tempcke
Você pode usar o deslocamento e buscar a próxima instrução no SQL server com a ordem por cláusula. Tente youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Respostas:

139

O equivalente a LIMITé SET ROWCOUNT, mas se você quiser paginação genérica, é melhor escrever uma consulta como esta:

;WITH Results_CTE AS
(
    SELECT
        Col1, Col2, ...,
        ROW_NUMBER() OVER (ORDER BY SortCol1, SortCol2, ...) AS RowNum
    FROM Table
    WHERE <whatever>
)
SELECT *
FROM Results_CTE
WHERE RowNum >= @Offset
AND RowNum < @Offset + @Limit

A vantagem aqui é a parametrização do deslocamento e do limite, caso você decida alterar suas opções de paginação (ou permita que o usuário faça isso).

Nota: o @Offsetparâmetro deve usar a indexação baseada em um para isso, em vez da indexação normal baseada em zero.

Aaronaught
fonte
22
Velho agora. Sql Server 2012 e posterior suportam OFFSET / FETCH
Joel Coehoorn
31
@JoelCoehoorn Não é velho. Acabei de ser atribuído ao projeto usando SLQ Server 2008 ter usado apenas mysql no passado ...
Cthulhu
Isso é muito bom, mas precisa ser ajusted um poucoWHERE RowNum >= (@Offset + 1)
Eric Herlitz
5
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified. MSSQL2008 R2.
Paul
2
@Aaronaught Se o meu Tabletiver 200k registros, ele buscará todos primeiro e aplicará o limite? Esta consulta é eficiente?
Jigar
231

Agora, esse recurso foi facilitado no SQL Server 2012. Isso está funcionando a partir do SQL Server 2012.

Limite com deslocamento para selecionar 11 a 20 linhas no SQL Server:

SELECT email FROM emailTable 
WHERE user_id=3
ORDER BY Id
OFFSET 10 ROWS
FETCH NEXT 10 ROWS ONLY;
  • OFFSET: número de linhas ignoradas
  • NEXT: número necessário de próximas linhas

Referência: https://docs.microsoft.com/en-us/sql/t-sql/queries/select-order-by-clause-transact-sql?view=sql-server-2017

Somnath Muluk
fonte
4
Existe um equivalente SQL_CALC_FOUND_ROWSao usar isso?
Petah
1
@Petah @@ Rowcount dará a você o que eu acho #
Rob Sedgwick
GOTCHA: Você não pode usar isso de dentro de um CTE. Ele deve ser usado na consulta principal. Queria limitar a quantidade de linhas retornadas (paginação) e, em seguida, executar um cálculo caro às 10 linhas retornadas, em vez de determinar as linhas, executar o cálculo caro e pular / pegar o que eu precisava. A resposta da @ Aaronaught funcionará para aqueles que precisam restringir linhas dentro de um CTE.
Derreck Dean
@Somnath Muluk Esse deslocamento e busca estão demorando muito tempo para obter um exemplo de volume maior de dados com deslocamento de 1000000. Como posso lidar com isso.
Saroj Shrestha 28/10
1
@ SarojShrestha: Este não é um problema de deslocamento e busca. Você deve revisitar a arquitetura da sua tabela agora. Considere Particionar tabelas, sua linha de dados e seus diferentes tipos de colunas e tamanho total da tabela, considere arquivar algumas linhas se não for necessário regularmente, verifique as especificações do servidor.
Somnath Muluk
23
select top {LIMIT HERE} * from (
      select *, ROW_NUMBER() over (order by {ORDER FIELD}) as r_n_n 
      from {YOUR TABLES} where {OTHER OPTIONAL FILTERS}
) xx where r_n_n >={OFFSET HERE}

Nota: Esta solução funcionará apenas no SQL Server 2005 ou superior, pois foi quando ROW_NUMBER()foi implementada.

jorgeu
fonte
Estou usando essa consulta há um tempo e agora funciona muito bem, obrigado por isso. Eu só estou querendo saber o que o 'xx' representa?
precisa saber é o seguinte
a subconsulta requer um nome. como eu não estou usando-o apenas colocar xx lá
jorgeu
2
O xx é apenas um alias da tabela. Pode ser um pouco mais claro se você disserAS xx
Concrete Gannet
alguém sabe como fazer junção esquerda nesta consulta?
Drenyl 22/02/19
12

Você pode usar ROW_NUMBER em uma expressão de tabela comum para conseguir isso.

;WITH My_CTE AS
(
     SELECT
          col1,
          col2,
          ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
)
SELECT
     col1,
     col2
FROM
     My_CTE
WHERE
     row_number BETWEEN @start_row AND @end_row
Tom H
fonte
4

Para mim, o uso de OFFSET e FETCH juntos foi lento, então usei uma combinação de TOP e OFFSET como este (que foi mais rápido):

SELECT TOP 20 * FROM (SELECT columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Nota: Se você usar TOP e OFFSET juntos na mesma consulta, como:

SELECT TOP 20 columname1, columname2 FROM tablename
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS

Então você recebe um erro; portanto, para usar TOP e OFFSET juntos, é necessário separá-lo com uma subconsulta.

E se você precisar usar SELECT DISTINCT, a consulta será como:

SELECT TOP 20 FROM (SELECT DISTINCT columname1, columname2
    WHERE <conditions...> ORDER BY columname1 OFFSET 100 ROWS) aliasname

Nota: O uso de SELECT ROW_NUMBER com DISTINCT não funcionou para mim.

sebasdev
fonte
1
Recebo "Um TOP não pode ser usado na mesma consulta ou subconsulta que um OFFSET".
MichaelRushton #
Você está certo @MichaelRushton, não pode ser usado na mesma consulta ou na mesma subconsulta, então você deve usar uma subconsulta para separá-la. Portanto, se você possui o SQL SELECT TOP 20 id FROM table1 where id > 10 order by date OFFSET 20 rows, deve transformá-lo SELECT TOP 20 * FROM (SELECT id FROM table1 where id > 10 order by date OFFSET 20 ROWS) t1. Vou editar minha resposta. Obrigado e com licença meu inglês.
91317 sebasdev
2

Outra amostra:

declare @limit int 
declare @offset int 
set @offset = 2;
set @limit = 20;
declare @count int
declare @idxini int 
declare @idxfim int 
select @idxfim = @offset * @limit
select @idxini = @idxfim - (@limit-1);
WITH paging AS
    (
        SELECT 
             ROW_NUMBER() OVER (order by object_id) AS rowid, *
        FROM 
            sys.objects 
    )
select *
    from 
        (select COUNT(1) as rowqtd from paging) qtd, 
            paging 
    where 
        rowid between @idxini and @idxfim
    order by 
        rowid;
sillyim
fonte
15
Eu removi seu discurso de ódio anti-microsoft. Não discuta guerras sagradas aqui; basta responder e fazer perguntas de maneira não subjetiva.
Earlz
2

Existe aqui alguém dizendo sobre esse recurso no sql 2011, é triste que eles escolham uma palavra-chave um pouco diferente "OFFSET / FETCH", mas não é padrão então ok.

keepkeywordspleeeease
fonte
2

Adicionando uma pequena variação na solução da Aaronaught, normalmente parametrizo o número da página (@PageNum) e o tamanho da página (@PageSize). Dessa forma, cada evento de clique na página envia o número da página solicitada junto com um tamanho de página configurável:

begin
    with My_CTE  as
    (
         SELECT col1,
              ROW_NUMBER() OVER(ORDER BY col1) AS row_number
     FROM
          My_Table
     WHERE
          <<<whatever>>>
    )
    select * from My_CTE
            WHERE RowNum BETWEEN (@PageNum - 1) * (@PageSize + 1) 
                              AND @PageNum * @PageSize

end
Tom
fonte
2

O mais próximo que eu poderia fazer é

select * FROM( SELECT *, ROW_NUMBER() over (ORDER BY ID ) as ct from [db].[dbo].[table] ) sub where ct > fromNumber  and ct <= toNumber

Que eu acho parecido com select * from [db].[dbo].[table] LIMIT 0, 10

user2991730
fonte
1
select top (@TakeCount) * --FETCH NEXT
from(
    Select  ROW_NUMBER() OVER (order by StartDate) AS rowid,*
    From YourTable
)A
where Rowid>@SkipCount --OFFSET
Barny
fonte
1
@nombre_row :nombre ligne par page  
@page:numero de la page

//--------------code sql---------------

declare  @page int,@nombre_row int;
    set @page='2';
    set @nombre_row=5;
    SELECT  *
FROM    ( SELECT    ROW_NUMBER() OVER ( ORDER BY etudiant_ID ) AS RowNum, *
      FROM      etudiant

    ) AS RowConstrainedResult
WHERE   RowNum >= ((@page-1)*@nombre_row)+1
    AND RowNum < ((@page)*@nombre_row)+1
ORDER BY RowNum
noureddine ahmer el kaab
fonte
1

Como ninguém forneceu esse código ainda:

SELECT TOP @limit f1, f2, f3...
FROM t1
WHERE c1 = v1, c2 > v2...
AND
    t1.id NOT IN
        (SELECT TOP @offset id
         FROM t1
         WHERE c1 = v1, c2 > v2...
         ORDER BY o1, o2...)
ORDER BY o1, o2...

Pontos importantes:

  • ORDER BY deve ser idêntico
  • @limit pode ser substituído pelo número de resultados a serem recuperados,
  • @offset é o número de resultados a serem ignorados
  • Compare o desempenho com as soluções anteriores, pois elas podem ser mais eficientes
  • essa solução duplica wheree order bycláusula e fornece resultados incorretos se estiverem fora de sincronia
  • por outro lado, order byexiste explicitamente se é isso que é necessário
przemo_li
fonte
1
-- @RowsPerPage  can be a fixed number and @PageNumber number can be passed 
DECLARE @RowsPerPage INT = 10, @PageNumber INT = 2

SELECT *

FROM MemberEmployeeData

ORDER BY EmployeeNumber

OFFSET @PageNumber*@RowsPerPage ROWS

FETCH NEXT 10 ROWS ONLY
shakeel
fonte
1

Especificamente para o SQL-SERVER, você pode conseguir isso de várias maneiras diferentes. Para um exemplo real, usamos a tabela Customer aqui.

Exemplo 1: Com "SET ROWCOUNT"

SET ROWCOUNT 10
SELECT CustomerID, CompanyName from Customers
ORDER BY CompanyName

Para retornar todas as linhas, defina ROWCOUNT como 0

SET ROWCOUNT 0  
SELECT CustomerID, CompanyName from Customers
    ORDER BY CompanyName

Exemplo 2: Com "ROW_NUMBER and OVER"

With Cust AS
( SELECT CustomerID, CompanyName,
ROW_NUMBER() OVER (order by CompanyName) as RowNumber 
FROM Customers )
select *
from Cust
Where RowNumber Between 0 and 10

Exemplo 3: Com "OFFSET and FETCH", mas com este "ORDER BY" é obrigatório

SELECT CustomerID, CompanyName FROM Customers
ORDER BY CompanyName
OFFSET 0 ROWS
FETCH NEXT 10 ROWS ONLY

Espero que isso ajude você.

Humayoun_Kabir
fonte
-1

No servidor SQL, você usaria TOP junto com ROW_NUMBER ()

SQLMenace
fonte
-1

Desde então, eu testo mais vezes esse script mais útil em 1 milhão de registros a cada página. 100 registros com paginação funcionam mais rápido meu PC executa esse script 0 segundos, enquanto a comparação com o mysql tem limite próprio e desloca cerca de 4,5 segundos para obter o resultado.

Alguém pode deixar de entender Row_Number () sempre classificar por campo específico. Caso seja necessário definir apenas a linha em sequência, deve-se usar:

ROW_NUMBER () OVER (ORDER BY (SELECIONAR NULL))

SELECT TOP {LIMIT} * FROM (
      SELECT TOP {LIMIT} + {OFFSET} ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS ROW_NO,*
      FROM  {TABLE_NAME}
) XX WHERE ROW_NO > {OFFSET}

Explicar:

  • {LIMIT}: número de registros para cada página
  • {OFFSET}: número de registros ignorados
Vanda Ros
fonte
2
Embora esse código possa resolver a questão, incluir uma explicação de como e por que isso resolve o problema realmente ajudaria a melhorar a qualidade da sua postagem e provavelmente resultaria em mais votos positivos. Lembre-se de que você está respondendo à pergunta dos leitores no futuro, não apenas à pessoa que está perguntando agora. Por favor edite sua resposta para adicionar explicações e dar uma indicação do que limitações e premissas se aplicam.
Brian