Duda SQL - rank() or last_value()

richmonde

Buenas,

Creo que le estoy cogiendo gustillo a esto de consultar dudas aquí. Lo veo una forma de mejorar. Dicho esto, al lío.

Tengo una query que:

  • Me devuelve muchos resultados (del orden de 1 a 4 millones de filas, dependiendo de las fechas)
  • Necesito sacar el último valor de un usuario para una serie de campos, pero no se si

Opciones:
Opción A: Usar la window function last_value() para cada uno de esos campos que necesito (lo cual requiere patearse la tabla entera para cada campo al que quiera acceder a su ultimo valor)
Opción B: Usar la función rank() para asignarle a cada fila un numero del 1 al que sea según las ocurrencias de cada usuario, y luego filtrar por el usuario con rank_field con 1.

La máquina detrás no es problema en términos de computación, pero tampoco quiero matarla. (Muchos nodos Amazon Redshift)

128 CPU,
20 TB Ram
1PB de HDD

Troyer

Yo lo que hago para coger el último registro de algo es hacer MAX(id), otra opción es ORDER BY id DESC LIMIT 1.

Como puedes ver la computación es nula, no creo que te de problemas.

Zerokkk

Un truco para casos así, que no sé si podrás hacer pero que definitivamente te optimizará la solución una barrabastada, es crear una tabla de datos contextual. "UserContextData" o algo así, pero básicamente sería una tabla en la cual cada fila está asociada con un user_id concreto, y luego cada columna puede guardar datos de interés para ti.

Por ejemplo, cuando un usuario guarde un registro que contenga ese número más alto que los demás, lo guardas ahí, y entonces cuando tengas que fetchearlo no tienes que recorrerte toda la tabla para hacer la comprobación, simplemente lo coges de su tabla de datos contextual porque sabes que en cierto campo de dicha tabla tendrás el valor deseado. Por supuesto, creo que está claro que requerirás de indexar la tabla de usuarios normal si quieres que funcione bien.

En esencia, la idea de estas tablas es proveer de un contenedor de datos que te suponen de cierta utilidad, que fetchearlos a través de queries pueden ser computacionalmente costosos. Pero si la operación no le cuesta demasiado a la máquina y no tienes una cantidad de requests muy bestia, tampoco te tendrás que rayar tanto.

1
richmonde

Es que en este caso el max no sirve.

Es coger el valor de un usuario hasta conseguir completar un nodo en un juego por primera vez (el cual puede ser satisfactoriamente, o como derrota, hasta que lo complete), por lo tanto, max para varchar no me sirve, y como puede hacer posteriores intentos, no me sirve, ya que hay mas campos donde el max no aplicaría.

Algo del estilo:

select a,b,c,d
from (
select rank, a,b,c,d
from tabla
where condiciones
)
where rank = 1

o bien

select distinct 
last_value(a) over (partition by ...),
last_value(b) over (partition by ...),
last_value(c) over (partition by ...),
last_value(d) over (partition by ...),
...
from tabla
where condiciones
B

yo suelo hacer partition by:

SELECT * FROM (
SELECT ROW_NUMBER() OVER(PARTITION BY A, B ORDER BY C DESC) AS HS, *
) as D WHERE HS = 1

Igualmente siempre es mejor que si tienes dudas de rendimiento, piques las dos formas y ejecutes el plan de ejecución del sql management:

https://stackoverflow.com/questions/7359702/how-do-i-obtain-a-query-execution-plan

1
richmonde

Al final me he decantado por el rank()

  • Computacionalmente el coste es muy parecido con el explain plan.
  • A la hora de adaptar el codigo para ver no solo el ultimo evento, sino posibilidad de los ultimos N eventos (pasando el campo por parametro) es más facil
  • Modificar una clausula de over (partition window ... order by ...) donde hay más de 6 elementos, es MUY tedioso, para cada campo.

Gracias igualmente!

Usuarios habituales