SQL - 使用递增日期

时间:2016-10-26 15:14:22

标签: sql sql-server tsql select

我有一个日期(MM/DD格式)的SQL表,目标和级别,如下:

Date     Target     Level 
10/2     1000       1
10/4     2000       1
10/7     2000       2

我想将这些日期用作层或检查点,以便何时使用相应的目标和级别。因此,在这些日期之前或之后(直到下一个日期)的任何内容都将使用该目标/级别。第一个日期之前的任何内容只使用第一个日期的值。

我想选择一系列日期(5周的日期范围,范围的开始日期和结束日期由当天确定:从今天开始的3周,从今天开始的2周)和相应地填写目标和水平:

Date     Target     Level
10/1     1000       1
10/2     1000       1
10/3     1000       1
10/4     2000       1
10/5     2000       1
10/6     2000       1
10/7     2000       2
10/8     2000       2
...
11/5     2000       2

我该怎么做:

  1. 选择日期范围(尽可能高效)

  2. 使用我的表格中的相应日期填写日期范围内的相应目标/级别?

  3. 谢谢。

4 个答案:

答案 0 :(得分:1)

您可以使用outer apply执行此操作。以下使用递归CTE创建日期列表:

with d as (
      select cast(getdate() as date) as dte
      union all
      select dateadd(day, -1, dte)
      from d
      where dte >= getdate() - 30
select d.dte, t.target, t.level
from d outer apply
     (select top 1 t.*
      from t
      where d.dte >= t.dte
      order by t.dte desc
     );

答案 1 :(得分:1)

您可以使用CTE生成“缺失”日期,然后使用CROSS APPLY获取上次活动的目标和级别(通过查询日期所在的TOP 1 DESC或当前日期之前) - 最后我将'最大日期'作为变量

引入
DECLARE @MAXD as DATETIME = '20161105';

WITH DATS AS (SELECT MIN([Date]) D FROM dbo.YourTab
            UNION ALL
            SELECT dateadd(day,1,D) FROM DATS WHERE D < @MAXD)
select DATS.D, CA.Target, CA.Level from DATS 
    CROSS APPLY
    (SELECT TOP 1 Y.Target, Y.Level FROM YourTab Y 
                                     WHERE 
                                          Y.[Date] <= DATS.D 
                                     ORDER BY Y.Date DESC) CA
    option (maxrecursion 0);

我做了一些更改日期,返回3,前进两周 - 我也切换到outer apply处理没有数据

DECLARE @MIND as DATETIME = dateadd(week,-3,cast(getdate() as date));
DECLARE @MAXD as DATETIME = dateadd(week, 5,@MIND);

WITH DATS AS (SELECT @MIND D 
            UNION ALL
            SELECT dateadd(day,1,D) FROM DATS WHERE D < @MAXD)
select DATS.D, CA.Target, CA.Level from DATS 
    OUTER APPLY
    (SELECT TOP 1 Y.Target, Y.Level FROM YourTab Y WHERE Y.[Date] <= DATS.D ORDER BY Y.Date DESC) CA
    ORDER BY DATS.D
    option (maxrecursion 0);

最终更改 - 如果日期没有更早的值 - 请采取第一个未来行

DECLARE @MIND as DATETIME = dateadd(week,-3,cast(getdate() as date));
DECLARE @MAXD as DATETIME = dateadd(week, 5,@MIND);

WITH DATS AS (SELECT @MIND D 
            UNION ALL
            SELECT dateadd(day,1,D) FROM DATS WHERE D < @MAXD)
select DATS.D, COALESCE(CA.Target, MQ.Target) Target , COALESCE(CA.Level, MQ.Level) Level from DATS 
    OUTER APPLY
    (SELECT TOP 1 Y.Target, Y.Level FROM YourTab Y WHERE Y.[Date] <= DATS.D ORDER BY Y.Date DESC) CA

    OUTER APPLY 
    (
        SELECT TOP 1 M.Target, M.Level FROM YourTab M ORDER BY M.[Date] ASC
    ) MQ
        ORDER BY DATS.D
    option (maxrecursion 0);

答案 2 :(得分:0)

我不知道为什么您将日期存储为MM/DD,但您需要转换为正确的数据类型。这可以做一个技巧:

;WITH YourTable AS (
SELECT *
FROM (VALUES
('10/2', 1000, 1),
('10/4', 2000, 1),
('10/7', 2000, 2)
) as t([Date], [Target],  [Level])
), dates_cte AS ( --this CTE is generating dates you need
    SELECT DATEADD(week,-3,GETDATE()) as d --3 weeks back
    UNION ALL
    SELECT dateadd(day,1,d)
    FROM dates_cte
    WHERE d < DATEADD(week,2,GETDATE()) --2 weeks forward
)

SELECT  REPLACE(CONVERT(nvarchar(5),d,101),'/0','/') as [Date],
        COALESCE(t.[Target],t1.[Target]) [Target],
        COALESCE(t.[Level],t1.[Level]) [Level]
FROM dates_cte dc
OUTER APPLY ( --Here we got PREVIOUS values
    SELECT TOP 1 *
    FROM YourTable
    WHERE CONVERT(datetime,REPLACE([Date],'/','/0')+'/2016',101) <= dc.d
    ORDER BY CONVERT(datetime,REPLACE([Date],'/','/0')+'/2016',101) DESC
    ) t
OUTER APPLY ( --Here we got NEXT values and use them if there is no PREV
    SELECT TOP 1 *
    FROM YourTable
    WHERE CONVERT(datetime,REPLACE([Date],'/','/0')+'/2016',101) >= dc.d
    ORDER BY CONVERT(datetime,REPLACE([Date],'/','/0')+'/2016',101) ASC
    ) t1

输出:

Date    Target  Level
10/5    2000    1
10/6    2000    1
10/7    2000    2
10/8    2000    2
10/9    2000    2
10/10   2000    2
10/11   2000    2
10/12   2000    2
...
11/9    2000    2

修改

使用Categories

;WITH YourTable AS (
SELECT *
FROM (VALUES
('10/2', 1000, 1, 'A'),
('10/4', 3000, 1, 'B'),
('10/7', 2000, 2, 'A')
) as t([Date], [Target], [Level], [Category])
), dates_cte AS (
    SELECT DATEADD(week,-3,GETDATE()) as d
    UNION ALL
    SELECT dateadd(day,1,d)
    FROM dates_cte
    WHERE d < DATEADD(week,2,GETDATE())
)

SELECT  REPLACE(CONVERT(nvarchar(5),d,101),'/0','/') as [Date],
        COALESCE(t.[Target],t1.[Target]) [Target],
        COALESCE(t.[Level],t1.[Level]) [Level],
        c.Category
FROM dates_cte dc
CROSS JOIN (
    SELECT DISTINCT Category
    FROM YourTable
) c
OUTER APPLY (
    SELECT TOP 1 *
    FROM YourTable
    WHERE CONVERT(datetime,REPLACE([Date],'/','/0')+'/2016',101) <= dc.d
        AND c.Category = Category
    ORDER BY CONVERT(datetime,REPLACE([Date],'/','/0')+'/2016',101) DESC
    ) t
OUTER APPLY (
    SELECT TOP 1 *
    FROM YourTable
    WHERE CONVERT(datetime,REPLACE([Date],'/','/0')+'/2016',101) >= dc.d
        AND c.Category = Category
    ORDER BY CONVERT(datetime,REPLACE([Date],'/','/0')+'/2016',101) ASC
    ) t1
ORDER BY c.Category, d

答案 3 :(得分:0)

我不确定我是否简化了这一点,但是:

select min(X.Date) Date_Range_Start, max(X.date) Date_Range_End
      , V.<value_date>
      , isnull(X.Target, 'Out of range') Target
      , isnull(X.Level, 'Out of range') Level
   from X --replace this with your table 
   left join <value_table> V --table with dates to be assessed
          on V.<Date> between X.Date_Range_Start and X.Date_Range_End
group by Target, Level, V.<value_date>