Deslocamento de linha no SQL Server

133

Existe alguma maneira no SQL Server para obter os resultados iniciando em um determinado deslocamento? Por exemplo, em outro tipo de banco de dados SQL, é possível fazer:

SELECT * FROM MyTable OFFSET 50 LIMIT 25

para obter resultados 51-75. Essa construção não parece existir no SQL Server.

Como posso fazer isso sem carregar todas as linhas que não me interessam? Obrigado!

Alex
fonte
Você pode usar o deslocamento e buscar a próxima instrução. youtu.be/EqHkAiiBwPc
Amresh Kumar Singh

Respostas:

152

Eu evitaria usar SELECT *. Especifique as colunas que você realmente deseja, mesmo que possam ser todas elas.

SQL Server 2005 ou superior

SELECT col1, col2 
FROM (
    SELECT col1, col2, ROW_NUMBER() OVER (ORDER BY ID) AS RowNum
    FROM MyTable
) AS MyDerivedTable
WHERE MyDerivedTable.RowNum BETWEEN @startRow AND @endRow

SQL Server 2000

Paginando com eficiência por meio de grandes conjuntos de resultados no SQL Server 2000

Um método mais eficiente para paginar através de grandes conjuntos de resultados

Brian Kim
fonte
6
Por que você sugere evitar SELECT, mesmo se estiver selecionando todas as colunas?
Adam Ness
12
Tenho certeza que ele usou "*" porque era mais simples para digitar e tem o ponto de vista melhor do que "col1, col2, ... Colón"
gillonba
9
Quanto ao motivo de não usá-lo, SELECT *significa que, se a estrutura da tabela mudar, sua consulta ainda será executada, mas fornecerá resultados diferentes. Se uma coluna for adicionada, isso pode ser útil (embora você ainda precise usá-la pelo nome em algum lugar); se uma coluna for excluída ou renomeada, é melhor que seu SQL quebre visivelmente do que o código mais abaixo se comportando de maneira estranha, porque uma variável não é inicializada.
IMSoP:
5
selecionar todos os dados da tabela e recortar? se tem 5000000000 linhas? selecione 5000000000 linhas e recorte para cada consulta? não é eficiente para CPU e memória do servidor.
e-info128
3
Por favor, note que 2012+ implementou muito melhor. Veja a resposta por + Martin Smith
meridius
100

Se você estiver processando todas as páginas em ordem, basta lembrar o último valor da chave visto na página anterior e usá-lo TOP (25) ... WHERE Key > @last_key ORDER BY Keypode ser o método com melhor desempenho, se existirem índices adequados para permitir a busca eficiente - ou um cursor de API, se não houver. .

Para selecionar uma página arbitrária, a melhor solução para o SQL Server 2005 - 2008 R2 é provavelmente ROW_NUMBEReBETWEEN

Para o SQL Server 2012+, você pode usar a cláusula ORDER BY aprimorada para essa necessidade.

SELECT  *
FROM     MyTable 
ORDER BY OrderingColumn ASC 
OFFSET  50 ROWS 
FETCH NEXT 25 ROWS ONLY 

Embora ainda seja necessário ver o desempenho dessa opção .

