LIMITE 10..20 no SQL Server


161

Estou tentando fazer algo como:

SELECT * FROM table LIMIT 10,20

ou

SELECT * FROM table LIMIT 10 OFFSET 10

mas usando o SQL Server

A única solução que encontrei parece exagerada:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE row > 5 and row <= 10

Eu também encontrei :

SELECT TOP 10 * FROM stuff; 

... mas não é o que eu quero fazer, pois não posso especificar o limite inicial.

Existe outra maneira de fazer isso?

Além disso, apenas curioso, existe uma razão pela qual o SQL Server não suporta a LIMITfunção ou algo semelhante? Eu não quero ser mau, mas isso realmente soa como algo que um SGBD precisa ... Se isso acontecer, sinto muito por ser tão ignorante! Eu tenho trabalhado com MySQL e SQL + nos últimos 5 anos, então ...


1
Usar um CTE para ROW_NUMBER()e limitar com TOPa largura do intervalo e uma WHEREcondição para um limite do intervalo é o melhor que consegui alcançar. Eu também notei um desempenho muito melhor se a TOPcláusula usa um literal em vez de variável
Jodrell

O problema de qualquer solução que envolva ROW_NUMBER () é que, se você não souber com antecedência quais colunas terá e se associará, e as tabelas unidas tiverem o mesmo nome, obterá a coluna "A coluna 'xxx' foi especificado várias vezes ". Isso não é tão incomum quanto pode parecer inicialmente. Eu uso o Dapper, e todas as minhas tabelas têm uma coluna Id. O Dapper divide e mapeia isso, então não quero renomeá-los, mas não posso usar o alias SELECT * FROM ([consulta original]). Ainda não descobri uma solução!
Steve Owen

Respostas:


104

A LIMITcláusula não faz parte do SQL padrão. É suportado como uma extensão de fornecedor para SQL pelo MySQL, PostgreSQL e SQLite.

Outras marcas de banco de dados podem ter recursos semelhantes (por exemplo, TOPno Microsoft SQL Server), mas nem sempre funcionam de forma idêntica.

É difícil usar TOPno Microsoft SQL Server para imitar a LIMITcláusula. Há casos em que simplesmente não funciona.

A solução que você mostrou ROW_NUMBER()está disponível no Microsoft SQL Server 2005 e posterior. Esta é a melhor solução (por enquanto) que funciona apenas como parte da consulta.

Outra solução é usar TOPpara buscar a primeira contagem + linhas de deslocamento e, em seguida, usar a API para procurar além das primeiras linhas de deslocamento .

Veja também:


135

Para o SQL Server 2012 +, você pode usar .

SELECT  *
FROM     sys.databases
ORDER BY name 
OFFSET  5 ROWS 
FETCH NEXT 5 ROWS ONLY 

10
SQL Server 2012 requer para especificar ORDER BY quando você usa OFFSET 5 linhas FETCH próximos 5 apenas linhas enquanto MySQL e SQLite não requer ORDER BY quando você usa LIMIT 5,5
Tomas Kubes

4
@ qub1n - O MySQL não garante que linhas você recebe nesse caso.
Martin Smith

3
Você precisa usar offsetou pode deixar essa linha de fora (assumindo que não deseja um deslocamento)?
precisa saber é o seguinte


Você consulta de exemplo funciona muito bem, mas se eu mudar o nome da tabela ea ordem pela col como abaixo SELECT * DE DimProduct ORDER BY ProductKey OFFSET 5 linhas FETCH próximos 5 apenas linhas Dá erroParse error at line: 4, column: 1: Incorrect syntax near 'OFFSET'
Shashwat

36

como você encontrou, este é o método preferido do servidor sql:

SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name) as row FROM sys.databases 
 ) a WHERE a.row > 5 and a.row <= 10

Por que o adepois da seleção interna? Eu suponho que você esteja dando um alias ao select interno, mas você nunca parece usá-lo ... Você deveria fazer isso em a.rowvez de apenas row?
Lucas

3
@ Lucas, você deve colocar um alias após a ( )tabela derivada, mas ela será liberada se você esquecer de usá-lo para se referir às colunas. Eu consertei isso ...
KM.

obrigado, descobri isso da maneira mais difícil (tentei deixar o apelido de fora).
Lucas

1
Votado +1: No entanto, a resposta do @MartinSmith é votada mais, depois de comparar o plano de execução com o dessa abordagem, descobri que essa solução funciona muito mais rápido.
Harsh

10

Se você estiver usando o SQL Server 2012+, vote na resposta de Martin Smith e use as extensões OFFSETe FETCH NEXTpara ORDER BY,

Se você tiver a infelicidade de ficar preso a uma versão anterior, poderá fazer algo assim,

WITH Rows AS
(
    SELECT
              ROW_NUMBER() OVER (ORDER BY [dbo].[SomeColumn]) [Row]
            , *
        FROM
              [dbo].[SomeTable]
)
SELECT TOP 10
          *
     FROM
         Rows
    WHERE Row > 10

Eu acredito que é funcionalmente equivalente a

SELECT * FROM SomeTable LIMIT 10 OFFSET 10 ORDER BY SomeColumn

e a maneira com o melhor desempenho que eu conheço em TSQL, antes do MS SQL 2012.


Se houver muitas linhas, você poderá obter um melhor desempenho usando uma tabela temporária em vez de uma CTE.


