Обновление LEAD / LAG или рекурсивным CTE? - PullRequest
0 голосов
/ 26 сентября 2019

Есть дата хранения таблицы и баланс счета.Однако баланс недоступен на некоторые даты.Предполагая, что баланс не изменяется, когда дата недоступна.Как обновить информацию о балансе для всех дат?

Вот пример: Таблица D содержит все действительные даты.

2000-01-01
2000-01-02
2000-01-03
2000-01-04
2000-01-05
2000-01-06
2000-01-07
2000-01-08
2000-01-09

Таблица A содержит дату и остаток на счете.

2000-01-02  $100
2000-01-05  $200
2000-01-09  $700

В конечном счете, я хочу создать такую ​​таблицу:

2000-01-01  null
2000-01-02  $100
2000-01-03  $100
2000-01-04  $100
2000-01-05  $200
2000-01-06  $200
2000-01-07  $200
2000-01-08  $200
2000-01-09  $700

Я думал оследующее:

  • LEAD и LAG,
  • Рекурсивный CTE

Однако, похоже, что ни один из них не подходит для этого сценария.

Ответы [ 4 ]

0 голосов
/ 26 сентября 2019

SQL Server не поддерживает параметр IGNORE NULLS для LAG() или LAST_VALUE().На самом деле это самый простой метод.

Вместо этого вы можете использовать APPLY:

select d.*, a.balance
from dates d outer apply
     (select top (1) a.*
      from a
      where a.date <= d.date
      order by a.date desc
     ) a;

или эквивалент с использованием коррелированного подзапроса:

select d.*,
       (select top (1) a.*
        from a
        where a.date <= d.date
        order by a.date desc
        fetch first 1 row only
       )
from dates d;

Это будетработать как в MySQL, так и в SQL Server с предостережением, которое вам необходимо LIMIT в MySQL.

При этом, если у вас большой объем данных (что маловероятно при гранулярности «даты»), тодва шага оконных функций, вероятно, являются лучшим решением:

select ad.date,
       max(ad.balance) over (partition by grp) as balance
from (select d.date, a.balance,
             count(a.date) over (order by d.date) as grp
      from dates d left join
           a
           on d.date = a.date
     ) ad;

Подзапрос назначает «группу» каждому значению баланса и последующим датам.Эта «группа» затем используется для назначения баланса во внешнем запросе.

Эта версия будет работать как в MySQL, так и в SQL Server.

0 голосов
/ 26 сентября 2019

ANSI SQL.

Table_D
-------
dd(field name)
-------
2000-01-01
2000-01-02
2000-01-03
2000-01-04
2000-01-05
2000-01-06
2000-01-07
2000-01-08
2000-01-09


Table_A
-------
dd(field name) cost(field name)
-------
2000-01-02  $100
2000-01-05  $200
2000-01-09  $700


select a.dd
  , (
    case when a.cost is null then min(a.cost) OVER (partition by a.cost_group ORDER BY a.dd) else a.cost end
  ) as cost
from (
    select a.dd, b.cost
      , count(b.cost) over (order by a.dd) as cost_group
    from Table_D a
    left join Table_A b on (b.dd = a.dd)
) a  
0 голосов
/ 26 сентября 2019

Мы можем использовать оконную функцию count() over () для создания другой функции partitions, а затем min() over () для распространения минимального значения на этот раздел.

Сначала я создал временную таблицу переменных для хранения данных OP -

declare @xyz table (dt date, amount int)
insert into @xyz values
('2000-01-02','100'),
('2000-01-05','200'),
('2000-01-09','700');

Во-вторых, я получу максимальную дату из приведенной выше таблицы.

declare @maxDT date = (select cast(max(dt) as date) from @xyz);

Окончательно, сначала CTE - это recursive CTE для создания данных с 2000-01-01 до max dateхранить в переменной выше.Второй CTE предназначен для создания разделов.

;with cte as (
    select cast('2000-01-01' as date) as dt
    union all
    select dateadd(day,1,cte.dt) from cte where cte.dt < @maxDT
), cte2 as (
    select cte.dt, x.amount, x.dt as dt2, count(x.dt) over (order by cte.dt) as ranking
    from cte
    left join @xyz x on x.dt = cte.dt
)
select dt, min(amount) over (partition by ranking)
from cte2;
0 голосов
/ 26 сентября 2019

Один из способов добиться этого - использовать первое значение и создать некоторые функции ранжирования.Я использую SQL-сервер

with cte as 
(
select '2000-01-01' as Datenew union all 
select '2000-01-02' as Datenew union all 
select '2000-01-03' as Datenew union all 
select '2000-01-04' as Datenew union all 
select '2000-01-05' as Datenew union all 
select '2000-01-06' as Datenew union all 
select '2000-01-07' as Datenew union all 
select '2000-01-08' as Datenew union all 
select '2000-01-09' as Datenew ), cte2 as (
select '2000-01-02' as DateSal, '100' as Salary union all 
select '2000-01-05' as DateSal, '200' as Salary union all 
select '2000-01-09' as DateSal, '700' as Salary ) 


select datenew, Salary = FIRST_VALUE(salary) over (partition by ranking order by datenew) from (
select datenew, salary ,  
  sum(case  when DateSal  is not null then 1 end) over (order by datenew) ranking 
    from cte c 
left join cte2 c2  on  c.Datenew = c2.DateSal ) tst 

order by  datenew  

- Sum создает промежуточный итог для создания группировки, а первое значение гарантирует, что мы получим одинаковое значение для данной группы.

это вывод

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...