With the following table, I need to make a query to replace the null values of “time” (number of minutes, integer) by a value consistent with the preceding and following values, with partitioning by “esz” and sorting by ascending “rank”. In the example, the data is already sorted. Ideally:
- the value should be as close as possible to the preceding one (+1) when it exists
- the value should be as close as possible to the one that follows (-1) if none precedes (rank 1)
- if the difference between the preceding and following values is insufficient (0 or 1, for example), then the value must be equal to the smaller of the two.
- the value must never be less than the previous one, nor greater than the following one. No "esz" can have only empty "time" values I've tried LAG() and LEAD() but, as the null values can be anywhere, I don't think this is the best option. I guess I'd have to use a recursive query, but I can't do it. Can anyone help me find a solution? Thank you very much
| id | esz | code | rank | time |
|---|---|---|---|---|
| 1 | 1 | "SOM" | 1 | 5 |
| 2 | 1 | "QUI" | 2 | NULL |
| 3 | 1 | "VER" | 3 | 10 |
| 4 | 1 | "NSC" | 4 | 15 |
| 5 | 1 | "3SM" | 99 | NULL |
| 6 | 2 | "QUI" | 1 | 7 |
| 7 | 2 | "VER" | 2 | NULL |
| 8 | 2 | "SOM" | 3 | NULL |
| 9 | 2 | "NSC" | 4 | 12 |
| 10 | 2 | "3SM" | 99 | NULL |
| 11 | 3 | "NSC" | 1 | NULL |
| 12 | 3 | "VER" | 2 | NULL |
| 13 | 3 | "QUI" | 3 | 11 |
| 14 | 3 | "SOM" | 4 | 12 |
| 15 | 3 | "3SM" | 99 | NULL |
| 16 | 4 | "SOM" | 1 | 2 |
| 17 | 4 | "QUI" | 2 | NULL |
| 18 | 4 | "NSC" | 3 | 3 |
| 19 | 4 | "VER" | 4 | NULL |
| 20 | 4 | "3SM" | 99 | NULL |
| 21 | 5 | "NSC" | 1 | NULL |
| 22 | 5 | "SOM" | 2 | 4 |
| 23 | 5 | "VER" | 3 | NULL |
| 24 | 5 | "QUI" | 4 | 7 |
| 25 | 5 | "3SM" | 99 | NULL |
Sample output:
| id | esz | code | rank | time |
|---|---|---|---|---|
| 1 | 1 | "SOM" | 1 | 5 |
| 2 | 1 | "QUI" | 2 | 6 |
| 3 | 1 | "VER" | 3 | 10 |
| 4 | 1 | "NSC" | 4 | 15 |
| 5 | 1 | "3SM" | 99 | 16 |
| 6 | 2 | "QUI" | 1 | 7 |
| 7 | 2 | "VER" | 2 | 8 |
| 8 | 2 | "SOM" | 3 | 9 |
| 9 | 2 | "NSC" | 4 | 12 |
| 10 | 2 | "3SM" | 99 | 13 |
| 11 | 3 | "NSC" | 1 | 9 |
| 12 | 3 | "VER" | 2 | 10 |
| 13 | 3 | "QUI" | 3 | 11 |
| 14 | 3 | "SOM" | 4 | 12 |
| 15 | 3 | "3SM" | 99 | 13 |
| 16 | 4 | "SOM" | 1 | 2 |
| 17 | 4 | "QUI" | 2 | 2 |
| 18 | 4 | "NSC" | 3 | 3 |
| 19 | 4 | "VER" | 4 | 4 |
| 20 | 4 | "3SM" | 99 | 5 |
| 21 | 5 | "NSC" | 1 | 3 |
| 22 | 5 | "SOM" | 2 | 4 |
| 23 | 5 | "VER" | 3 | 5 |
| 24 | 5 | "QUI" | 4 | 7 |
| 25 | 5 | "3SM" | 99 | 8 |
I can handle most of the cases in the table with LAG() and LEAD() and CASE. But I still have problems with some of them: id=11 and id=12 for example. Either I have to go backwards from the value of id=13, or I have to know when filling id=11 that I have to leave a value available to fill id=12.
My last query:
SELECT
esz,
code,
rank,
CASE
WHEN time IS NOT NULL THEN time
ELSE
CASE
WHEN rank = 1 THEN 1
WHEN LAG(time) OVER(PARTITION BY esz ORDER BY rank) IS NOT NULL AND LEAD(time) OVER(PARTITION BY esz ORDER BY rank) IS NULL THEN LAG(time) OVER(PARTITION BY esz ORDER BY rank) + 1
WHEN LEAD(time) OVER(PARTITION BY esz ORDER BY rank) IS NOT NULL AND LAG(time) OVER(PARTITION BY esz ORDER BY rank) IS NULL THEN LEAD(time) OVER(PARTITION BY esz ORDER BY rank) - 1
WHEN LAG(time) OVER(PARTITION BY esz ORDER BY rank) IS NOT NULL AND LEAD(time) OVER(PARTITION BY esz ORDER BY rank) IS NOT NULL THEN
CASE
WHEN LEAD(time) OVER(PARTITION BY esz ORDER BY rank) - LAG(time) OVER(PARTITION BY esz ORDER BY rank) <= 1 THEN LAG(time) OVER(PARTITION BY esz ORDER BY rank)
ELSE LAG(time) OVER(PARTITION BY esz ORDER BY rank) +1
END
WHEN rank > LEAD(rank) OVER(PARTITION BY esz ORDER BY rank) OR LEAD(rank) OVER(PARTITION BY esz ORDER BY rank) IS NULL THEN 999
END
END AS time
FROM mytable
ORDER BY esz, rank
Here's the result:
| id | esz | code | rank | time |
|---|---|---|---|---|
| 1 | 1 | "SOM" | 1 | 5 |
| 2 | 1 | "QUI" | 2 | 6 |
| 3 | 1 | "VER" | 3 | 10 |
| 4 | 1 | "NSC" | 4 | 15 |
| 5 | 1 | "3SM" | 99 | 16 |
| 6 | 2 | "QUI" | 1 | 7 |
| 7 | 2 | "VER" | 2 | 8 |
| 8 | 2 | "SOM" | 3 | <11> |
| 9 | 2 | "NSC" | 4 | 12 |
| 10 | 2 | "3SM" | 99 | 13 |
| 11 | 3 | "NSC" | 1 | <1> |
| 12 | 3 | "VER" | 2 | 10 |
| 13 | 3 | "QUI" | 3 | 11 |
| 14 | 3 | "SOM" | 4 | 12 |
| 15 | 3 | "3SM" | 99 | 13 |
| 16 | 4 | "SOM" | 1 | 2 |
| 17 | 4 | "QUI" | 2 | 2 |
| 18 | 4 | "NSC" | 3 | 3 |
| 19 | 4 | "VER" | 4 | 4 |
| 20 | 4 | "3SM" | 99 | <999> |
| 21 | 5 | "NSC" | 1 | <1> |
| 22 | 5 | "SOM" | 2 | 4 |
| 23 | 5 | "VER" | 3 | 5 |
| 24 | 5 | "QUI" | 4 | 7 |
| 25 | 5 | "3SM" | 99 | 8 |
I've highlighted the values that differ from the expected result. It's not perfect, but there are no inconsistencies. However, there would be a problem if more than 3 consecutive overrides were inserted.

NULLvalue will come at columntime, for condition:over(partition by esz order by ranks). - @Nicolas