【问题标题】:Populating a list of dates without a defined end date - SQL server填充没有定义结束日期的日期列表 - SQL Server
【发布时间】:2016-02-08 08:32:00
【问题描述】:

我有一个帐户列表及其费用,每隔几天就会发生变化。 在此列表中,每次成本更新为新的时,我只有开始日期,但没有结束日期的列。 这意味着,我需要填充日期列表,当特定帐户和费用的结束日期应被推断为具有新费用的同一帐户的开始日期时。

差不多这样:
帐户开始日期费用
一个          2016 年 1 月 1 日  100 美元
两个           2016 年 1 月 1 日  150 美元
一个           2016 年 4 月 1 日  200 美元
两个           2016 年 3 月 1 日  200 美元

我需要的结果是:
帐户   日期   成本
一个       2016 年 1 月 1 日 100 美元
一个       2016 年 2 月 1 日 100 美元
一个       2016 年 3 月 1 日 100 美元
一个       2016 年 4 月 1 日 200 美元
两个       2016 年 1 月 1 日 150 美元
两个       2016 年 2 月 1 日 150 美元
两个       2016 年 3 月 1 日 200 美元

例如,如果成本在月中发生变化,那么样本数据将仅保存两条记录(每个账户-开始日期-成本的唯一组合各一条),而结果将保存 30 条记录每月每一天的费用(第一次费用为 15,第二次费用为 15)。成本是给定的,无需计算(手动插入)。

请注意,结果包含更多记录,因为示例数据仅显示该帐户截至该日期的开始日期和更新成本。结果显示了当月每一天的成本。

有什么想法吗?

【问题讨论】:

  • 这个优化怎么样?
  • 如果您知道如何在有开始和结束日期时进行填充,那么只需按您的帐户选择 max(date) 和 min(date) 组,您就会有开始和结束日期跨度>
  • @t-clausen.dk:我们应该将不存在的查询优化为有效的查询。
  • 问题也不是很清楚。请详细说明您的要求。解释规则,为什么您的结果包含比样本数据更多的记录?费用呢,如何计算?
  • @TimSchmelter 这个问题并不难理解。解决方案可能有点困难

标签: sql-server


【解决方案1】:

解答有点长。

我添加了一个额外的日期用于测试目的:

DECLARE @t table(account varchar(10), startdate date, cost int)

INSERT @t 
values
('one','1/1/2016',100),('two','1/1/2016',150),
('one','1/4/2016',200),('two','1/3/2016',200),
('two','1/6/2016',500) -- extra row

;WITH CTE as
( SELECT
    row_number() over (partition by account order by startdate) rn,
    *
  FROM @t
),N(N)AS 
(
  SELECT 1 FROM(VALUES(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))M(N)
),
tally(N) AS -- tally is limited to 1000 days
(
  SELECT ROW_NUMBER()OVER(ORDER BY N.N) - 1 FROM N,N a,N b
),GROUPED as
(
  SELECT
    cte.account, cte.startdate, cte.cost, cte2.cost cost2, cte2.startdate enddate
  FROM CTE
  JOIN CTE CTE2
  ON CTE.account = CTE2.account
  and CTE.rn = CTE2.rn - 1
)
-- used DISTINCT to avoid overlapping dates
SELECT DISTINCT
  CASE WHEN datediff(d, startdate,enddate) = N THEN cost2 ELSE cost END cost,
  dateadd(d, N, startdate) startdate,
  account
FROM grouped
JOIN tally
ON datediff(d, startdate,enddate) >= N

结果:

cost  startdate   account
100   2016-01-01  one
100   2016-01-02  one
100   2016-01-03  one
150   2016-01-01  two
150   2016-01-02  two
200   2016-01-03  two
200   2016-01-04  one
200   2016-01-04  two
200   2016-01-05  two
500   2016-01-06  two

【讨论】:

    【解决方案2】:

    谢谢@t-clausen.dk!

    它并没有完全解决问题,但确实以正确的方式指导了我。

    最终,我使用 LEAD 函数为每个帐户的每个成本生成了一个结束日期,然后我能够根据that idea 填充日期列表。

    我是这样生成结束日期的:

    DECLARE @t table(account varchar(10), startdate date, cost int)
    INSERT @t 
    values
    ('one','1/1/2016',100),('two','1/1/2016',150),
    ('one','1/4/2016',200),('two','1/3/2016',200),
    ('two','1/6/2016',500)
    
    select account 
          ,[startdate]
          ,DATEADD(DAY, -1, LEAD([Startdate], 1,'2100-01-01') OVER (PARTITION BY account ORDER BY [Startdate] ASC)) AS enddate 
          ,cost
    from @t
    

    它返回了预期的结果:

    帐户开始日期      结束日期      费用
    一个       2016-01-01    2016-01-03      100
    一个       2016-01-04    2099-12-31      200
    两个       2016-01-01    2016-01-02      150
    两个       2016-01-03    2016-01-05      200
    两个      2016-01-06    2099-12-31     500

    请注意,我将当前费用的结束日期设置为遥远将来的某个日期,这意味着(对我而言)它们目前处于活动状态。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-13
      • 1970-01-01
      • 1970-01-01
      • 2023-01-05
      相关资源
      最近更新 更多