Importante: considere actualizar a MySQL 8+ y use la función definida y documentada ROW_NUMBER (), y elimine los viejos hacks vinculados a una versión antigua limitada de características de MySQL
Ahora aquí está uno de esos hacks:
Las respuestas aquí que usan variables de consulta en su mayoría / todas parecen ignorar el hecho de que la documentación dice (parafraseando):
No confíe en los elementos de la lista SELECT que se evalúan en orden de arriba a abajo. No asigne variables en un elemento SELECT y no las use en otro.
Como tal, existe el riesgo de que generen la respuesta incorrecta, porque generalmente hacen un
select
(row number variable that uses partition variable),
(assign partition variable)
Si alguna vez se evalúan de abajo hacia arriba, el número de fila dejará de funcionar (sin particiones)
Por lo tanto, debemos usar algo con un orden de ejecución garantizado. Ingrese CASO CUANDO:
SELECT
t.*,
@r := CASE
WHEN col = @prevcol THEN @r + 1
WHEN (@prevcol := col) = null THEN null
ELSE 1 END AS rn
FROM
t,
(SELECT @r := 0, @prevcol := null) x
ORDER BY col
Como se describe en ld, el orden de asignación de prevcol es importante: se debe comparar prevcol con el valor de la fila actual antes de asignarle un valor de la fila actual (de lo contrario, sería el valor col de las filas actuales, no el valor col de la fila anterior) .
Así es como encaja esto:
El primero CUANDO se evalúa. Si la columna de esta fila es igual a la columna de la fila anterior, entonces @r se incrementa y se devuelve desde CASE. Estos valores de retorno de led se almacenan en @r. Es una característica de MySQL que la asignación devuelve el nuevo valor de lo que se asigna a @r en las filas de resultados.
Para la primera fila del conjunto de resultados, @prevcol es nulo (se inicializa como nulo en la subconsulta), por lo que este predicado es falso. Este primer predicado también devuelve falso cada vez que cambia la columna (la fila actual es diferente a la fila anterior). Esto hace que el segundo CUANDO sea evaluado.
El segundo predicado WHEN siempre es falso, y existe únicamente para asignar un nuevo valor a @prevcol. Debido a que la columna de esta fila es diferente de la columna de la fila anterior (sabemos esto porque si fuera lo mismo, el primer CUANDO se hubiera utilizado), tenemos que asignar el nuevo valor para mantenerlo para probar la próxima vez. Debido a que la asignación se realiza y luego el resultado de la asignación se compara con nulo, y todo lo que se equipare con nulo es falso, este predicado siempre es falso. Pero al menos evaluarlo hizo su trabajo al mantener el valor de col de esta fila, por lo que puede evaluarse contra el valor de col de la siguiente fila
Debido a que el segundo CUANDO es falso, significa que en situaciones donde la columna por la que estamos particionando (col) ha cambiado, es ELSE el que da un nuevo valor para @r, reiniciando la numeración desde 1
Llegamos a una situación en la que esto:
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY pcol1, pcol2, ... pcolX ORDER BY ocol1, ocol2, ... ocolX) rn
FROM
t
Tiene la forma general:
SELECT
t.*,
@r := CASE
WHEN col1 = @pcol1 AND col2 = @pcol2 AND ... AND colX = @pcolX THEN @r + 1
WHEN (@pcol1 := pcol1) = null OR (@pcol2 := col2) = null OR ... OR (@pcolX := colX) = null THEN null
ELSE 1
END AS rn
FROM
t,
(SELECT @r := 0, @pcol1 := null, @pcol2 := null, ..., @pcolX := null) x
ORDER BY pcol1, pcol2, ..., pcolX, ocol1, ocol2, ..., ocolX
Notas al pie:
La p en pcol significa "partición", la o en ocol significa "orden" - en la forma general eliminé la "anterior" del nombre de la variable para reducir el desorden visual
Los corchetes (@pcolX := colX) = null
son importantes. Sin ellos asignarás nulo a @pcolX y las cosas dejarán de funcionar
Es un compromiso que el conjunto de resultados también debe ser ordenado por las columnas de partición, para que la columna anterior se pueda comparar. Por lo tanto, no puede ordenar su número de rown de acuerdo con una columna, pero su conjunto de resultados se ordenó a otro. Es posible que pueda resolver esto con subconsultas, pero creo que los documentos también indican que el pedido de subconsultas puede ignorarse a menos que se use LIMIT y esto podría afectar actuación
No he profundizado en ello más allá de probar que el método funciona, pero si existe el riesgo de que los predicados en el segundo CUANDO se optimicen (cualquier cosa en comparación con nulo es nulo / falso, ¿por qué molestarse en ejecutar la asignación?) Y no se ejecuta , también se detiene. Esto no parece suceder en mi experiencia, pero con mucho gusto aceptaré comentarios y propondré una solución si pudiera ocurrir razonablemente.
Puede ser conveniente convertir los valores nulos que crean @pcolX a los tipos reales de sus columnas, en la subconsulta que crea las variables @pcolX, a saber: select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)
greatest-n-per-group
para guiarlo a preguntas similares.