2012-10-04 30 views
6

El uso de variables de usuario al número filas

a menudo encontrar respuestas aquí en sugiriendo así que el uso de variables de usuario para numerar alguna cosa o la otra. Quizás el ejemplo más claro sea una consulta para seleccionar cada segunda fila de un conjunto de resultados dado. (Esta pregunta y consulta es similar a this answer, pero fue this la respuesta que en realidad desencadenó esta pregunta aquí).garantías cuando se utilizan variables de usuario al número filas

SELECT * 
FROM (SELECT *, (@row := @row + 1) AS rownum 
     FROM (SELECT @row := 0) AS init, tablename 
     ORDER BY tablename.ordercol 
    ) sub 
WHERE rownum % 2 = 1 

Este enfoque parece funcionar normalmente.

razones para tener cuidado

Por otro lado, el MySQ docs Estado:

Como regla general, nunca se debe asignar un valor a una variable de usuario y leer el valor dentro de la misma declaración . Puede obtener los resultados que espera, pero esto no está garantizado. El orden de evaluación para expresiones que involucran variables de usuario no está definido y puede cambiar en función de los elementos contenidos en una declaración dada; Además, no se garantiza que este orden sea el mismo entre versiones del servidor MySQL.

pregunta Core

Así que mi pregunta no es cómo lograr un ordenamiento como el uso de servidores actuales, pero en cambio si la solución que se sugiere el uso de variables de usuario es garantizado para trabajar en todas las circunstancias (razonable) y para todas las versiones futuras de MySQL.

Por "garantías" Me refiero a fuentes autorizadas como la documentación de MySQL o algunas conformidades estándar de MySQL. Al carecer de respuestas autorizadas, otras fuentes como los tutoriales de uso frecuente o partes del código fuente de MySQL podrían citarse en su lugar. Por "works" Me refiero al hecho de que las asignaciones se ejecutarán secuencialmente, una vez por fila del resultado, y en el orden inducido por la línea ORDER BY.

Ejemplo de consulta de ruptura

Para dar un ejemplo de cómo fácilmente fallan cosas:

SELECT * 
FROM (SELECT *, (@row := @row + 1) AS rownum 
     FROM (SELECT @row := 0) AS init, tablename 
     HAVING rownum > 0 
     ORDER BY tablename.ordercol 
    ) sub 
WHERE rownum % 2 = 1 

producirá un resultado vacío en el MySQL 5.5.27 instalada actualmente en SQL Fiddle. El motivo parece ser que la condición HAVING hace que la expresión rownum se evalúe dos veces, por lo que el resultado final solo tendrá números pares. Tengo una idea de lo que está sucediendo detrás de escena, y no estoy afirmando que la consulta con el HAVING tiene mucho sentido. Solo quiero demostrar que hay una línea fina entre el código que funciona y el código que se ve muy similar pero se rompe.

+0

@hvd, gracias por el [puntero] (http://bugs.mysql.com/bug.php?id=35893) en la solicitud de función 'ROW_NUMBER'. [Dems] (http://stackoverflow.com/users/53341/dems) recientemente [me dijo] (http://stackoverflow.com/a/12727443) acerca de esta función. Parece bastante potente y bien definido para arrancar. Lástima que parece haber poco progreso en esa solicitud desde 2008. – MvG

+0

Eliminé mi comentario cuando vi que 'ROW_NUMBER' ya se mencionó en los comentarios y respuestas a su pregunta anterior :) – hvd

+0

[¿Ya leyó este artículo?] (http://www.xaprb.com/blog/2006/12/15/advanced-mysql-user-variable-techniques/) Particularmente, la parte 'MySQL no evalúa las expresiones que contienen variables de usuario hasta que se envían al cliente, ' –

Respuesta

9

Leyó mal la declaración. Se relaciona con el orden de las expresiones en la lista SELECCIONAR, cuando se usan múltiples variables.
Tal como se presenta, el ORDER BY en esta declaración de variable única tiene un orden garantizado hasta la versión actual de MySQL y nada en ese texto sugiere que vaya a cambiar.

Pero garantizan el futuro? Quién sabe.


cuanto a la pregunta de ruptura , que haya más mal entendido cómo funciona MySQL. Vamos a desglosar tu consulta. Tome nota de esta afirmación en el manual

En una instrucción SELECT, cada expresión de selección se evalúa solo cuando se envía al cliente. Esto significa que en un HAVING, GROUP BY o ORDER BY cláusula, en referencia a una variable que se asigna un valor en la lista de selección expresión no funciona como se esperaba

El orden del proceso de consultas es más o menos

FROM/JOIN 
WHERE/ON 
GROUP BY/ROLLUP 
HAVING 
UNION 
SELECT 
ORDER BY 
@variable resolution 

Su petición "roto" intenta utilizar la variable dentro del mismo nivel, que es casi tan pecaminoso como el uso de un DONDE/cláusula contra un alias de columna HAVING. Es por eso que nunca verá las soluciones de row_numbering basadas en variables de MySQL usando la variable en el mismo nivel de consulta, siempre está en una subconsulta. La consulta externa se puede considerar client de la consulta interna en qué etapa se ha procesado la variable/placeholder-expression. Con su argumento, puede simplemente romperlo usando una cláusula WHERE que implique al @row directamente (sí, se ejecutará).

+0

No estoy convencido, pero incluso si medí la declaración, eso aún nos deja sin garantías explícitas tampoco, y mi pregunta permanece. Cuando escribe que las versiones actuales garantizan el orden, ¿tiene alguna referencia para respaldar esto, o es solo una experiencia personal? – MvG

+0

Además de la experiencia personal, tenga en cuenta el hecho de que la solución es bien conocida y frecuentemente citada. Encuéntrame cualquier referencia o comentario que no haya funcionado en todo momento. Eso es lo suficientemente bueno para mí. – RichardTheKiwi

+0

[Esta respuesta] (http://stackoverflow.com/a/12171808) no funcionó para el OP de esa pregunta, de acuerdo con los comentarios y una [pregunta de seguimiento] (http://stackoverflow.com/q/12243779) Hasta ahora personalmente considero que este problema es la causa más probable. – MvG

Cuestiones relacionadas