2008-10-19 14 views
37

Cuando trabajé en el Zend Framework's database component, intentamos abstraer la funcionalidad de la cláusula LIMIT admitida por MySQL, PostgreSQL y SQLite. Es decir, la creación de una consulta se podría hacer de esta manera:Emular la cláusula LIMIT de MySQL en Microsoft SQL Server 2000

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

Cuando la base de datos soporta LIMIT, esto produce una consulta SQL como la siguiente:

SELECT * FROM mytable ORDER BY somecolumn LIMIT 10, 20 

Esto fue más complejo para las marcas de base de datos que no es compatible con LIMIT (esa cláusula no es parte del lenguaje SQL estándar, por cierto). Si puede generar números de fila, haga que toda la consulta sea una tabla derivada, y en la consulta externa use BETWEEN. Esta fue la solución para Oracle e IBM DB2. Microsoft SQL Server 2005 tiene una función de número de fila similar, por lo que uno puede escribir la consulta de esta manera:

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 @[email protected]; 

Sin embargo, Microsoft SQL Server 2000 no tienen la función ROW_NUMBER().

Así que mi pregunta es, ¿se te ocurre una manera de emular la funcionalidad LIMIT en Microsoft SQL Server 2000, utilizando únicamente SQL? Sin usar cursores o T-SQL o un procedimiento almacenado. Tiene que admitir ambos argumentos para LIMIT, tanto el recuento como el desplazamiento. Las soluciones que usan una tabla temporal tampoco son aceptables.

Editar:

La solución más común para MS SQL Server 2000 parece ser como la de abajo, por ejemplo, para obtener filas 50 a 75:

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

Sin embargo, este doesn' t funciona si el conjunto total de resultados es, digamos 60 filas. La consulta interna devuelve 60 filas porque está en el top 75. Luego, la consulta externa devuelve las filas 35-60, que no cabe en la "página" deseada de 50-75. Básicamente, esta solución funciona a menos que necesite la última "página" de un conjunto de resultados que no sea un múltiplo del tamaño de la página.

Editar:

Otra solución funciona mejor, pero sólo si puede asumir el conjunto de resultados incluye una columna que es único:

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

Conclusión:

Ningún general solución de propósito parece existir para emular LIMIT en MS SQL Server 2000. Existe una buena solución si puede usar la función ROW_NUMBER() ion en MS SQL Server 2005.

Respuesta

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

Sí, esto está cerca, pero sólo funciona cuando hay una clave única en el resultado de la consulta provisional. ¿Cómo lo haría en una consulta GROUP BY o para una consulta que une varias tablas? –

4

Cuando solo necesita LIMIT, ms sql tiene la palabra clave TOP equivalente, por lo que es claro. Cuando necesita LIMIT con OFFSET, puede probar algunos hacks como se describió anteriormente, pero todos agregan algo de sobrecarga, es decir, para ordenar de una manera y luego de la otra, o de la operación NOT IN. Creo que todas esas cascadas no son necesarias. La solución más limpia en mi opinión sería simplemente usar TOP sin compensación en el lado de SQL, y luego buscar el registro de inicio requerido con el método de cliente apropiado, como mssql_data_seek en php.Si bien esta no es una solución pura de SQL, creo que es la mejor, ya que no agrega ningún sobrecarga (los registros salteados no se transferirán a la red cuando los busque, si eso es lo que le preocupa)

+0

Estoy de acuerdo con su recomendación al diseñar una aplicación. En mi caso, estaba diseñando un marco de acceso a la base de datos que supuestamente tenía una API consistente, pero producía SQL diferentes según las necesidades de diferentes marcas de bases de datos. La solución tenía que estar en la preparación de SQL, no en la búsqueda. –

+1

Buena idea, muchas gracias. En general, creo que su solución es la más viable. – 0plus1

+0

¡Es por eso que no me gusta MSSQL! –

5

Aquí hay otra solución que solo funciona en Sql Server 2005 y posteriores porque usa la declaración except. Pero lo comparto de todos modos. Si desea obtener los registros 50-75 escritura:

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 
+0

Gracias, parece que también funcionaría en Microsoft SQL Server 2005. Todavía nos deja sin una buena solución para Microsoft SQL Server 2000. Pero supongo que como es 2009, finalmente es razonable dejar de lado el soporte para 2000 de todos modos. –

+0

Sí, funciona en Sql Server 2005. –

+0

¡Eso es inteligente! No había pensado hacerlo de esa manera. – jocull

0

me gustaría tratar de implementar esto en mi ORM, ya que es bastante simple allí. Si realmente necesita estar en SQL Server, entonces miraría el código generado por linq a sql para la siguiente instrucción linq a sql e iría desde allí. El ingeniero de MSFT que implementó ese código fue parte del equipo de SQL durante muchos años y sabía lo que estaba haciendo.

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

+0

http://msdn.microsoft.com/en-us/library/bb386988.aspx dice: "La traducción es diferente para SQL Server 2000 y SQL Server 2005. Si planea usar Skip (Of TSource) con una consulta de cualquier complejidad, use SQL Server 2005. " –

+0

Mi punto es que hay muchas buenas soluciones para este problema si el requisito previo es ejecutar MS SQL Server 2005. Pero pocas de ellas funcionan en el caso general en MS SQL Server 2000. Es cierto que ahora es 2011 y es cada vez más difícil para justificar la ejecución de MS SQL Server 2000 a medida que avanza el tiempo. ¡Sin embargo, algunas personas todavía lo hacen! :( –

Cuestiones relacionadas