Promovido por apontar a resposta de Martin Smith (e vincular-se a ela) enquanto fornece uma solução pré-2012. Também para o conselho tabela temporária, porque você está correto :)
fujiiface

7

Infelizmente, o ROW_NUMBER()melhor é o que você pode fazer. Na verdade, é mais correto, porque os resultados de uma cláusula limitou toprealmente não têm significado sem respeitar uma ordem específica. Mas ainda é difícil de fazer.

Atualização: o Sql Server 2012 adiciona um limitrecurso semelhante via palavras-chave OFFSET e FETCH . Esta é a abordagem ansi-padrão, ao contrário de LIMIT, que é uma extensão MySql não-padrão.


@ Joel: Você pode explicar por que ROW_NUMBER () não consegue numerar as linhas da maneira como elas saem de ORDER BY? Eu sempre me perguntei por que o "OVER (ORDER BY name)" é obrigatório, mas acho que há uma boa razão para isso. Ou pelo menos uma razão.
Tomalak

3
porque não existe ordem sem ordem por cláusula. Você obtém qualquer ordem em que os registros estivessem disponíveis para o servidor e isso poderia mudar de solicitação de consulta para solicitação de consulta.
Joel Coehoorn

1
@marcgg: nunca li nenhuma indicação de que a Microsoft planeje implementar o LIMIT. Mesmo que eles tenham esse plano, os fornecedores de código fechado tendem a não anunciar previamente os recursos. Certamente seria um recurso útil, mas não sabemos quanto trabalho seria implementar, dado o código.
22330 Bill Karwin

3
Se você não quiser se repetir na cláusula ORDER BY, use o alias ROW_NUMBER () em vez do conjunto original de colunas.
23640 Peter Radocchia

2
@Tomalak: No que diz respeito ao SQL Server, a ordem usada para calcular ROW_NUMBER () não tem nenhuma relação com a ordem do conjunto de resultados. É por isso que você precisa especificá-los separadamente.
10139 LukeH

6

Que tal agora?

SET ROWCOUNT 10 

SELECT TOP 20 *
FROM sys.databases
ORDER BY database_id DESC

Ele fornece as últimas 10 linhas das 20 primeiras linhas. Uma desvantagem é que a ordem é revertida, mas, pelo menos, é fácil lembrar.


6
E se houver apenas 14 linhas na tabela? Você obtém as linhas 14 até 5, o que não é o mesmo que as linhas retornadas por LIMIT 10 OFFSET 10 (devem ser as linhas 14 até 11).
22477 Bill Karwin

2
SELECT TOP 10 *
FROM TABLE
WHERE IDCOLUMN NOT IN (SELECT TOP 10 IDCOLUMN FROM TABLE)

Deve dar registros 11-20. Provavelmente não é muito eficiente se estiver incrementando para obter mais páginas e não tem certeza de como isso pode ser afetado pelo pedido. Pode ser necessário especificar isso nas duas declarações WHERE.


1

Uma boa maneira é criar um procedimento:

create proc pagination (@startfrom int ,@endto int) as
SELECT * FROM ( 
  SELECT *, ROW_NUMBER() OVER (ORDER BY name desc) as row FROM sys.databases 
 ) a WHERE a.row > @startfrom and a.row <= @endto

assim como o limite 0,2 /////////////// execute pagination 0,4


1

Apenas para a solução de registro que funciona na maioria dos mecanismos de banco de dados, embora possa não ser a mais eficiente:

Select Top (ReturnCount) *
From (
    Select Top (SkipCount + ReturnCount) *
    From SourceTable
    Order By ReverseSortCondition
) ReverseSorted
Order By SortCondition

Nota anterior: a última página ainda conteria linhas ReturnCount, independentemente do que seja SkipCount. Mas isso pode ser uma coisa boa em muitos casos.


1

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

0
select * from (select id,name,ROW_NUMBER() OVER (ORDER BY id  asc) as row
from tableName1) tbl1
where tbl1.row>=10 and tbl1.row<=15

Irá imprimir linhas de 10 a 15.


0

Até agora, esse formato é o que está funcionando para mim (embora não seja o melhor desempenho):

SELECT TOP {desired amount of rows} * 
FROM (SELECT *, ROW_NUMBER() OVER (ORDER BY {order columns} asc)__row__ FROM {table})tmp
WHERE __row__ > {offset row count}

Uma observação lateral, paginar sobre dados dinâmicos pode levar a resultados estranhos / inesperados.


0

Na documentação online do MS SQL Server ( http://technet.microsoft.com/en-us/library/ms186734.aspx ), eis o exemplo deles que testei e trabalho para recuperar um conjunto específico de linhas. ROW_NUMBER exige um OVER, mas você pode solicitar o que quiser:

WITH OrderedOrders AS
(
  SELECT SalesOrderID, OrderDate,
  ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNumber
  FROM Sales.SalesOrderHeader 
) 
SELECT SalesOrderID, OrderDate, RowNumber  
FROM OrderedOrders 
WHERE RowNumber BETWEEN 50 AND 60;

0

Use todo o servidor SQL:; com tbl como (SELECT ROW_NUMBER () over (ordem de (selecione 1)) como RowIndex, * da tabela) selecione os 10 principais * de tbl onde RowIndex> = 10


-3
 SELECT * FROM users WHERE Id Between 15 and 25

imprimirá de 15 a 25 como limite em MYSQl


2
E se o usuário excluiu um registro entre 15 e 25?
Gökçer Gökdal
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.