Mam następujące dane wejściowe:
id | value
----+-------
1 | 136
2 | NULL
3 | 650
4 | NULL
5 | NULL
6 | NULL
7 | 954
8 | NULL
9 | 104
10 | NULL
Oczekuję następującego wyniku:
id | value
----+-------
1 | 136
2 | 136
3 | 650
4 | 650
5 | 650
6 | 650
7 | 954
8 | 954
9 | 104
10 | 104
Trywialnym rozwiązaniem byłoby połączenie tabel z <
relacją, a następnie wybranie MAX
wartości w 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;
Jednak trywialne wykonanie tego kodu stworzyłoby wewnętrznie kwadrat liczby wierszy tabeli wejściowej ( O (n ^ 2) ). Spodziewałem się, że t-sql zoptymalizuje to - na poziomie bloku / rekordu zadanie do wykonania jest bardzo łatwe i liniowe, zasadniczo dla pętli for ( O (n) ).
Jednak w moich eksperymentach najnowszy MS SQL 2016 nie może poprawnie zoptymalizować tego zapytania, co uniemożliwia wykonanie tego zapytania dla dużej tabeli wejściowej.
Ponadto zapytanie musi być uruchamiane szybko, co sprawia, że podobnie łatwe (ale bardzo różne) rozwiązanie oparte na kursorach jest niemożliwe.
Użycie tabeli tymczasowej opartej na pamięci może być dobrym kompromisem, ale nie jestem pewien, czy można ją uruchomić znacznie szybciej, biorąc pod uwagę, że moje przykładowe zapytanie wykorzystujące podzapytania nie działało.
Zastanawiam się również nad wykryciem jakiejś funkcji okienkowania z dokumentów t-sql, co może być trudne do zrobienia, co chcę. Na przykład suma skumulowana działa bardzo podobnie, ale nie mogłem oszukać, aby uzyskać najnowszy element inny niż null, a nie sumę elementów wcześniej.
Idealnym rozwiązaniem byłoby szybkie zapytanie bez kodu proceduralnego lub tabel tymczasowych. Alternatywnie, również rozwiązanie z tabelami tymczasowymi jest w porządku, ale procedury powtarzania tabeli nie są.
źródło