显示所有日期范围的数据,包括缺少的日期

时间:2019-04-30 10:44:37

标签: sql sql-server tsql recursive-query

我在约会方面遇到了问题。我有一张表,其中提供了某位雇员的截止日期和截止日期。为了进行评估,我想显示月份的每个日期以及第二个sql表中的相应值。

SQL表:

EmpNr | datefrom   | dateto     | hours
 0815 | 01.01.2019 | 03.01.2019 | 15
 0815 | 05.01.2019 | 15.01.2019 | 15
 0815 | 20.01.2019 | 31.12.9999 | 40

给定的员工(0815)在01.01.-15.01中工作。 15小时和20.01.-31.01。 40小时

我希望得到以下结果:

0815 | 01.01.2019 | 15
0815 | 02.01.2019 | 15
0815 | 03.01.2019 | 15
0815 | 04.01.2019 | NULL
0815 | 05.01.2019 | 15
...
0815 | 15.01.2019 | 15
0815 | 16.01.2019 | NULL
0815 | 17.01.2019 | NULL
0815 | 18.01.2019 | NULL
0815 | 19.01.2019 | NULL
0815 | 20.01.2019 | 40
0815 | 21.01.2019 | 40
...
0815 | 31.01.2019 | 40

关于日期,我们有:

declare @year int = 2019, @month int = 1;
WITH numbers
as
(
    Select 1 as value
    UNion ALL
    Select value + 1 from numbers
    where value + 1 <= Day(EOMONTH(datefromparts(@year,@month,1)))
)
SELECT b.empnr, b.hours, datefromparts(@year,@month,numbers.value) Datum FROM numbers left outer join 
emptbl b  on b.empnr = '0815' and  (datefromparts(@year,@month,numbers.value) >=  b.datefrom and datefromparts(@year,@month,numbers.value) <= case b.dateto )

效果很好,但我有一个奇怪的问题,该代码仅适用于2019年1月1日至2019年3月1日之间的日期

非常感谢您!

4 个答案:

答案 0 :(得分:1)

递归CTE必须以MIN(datefrom)MAX(dateto)开头:

DECLARE @t TABLE (empnr INT, datefrom DATE, dateto DATE, hours INT);

INSERT INTO @t VALUES
(815, '2019-01-01', '2019-01-03', 15),
(815, '2019-01-05', '2019-01-15', 15),
(815, '2019-01-20', '9999-01-01', 40),
-- another employee
(999, '2018-01-01', '2018-01-31', 15),
(999, '2018-03-01', '2018-03-31', 15),
(999, '2018-12-01', '9999-01-01', 40);

WITH rcte AS (
    SELECT empnr
         , MIN(datefrom) AS refdate
         , ISNULL(NULLIF(MAX(dateto), '9999-01-01'), CURRENT_TIMESTAMP) AS maxdate -- clamp year 9999 to today
    FROM @t
    GROUP BY empnr

    UNION ALL

    SELECT empnr
         , DATEADD(DAY, 1, refdate)
         , maxdate
    FROM rcte
    WHERE refdate < maxdate
)
SELECT rcte.empnr
     , rcte.refdate
     , t.hours
FROM rcte
LEFT JOIN @t AS t ON rcte.empnr = t.empnr AND rcte.refdate BETWEEN t.datefrom AND t.dateto
ORDER BY rcte.empnr, rcte.refdate
OPTION (MAXRECURSION 1000) -- approx 3 years

Demo on db<>fiddle

答案 1 :(得分:1)

您是否检查datefrom和dateto是否在正确的范围内?

DateTime字段的最小值为1753-01-01,最大值为9999-12-31。

查看源表以检查初始值。

答案 2 :(得分:0)

可以选择,试试:

SELECT b.empnr, b.hours, datefromparts(@year,@month,numbers.value) Datum
FROM numbers 
LEFT OUTER JOIN emptbl b ON b.empnr = '0815' AND 
datefromparts(@year,@month,numbers.value) BETWEEN b.datefrom AND b.dateto 

答案 3 :(得分:0)

您的CTE仅产生31个数字,因此仅显示一月的日期。

declare @year int = 2019, @month int = 1;

WITH numbers
as
(
    Select 1 as value
    UNion ALL
    Select value + 1 from numbers
    where value + 1 <= Day(EOMONTH(datefromparts(@year,@month,1)))
)
SELECT *
FROM numbers

https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=a24e58ef4ce522d3ec914f90907a0a9e

您可以尝试以下代码,

with t0 (i)    as (select 0  union all  select 0  union all  select 0),
     t1 (i)    as (select a.i from t0 a ,t0 b ),
     t2 (i)    as (select a.i from t1 a ,t1 b ),
     t3 (srno) as (select row_number()over(order by a.i) from t2 a ,t2 b ),
     tbldt(dt) as (select dateadd(day,t3.srno-1,'01/01/2019') from t3)   
select  tbldt.dt 
from tbldt
where tbldt.dt <= b.dateto -- put your condition here

https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=b16469908b323b8d1b98d77dd09bab3d