Emulare la clausola LIM LIMIT in Microsoft SQL Server 2000

Quando ho lavorato sul componente del database di Zend Framework , abbiamo cercato di astrarre la funzionalità della clausola LIMIT supportata da MySQL, PostgreSQL e SQLite. Cioè, la creazione di una query può essere fatta in questo modo:

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

Quando il database supporta LIMIT , ciò produce una query SQL come la seguente:

 SELECT * FROM mytable ORDER BY somecolumn LIMIT 10, 20 

Questo era più complesso per le marche di database che non supportano LIMIT (la clausola non fa parte del linguaggio SQL standard, comunque). Se è ansible generare numeri di riga, rendere l’intera query una tabella derivata e nella query esterna utilizzare BETWEEN . Questa era la soluzione per Oracle e IBM DB2. Microsoft SQL Server 2005 ha una funzione di numero di riga simile, quindi è ansible scrivere la query in questo modo:

 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; 

Tuttavia, Microsoft SQL Server 2000 non ha la funzione ROW_NUMBER() .

Quindi la mia domanda è, puoi trovare un modo per emulare la funzionalità LIMIT in Microsoft SQL Server 2000, utilizzando esclusivamente SQL? Senza usare cursori o T-SQL o stored procedure. Deve supportare entrambi gli argomenti per LIMIT , sia il conteggio sia l’offset. Anche le soluzioni che utilizzano una tabella temporanea non sono accettabili.

Modificare:

La soluzione più comune per MS SQL Server 2000 sembra essere simile a quella riportata di seguito, ad esempio per ottenere le righe da 50 a 75:

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

Tuttavia, questo non funziona se il set di risultati totale è, diciamo 60 righe. La query interna restituisce 60 righe perché è nella top 75. Quindi la query esterna restituisce le righe 35-60, che non rientrano nella “pagina” desiderata di 50-75. Fondamentalmente, questa soluzione funziona a meno che non sia necessaria l’ultima “pagina” di un set di risultati che non è un multiplo delle dimensioni della pagina.

Modificare:

Un’altra soluzione funziona meglio, ma solo se si può supporre che il set di risultati includa una colonna che è unica:

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

Conclusione:

Nessuna soluzione generica sembra esistere per emulare LIMIT in MS SQL Server 2000. Una buona soluzione esiste se è ansible utilizzare la funzione ROW_NUMBER() in MS SQL Server 2005.

    Ecco un’altra soluzione che funziona solo in Sql Server 2005 e versioni successive perché utilizza l’istruzione except. Ma lo condivido comunque. Se vuoi ottenere i record 50 – 75 scrivi:

     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 hai bisogno solo di LIMIT, ms sql ha la parola chiave TOP equivalente, quindi è chiaro. Quando hai bisogno di LIMIT con OFFSET, puoi provare alcuni hack come descritto in precedenza, ma tutti aggiungono un po ‘di overhead, cioè per ordinare un modo e poi l’altro, o l’operazione NON INIZIALE. Penso che tutte quelle cascate non siano necessarie. La soluzione più pulita nella mia opinione sarebbe solo utilizzare TOP senza offset sul lato SQL, quindi cercare il record iniziale richiesto con il metodo client appropriato, come mssql_data_seek in php. Anche se questa non è una soluzione SQL pura, penso che sia la migliore perché non aggiunge alcun overhead (i record saltati non saranno trasferiti sulla rete quando li cerchi, se è questo che ti preoccupa ).

    Proverò a implementarlo nel mio ORM dato che è piuttosto semplice lì. Se davvero deve essere in SQL Server, guarderò il codice generato da linq a sql per la seguente istruzione linq to sql e passerò da lì. L’ingegnere MSFT che ha implementato quel codice faceva parte del team SQL per molti anni e sapeva cosa stava facendo.

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