Emula a cláusula MySQL LIMIT no Microsoft SQL Server 2000

Quando trabalhei no componente de database do Zend Framework , tentamos abstrair a funcionalidade da cláusula LIMIT suportada pelo MySQL, PostgreSQL e SQLite. Ou seja, criar uma consulta poderia ser feito desta maneira:

 $select = $db->select(); $select->from('mytable'); $select->order('somecolumn'); $select->limit(10, 20); 

Quando o database suporta LIMIT , isso produz uma consulta SQL como a seguinte:

 SELECT * FROM mytable ORDER BY somecolumn LIMIT 10, 20 

Isso era mais complexo para marcas de database que não suportam LIMIT (a propósito, essa cláusula não faz parte da linguagem SQL padrão). Se você puder gerar números de linha, torne toda a consulta uma tabela derivada e, na consulta externa, use BETWEEN . Esta foi a solução para Oracle e IBM DB2. O Microsoft SQL Server 2005 tem uma function de número de linha semelhante, portanto, é possível escrever a consulta desta maneira:

 SELECT z2.* FROM ( SELECT ROW_NUMBER OVER(ORDER BY id) AS zend_db_rownum, z1.* FROM ( ...original SQL query... ) z1 ) z2 WHERE z2.zend_db_rownum BETWEEN @offset+1 AND @offset+@count; 

No entanto, o Microsoft SQL Server 2000 não tem a function ROW_NUMBER() .

Então, minha pergunta é: você pode criar uma maneira de emular a funcionalidade LIMIT no Microsoft SQL Server 2000, usando apenas o SQL? Sem usar cursores ou T-SQL ou um procedimento armazenado. Ele tem que suportar ambos os argumentos para LIMIT , count e offset. Soluções usando uma tabela temporária também não são aceitáveis.

Editar:

A solução mais comum para o MS SQL Server 2000 parece ser a seguinte, por exemplo, para obter as linhas 50 a 75:

 SELECT TOP 25 * FROM ( SELECT TOP 75 * FROM table ORDER BY BY field ASC ) a ORDER BY field DESC; 

No entanto, isso não funciona se o conjunto de resultados total for, digamos, 60 linhas. A consulta interna retorna 60 linhas porque está no top 75. Em seguida, a consulta externa retorna as linhas 35-60, o que não cabe na “página” desejada de 50-75. Basicamente, esta solução funciona a menos que você precise da última “página” de um conjunto de resultados que não seja um múltiplo do tamanho da página.

Editar:

Outra solução funciona melhor, mas somente se você puder assumir que o conjunto de resultados inclui uma coluna exclusiva:

 SELECT TOP n * FROM tablename WHERE key NOT IN ( SELECT TOP x key FROM tablename ORDER BY key ); 

Conclusão:

Nenhuma solução de propósito geral parece existir para emular LIMIT no MS SQL Server 2000. Uma boa solução existe se você puder usar a function ROW_NUMBER() no MS SQL Server 2005.

   

    Aqui está outra solução que só funciona no Sql Server 2005 e mais recente porque usa a instrução except. Mas eu compartilho mesmo assim. Se você quiser obter os registros 50 – 75 escreva:

     select * from ( SELECT top 75 COL1, COL2 FROM MYTABLE order by COL3 ) as foo except select * from ( SELECT top 50 COL1, COL2 FROM MYTABLE order by COL3 ) as bar 
     SELECT TOP n * FROM tablename WHERE key NOT IN ( SELECT TOP x key FROM tablename ORDER BY key DESC ); 

    Quando você precisa apenas de LIMIT, ms sql tem a palavra-chave TOP equivalente, o que é claro. Quando você precisa de LIMIT com OFFSET, você pode tentar alguns hacks como descrito anteriormente, mas todos eles adicionam alguma sobrecarga, ou seja, para fazer o pedido de uma forma e depois da outra, ou a operação NOT IN. Eu acho que todas essas cascatas não são necessárias. A solução mais limpa na minha opinião seria apenas usar TOP sem offset no lado SQL, e então procurar o registro inicial requerido com o método client apropriado, como mssql_data_seek no php. Embora esta não seja uma solução SQL pura, acho que é a melhor, porque não adiciona nenhuma sobrecarga (os registros ignorados não serão transferidos na rede quando você procura por eles, se é isso que o preocupa ).

    Eu tentaria implementar isso no meu ORM, pois é bastante simples lá. Se realmente precisa estar no SQL Server, então eu olharia o código gerado pelo linq para sql para a seguinte instrução linq para sql e de lá. O engenheiro da MSFT que implementou esse código fazia parte da equipe de SQL por muitos anos e sabia o que estava fazendo.

    var result = myDataContext.mytable.Skip (pageIndex * pageSize) .Take (pageSize)