Заполнить лакуны в последовательности одним запросом
От: teapot2  
Дата: 15.11.18 19:19
Оценка:
Добрый день, друзья.

Есть таблица в БД. Два поля. Первое, скажем, дата — первичный ключ. Тип второго не имеет значения, и это поле NULLable.

Таблица, как ей и положено, заполнена записями. Есть такие, для которых второе поле NULL — таких много.

Надо одним запросом вместо этих NULL установить значение из ближайшей более ранней записи, в которой это поле было не NULL.

Попробую объяснить на конкретном примере. Есть таблица "Курс $ ММВБ", два столбца: дата и собственно курс. Дни (даты) идут подряд, в дни, когда торгов не было и биржевой курс не определялся, стоит NULL. Необходимо заменить все эти NULL на значение из ближайшего предшествующего дня, когда торги были и курс, таким образом, установлен. Для простоты считаем, что для самой ранней даты курс имеется.

Будет ли такой запрос эффективен? Количество записей — несколько тысяч.
Re: Заполнить лакуны в последовательности одним запросом
От: IT Россия linq2db.com
Дата: 15.11.18 21:08
Оценка:
Здравствуйте, teapot2, Вы писали:

with cte ([Date], [Value])
as
(
    select *
    from 
    (values
        ('2018-01-01', NULL),
        ('2018-01-02', NULL),
        ('2018-01-03', 3),
        ('2018-01-04', NULL),
        ('2018-01-05', 5),
        ('2018-01-06', 6),
        ('2018-01-07', NULL),
        ('2018-01-08', NULL),
        ('2018-01-09', 9),
        ('2018-01-10', NULL),
        ('2018-01-11', 1),
        ('2018-01-12', 2)
    ) as Data([Date], [Value])
)

select t1.Date, ISNULL(t1.Value, t2.Value)
from cte t1
outer apply (
    select top 1 *
    from cte t2
    where t2.Value is not null and t2.Date <= t1.Date
    order by t2.Date desc
) t2
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: Заполнить лакуны в последовательности одним запросом
От: _ABC_  
Дата: 16.11.18 03:22
Оценка: +1
Здравствуйте, IT, Вы писали:

IT>Здравствуйте, teapot2, Вы писали:


IT>
IT>select t1.Date, ISNULL(t1.Value, t2.Value)
IT>

По идее, isnull не нужен (лишний compute scalar), или я упускаю что-то тонкое?
Re[3]: Заполнить лакуны в последовательности одним запросом
От: IT Россия linq2db.com
Дата: 16.11.18 04:19
Оценка: 6 (1) :)
Здравствуйте, _ABC_, Вы писали:

_AB>По идее, isnull не нужен (лишний compute scalar), или я упускаю что-то тонкое?


На всякий случай
Если нам не помогут, то мы тоже никого не пощадим.
Re: Заполнить лакуны в последовательности одним запросом
От: Olaf Россия  
Дата: 16.11.18 19:41
Оценка:
Здравствуйте, teapot2, Вы писали:

T>...


T>Надо одним запросом вместо этих NULL установить значение из ближайшей более ранней записи, в которой это поле было не NULL.


Зависит от СУБД и версии, а вы ее не назвали. Например, для SQL Server 2012+ по аналогии с запросом выше от IT, т.е. через два обращения к таблице, но с использованием кадрирования...

with cte ([Date], [Value])
as
(
    select *
    from 
    (values
        ('2018-01-01', NULL),
        ('2018-01-02', NULL),
        ('2018-01-03', 3),
        ('2018-01-04', NULL),
        ('2018-01-05', 5),
        ('2018-01-06', 6),
        ('2018-01-07', NULL),
        ('2018-01-08', NULL),
        ('2018-01-09', 9),
        ('2018-01-10', NULL),
        ('2018-01-11', 1),
        ('2018-01-12', 2)
    ) as Data([Date], [Value])
)
select *, (select Value from cte where Date = LastNonNullDate) as LastValue
from
(
    select Date, Value,
           max(iif(Value is not null, Date, null)) over(order by Date rows unbounded preceding) as LastNonNullDate
    from cte
) a


Не удалось только получить план такого запроса на Microsoft SQL Server Express Edition 2014

An error occurred while executing batch. Error message is: Error processing execution plan results. The error message is:
В документе XML (1, 5631) присутствует ошибка.
Ошибка проверки экземпляра: "Window Spool" не является допустимым значением PhysicalOpType.

Re[2]: Заполнить лакуны в последовательности одним запросом
От: teapot2  
Дата: 18.11.18 17:24
Оценка:
Спасибо за ответ. Забыл сказать, что у меня Postgresql и он apply не умеет.

Обошелся простым update mytable set value = (select value from myteble wwhere ... limit 1) where value is null. Сработало.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.