Martin Smith
fonte
2
Agora está disponível no SQL Server Compact 4.0 -> msdn.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
Bart Verkoeijen
13
Já era hora de eles adicionarem isso ao tSQL
JohnFx
3
Apenas para o Sql Server 2012 :(
e-info128
22

Esta é uma maneira (SQL2000)

SELECT * FROM
(
    SELECT TOP (@pageSize) * FROM
    (
        SELECT TOP (@pageNumber * @pageSize) *
        FROM tableName 
        ORDER BY columnName ASC
    ) AS t1 
    ORDER BY columnName DESC
) AS t2 
ORDER BY columnName ASC

e essa é outra maneira (SQL 2005)

;WITH results AS (
    SELECT 
        rowNo = ROW_NUMBER() OVER( ORDER BY columnName ASC )
        , *
    FROM tableName 
) 
SELECT * 
FROM results
WHERE rowNo between (@pageNumber-1)*@pageSize+1 and @pageNumber*@pageSize
leoinfo
fonte
Apenas para esclarecer o primeiro ... (@pageSize) é um espaço reservado aqui para o valor real. Você terá que fazer o 'TOP 25' especificamente; O SQL Server 2000 não suporta variáveis ​​em uma cláusula TOP. Isso torna uma dor envolvendo SQL dinâmico.
Cowan
5
Essa solução para o SQL2000 não funciona para a última página no conjunto de resultados, a menos que o número total de linhas seja múltiplo do tamanho da página.
Bill Karwin
10

Você pode usar a ROW_NUMBER()função para obter o que deseja:

SELECT *
FROM (SELECT ROW_NUMBER() OVER(ORDER BY id) RowNr, id FROM tbl) t
WHERE RowNr BETWEEN 10 AND 20
Matthias Meid
fonte
7

Existe OFFSET .. FETCHno SQL Server 2012, mas você precisará especificar uma ORDER BYcoluna.

Se você realmente não possui nenhuma coluna explícita que possa passar como uma ORDER BYcoluna (como outros sugeriram), use este truque:

SELECT * FROM MyTable 
ORDER BY @@VERSION 
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

... ou

SELECT * FROM MyTable 
ORDER BY (SELECT 0)
OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY

Estamos usando no jOOQ quando os usuários não especificam explicitamente um pedido. Isso produzirá pedidos aleatórios, sem custos adicionais.

Lukas Eder
fonte
6

Para tabelas com mais e grandes colunas de dados, prefiro:

SELECT 
  tablename.col1,
  tablename.col2,
  tablename.col3,
  ...
FROM
(
  (
    SELECT
      col1
    FROM 
    (
      SELECT col1, ROW_NUMBER() OVER (ORDER BY col1 ASC) AS RowNum
      FROM tablename
      WHERE ([CONDITION])
    )
    AS T1 WHERE T1.RowNum BETWEEN [OFFSET] AND [OFFSET + LIMIT]
  )
  AS T2 INNER JOIN tablename ON T2.col1=tablename.col1
);

-

[CONDITION] can contain any WHERE clause for searching.
[OFFSET] specifies the start,
[LIMIT] the maximum results.

Ele tem um desempenho muito melhor em tabelas com dados grandes, como BLOBs, porque a função ROW_NUMBER precisa procurar apenas uma coluna e somente as linhas correspondentes são retornadas com todas as colunas.

Arthur van Dijk
fonte
5

Veja minha seleção para paginador

SELECT TOP @limit * FROM (
   SELECT ROW_NUMBER() OVER (ORDER BY colunx ASC) offset, * FROM (

     -- YOU SELECT HERE
     SELECT * FROM mytable


   ) myquery
) paginator
WHERE offset > @offset

Isso resolve a paginação;)

PerfectLion
fonte
3
SELECT TOP 75 * FROM MyTable
EXCEPT 
SELECT TOP 50 * FROM MyTable
Jithin Shaji
fonte
O desempenho não parece ótimo, pois a consulta é desnecessariamente executada duas vezes. Especialmente quando o usuário acessa as páginas superiores, a consulta para descartar linhas, ou seja, a parte abaixo de EXCEPT vai demorar mais e mais.
vanval
2

Dependendo da sua versão, você não pode fazê-lo diretamente, mas você pode fazer algo hacky como

select top 25 *
from ( 
  select top 75 *
  from   table 
  order by field asc
) a 
order by field desc 

onde 'campo' é a chave.

Sem fatias
fonte
4
Essa solução para o SQL2000 não funciona para a última página no conjunto de resultados, a menos que o número total de linhas seja múltiplo do tamanho da página.
Bill Karwin
2

A seguir, serão exibidos 25 registros, excluindo os primeiros 50 registros de trabalhos no SQL Server 2012.

SELECT * FROM MyTable ORDER BY ID OFFSET 50 ROWS FETCH NEXT 25 ROWS ONLY;

você pode substituir o ID como sua exigência

Shb
fonte
Pls também, adicione isso é possível no SQL Server 2012
Usman Younas
2

Você deve ter cuidado ao usar a ROW_NUMBER() OVER (ORDER BY)declaração, pois o desempenho é muito ruim. O mesmo vale para o uso de expressões comuns de tabela com ROW_NUMBER()isso é ainda pior. Estou usando o seguinte snippet que provou ser um pouco mais rápido do que usar uma variável de tabela com uma identidade para fornecer o número da página.

DECLARE @Offset INT = 120000
DECLARE @Limit INT = 10

