Tengo la siguiente entrada:
id | value
----+-------
1 | 136
2 | NULL
3 | 650
4 | NULL
5 | NULL
6 | NULL
7 | 954
8 | NULL
9 | 104
10 | NULL
Espero el siguiente resultado:
id | value
----+-------
1 | 136
2 | 136
3 | 650
4 | 650
5 | 650
6 | 650
7 | 954
8 | 954
9 | 104
10 | 104
La solución trivial sería unir las tablas con una <
relación y luego seleccionar el MAX
valor en a GROUP BY
:
WITH tmp AS (
SELECT t2.id, MAX(t1.id) AS lastKnownId
FROM t t1, t t2
WHERE
t1.value IS NOT NULL
AND
t2.id >= t1.id
GROUP BY t2.id
)
SELECT
tmp.id, t.value
FROM t, tmp
WHERE t.id = tmp.lastKnownId;
Sin embargo, la ejecución trivial de este código crearía internamente el cuadrado del recuento de las filas de la tabla de entrada ( O (n ^ 2) ). Esperaba que t-sql lo optimizara: en un nivel de bloque / registro, la tarea a realizar es muy fácil y lineal, esencialmente un bucle for ( O (n) ).
Sin embargo, en mis experimentos, el último MS SQL 2016 no puede optimizar esta consulta correctamente, lo que hace que esta consulta sea imposible de ejecutar para una tabla de entrada grande.
Además, la consulta debe ejecutarse rápidamente, lo que hace que una solución similarmente fácil (pero muy diferente) basada en el cursor no sea factible.
Usar una tabla temporal respaldada por memoria podría ser un buen compromiso, pero no estoy seguro de si se puede ejecutar significativamente más rápido, considerando que mi consulta de ejemplo usando subconsultas no funcionó.
También estoy pensando en desenterrar alguna función de ventanas de los documentos de t-sql, lo que podría ser engañado para hacer lo que quiero. Por ejemplo, la suma acumulativa está haciendo algo muy similar, pero no pude engañarlo para obtener el último elemento no nulo, y no la suma de los elementos anteriores.
La solución ideal sería una consulta rápida sin código de procedimiento o tablas temporales. Alternativamente, también una solución con tablas temporales está bien, pero iterar la tabla de forma procesal no lo está.
fuente