SQL 用循环填充下一个日期(月份)
SQL fill next date (month) with loop
我输入了 table,需要添加缺失的日期,但不是最大值,而是下一个可用的月份。
所以我需要使用循环。
SET @mindate = '2021.01'
SET @maxdate = CAST( GETDATE() AS Date ) --date today as max date
while
begin
if @mindate => @maxdate
begin
break
end
set @mindate = @mindate + 1
end
然后我可以得到 1+.. 但它不会停止到 7 个月
所以我完全陷入了写作循环。
数据table:
有人可以帮忙写代码吗?因为大多数示例都与连接、数据 table 或一个最大值有关。
Paul,我假设你忘记在模拟数据中指定月份。
我希望下面的代码可以帮助您了解您要完成的任务有多么重要:-)感谢您摆脱循环的意愿。
为了让它变得更好,我建议进行反规范化(注意!):
- 创建另一列
price_valid_until
- 最新的价格记录会有
price_valid_until = '21000101'
(又名,遥远的未来)
- 注册新价格时,将之前的价格更新为
new price_valid_from - 1 day
这是解决方案,使用非常复杂但高效的查询 (http://sqlfiddle.com/#!18/4ab23/4)
create table price_history(
SKU varchar(255),
price_valid_from date,
price decimal(16, 2)
)
insert into price_history
values
('a', '20210101', 10),
('a', '20210107', 12),
('b', '20210102', 4),
('b', '20210110', 2),
('b', '20210214', 5);
-- This fiddler won't let me initialize and reference:
--
-- declare
-- @from_date date,
-- @to_date date;
--
-- select
-- @from_date = min(date_from),
-- @to_date = max(date_from)
-- from price_history
with
date_range as(
select
min(price_valid_from) as from_date,
--
eomonth(
max(price_valid_from)
) as to_date
from price_history
),
--
all_dates as(
select from_date as date_in_range
from date_range
-- ----------
union all
-- ----------
select dateadd(day, 1, date_in_range)
from all_dates
where
date_in_range < (
select to_date
from date_range
)
),
--
price_history_boundaries as(
select
ph.SKU,
ph.price,
--
ph.price_valid_from,
-- The latest price, so far, is valid until 01/01/2100
coalesce(
dateadd(
day,
-1,
min(ph_next.price_valid_from)
),
'21000101'
) as price_valid_until
from
price_history ph
left outer join price_history ph_next
on(
ph_next.SKU = ph.SKU
and ph_next.price_valid_from > ph.price_valid_from
)
group by ph.SKU, ph.price_valid_from, ph.price
)
select
phb.SKU,
ad.date_in_range,
phb.price
from
all_dates ad
inner join price_history_boundaries phb
on(
phb.price_valid_from <= ad.date_in_range
and phb.price_valid_until >= ad.date_in_range
)
order by phb.SKU, ad.date_in_range
您可以通过创建加入的日期列表轻松实现您想要的结果。在这里,我使用递归 CTE 创建日期范围,每次迭代增加 1 个月到当前日期。
连接到您的源数据是一件简单的事情,这里 lead()
可以方便地限制连接的行。此外,假设使用 SQL Server Getdate:
declare @start date=(select Min([date]) from sourcetable);
with m as (
select 1 num, @start [Date]
union all
select num+1 , DateAdd(month,1,m.[date])
from m
where DateAdd(month,1,m.[date]) <= GetDate()
), t as (
select *, Lead([date],1,GetDate()) over (order by [date]) NextDate
from sourcetable
)
select m.[Date], t.sku, t.price
from m
join t on m.[date] >= t.[date] and m.[date] < t.nextdate
我输入了 table,需要添加缺失的日期,但不是最大值,而是下一个可用的月份。 所以我需要使用循环。
SET @mindate = '2021.01'
SET @maxdate = CAST( GETDATE() AS Date ) --date today as max date
while
begin
if @mindate => @maxdate
begin
break
end
set @mindate = @mindate + 1
end
然后我可以得到 1+.. 但它不会停止到 7 个月
所以我完全陷入了写作循环。
数据table:
有人可以帮忙写代码吗?因为大多数示例都与连接、数据 table 或一个最大值有关。
Paul,我假设你忘记在模拟数据中指定月份。
我希望下面的代码可以帮助您了解您要完成的任务有多么重要:-)感谢您摆脱循环的意愿。
为了让它变得更好,我建议进行反规范化(注意!):
- 创建另一列
price_valid_until
- 最新的价格记录会有
price_valid_until = '21000101'
(又名,遥远的未来) - 注册新价格时,将之前的价格更新为
new price_valid_from - 1 day
这是解决方案,使用非常复杂但高效的查询 (http://sqlfiddle.com/#!18/4ab23/4)
create table price_history(
SKU varchar(255),
price_valid_from date,
price decimal(16, 2)
)
insert into price_history
values
('a', '20210101', 10),
('a', '20210107', 12),
('b', '20210102', 4),
('b', '20210110', 2),
('b', '20210214', 5);
-- This fiddler won't let me initialize and reference:
--
-- declare
-- @from_date date,
-- @to_date date;
--
-- select
-- @from_date = min(date_from),
-- @to_date = max(date_from)
-- from price_history
with
date_range as(
select
min(price_valid_from) as from_date,
--
eomonth(
max(price_valid_from)
) as to_date
from price_history
),
--
all_dates as(
select from_date as date_in_range
from date_range
-- ----------
union all
-- ----------
select dateadd(day, 1, date_in_range)
from all_dates
where
date_in_range < (
select to_date
from date_range
)
),
--
price_history_boundaries as(
select
ph.SKU,
ph.price,
--
ph.price_valid_from,
-- The latest price, so far, is valid until 01/01/2100
coalesce(
dateadd(
day,
-1,
min(ph_next.price_valid_from)
),
'21000101'
) as price_valid_until
from
price_history ph
left outer join price_history ph_next
on(
ph_next.SKU = ph.SKU
and ph_next.price_valid_from > ph.price_valid_from
)
group by ph.SKU, ph.price_valid_from, ph.price
)
select
phb.SKU,
ad.date_in_range,
phb.price
from
all_dates ad
inner join price_history_boundaries phb
on(
phb.price_valid_from <= ad.date_in_range
and phb.price_valid_until >= ad.date_in_range
)
order by phb.SKU, ad.date_in_range
您可以通过创建加入的日期列表轻松实现您想要的结果。在这里,我使用递归 CTE 创建日期范围,每次迭代增加 1 个月到当前日期。
连接到您的源数据是一件简单的事情,这里 lead()
可以方便地限制连接的行。此外,假设使用 SQL Server Getdate:
declare @start date=(select Min([date]) from sourcetable);
with m as (
select 1 num, @start [Date]
union all
select num+1 , DateAdd(month,1,m.[date])
from m
where DateAdd(month,1,m.[date]) <= GetDate()
), t as (
select *, Lead([date],1,GetDate()) over (order by [date]) NextDate
from sourcetable
)
select m.[Date], t.sku, t.price
from m
join t on m.[date] >= t.[date] and m.[date] < t.nextdate