DECLARE @ROWCOUNT INT = @Offset+@Limit
SET ROWCOUNT @ROWCOUNT

SELECT * FROM MyTable INTO #ResultSet
WHERE MyTable.Type = 1

SELECT * FROM
(
    SELECT *, ROW_NUMBER() OVER(ORDER BY SortConst ASC) As RowNumber FROM
    (
        SELECT *, 1 As SortConst FROM #ResultSet
    ) AS ResultSet
) AS Page
WHERE RowNumber BETWEEN @Offset AND @ROWCOUNT

DROP TABLE #ResultSet
Patrik Melander
fonte
Isso irá retornar 11 linhas, não 10.
Aaron Bertrand
1

Eu uso essa técnica para paginação. Eu não busco todas as linhas. Por exemplo, se minha página precisar exibir as 100 principais linhas, busco apenas a cláusula 100 with where. A saída do SQL deve ter uma chave exclusiva.

A tabela possui o seguinte:

ID, KeyId, Rank

A mesma classificação será atribuída a mais de um KeyId.

SQL é select top 2 * from Table1 where Rank >= @Rank and ID > @Id

Pela primeira vez eu passo 0 para ambos. A segunda vez passa 1 e 14. A terceira vez passa 2 e 6 ....

O valor do 10º registro Rank & Id é passado para o próximo

11  21  1
14  22  1
7   11  1
6   19  2
12  31  2
13  18  2

Isso terá menos estresse no sistema

Ravi Ramaswamy
fonte
1

No SqlServer2005, você pode fazer o seguinte:

DECLARE @Limit INT
DECLARE @Offset INT
SET @Offset = 120000
SET @Limit = 10

SELECT 
    * 
FROM
(
   SELECT 
       row_number() 
   OVER 
      (ORDER BY column) AS rownum, column2, column3, .... columnX
   FROM   
     table
) AS A
WHERE 
 A.rownum BETWEEN (@Offset) AND (@Offset + @Limit-1) 
Aheho
fonte
Não deveria ser @Offset + @Limit - 1? Se @Limit for 10, retornará 11 linhas.
Aaron Bertrand
1

A melhor maneira de fazer isso sem perder tempo para solicitar registros é assim:

select 0 as tmp,Column1 from Table1 Order by tmp OFFSET 5000000 ROWS FETCH NEXT 50 ROWS ONLY

leva menos de um segundo!
melhor solução para mesas grandes.

Pishgaman.org
fonte
0

Estou pesquisando essa resposta há um tempo (para consultas genéricas) e descobri outra maneira de fazer isso no SQL Server 2000+ usando ROWCOUNT e cursores e sem TOP ou qualquer tabela temporária.

Usando o, SET ROWCOUNT [OFFSET+LIMIT]você pode limitar os resultados e, com os cursores, vá diretamente para a linha que deseja e, em seguida, faça um loop até o fim.

Portanto, sua consulta seria assim:

SET ROWCOUNT 75 -- (50 + 25)
DECLARE MyCursor SCROLL CURSOR FOR SELECT * FROM pessoas
OPEN MyCursor
FETCH ABSOLUTE 50 FROM MyCursor -- OFFSET
WHILE @@FETCH_STATUS = 0 BEGIN
    FETCH next FROM MyCursor
END
CLOSE MyCursor
DEALLOCATE MyCursor
SET ROWCOUNT 0
Capilé
fonte
Eu odiaria ver o desempenho deste quando você chegar no final da mesa ...
Aaron Bertrand
0

Com o SQL Server 2012 (11.x) e posterior e o Banco de Dados SQL do Azure, você também pode ter "fetch_row_count_expression", também pode ter a cláusula ORDER BY junto com isso.

USE AdventureWorks2012;  
GO  
-- Specifying variables for OFFSET and FETCH values    
DECLARE @skip int = 0  , @take int = 8;  
SELECT DepartmentID, Name, GroupName  
FROM HumanResources.Department  
ORDER BY DepartmentID ASC   
    OFFSET @skip ROWS   
    FETCH NEXT @take ROWS ONLY; 

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

Nota OFFSET Especifica o número de linhas a serem ignoradas antes de começar a retornar linhas da expressão de consulta. NÃO é o número da linha inicial. Portanto, deve ser 0 para incluir o primeiro registro.

Tejasvi Hegde
fonte