Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/76.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 查找两个日期之间缺少的日期_Sql_Sql Server - Fatal编程技术网

Sql 查找两个日期之间缺少的日期

Sql 查找两个日期之间缺少的日期,sql,sql-server,Sql,Sql Server,我正在尝试为员工获取考勤,我的存储过程正在运行并返回数据。但是我看不到这一天。如果没有打卡或打卡,则不会向员工显示当天。如何使用下面的存储过程返回一天甚至不打卡 DECLARE @startdate DATETIME, @enddate DATETIME; SET @startdate = '2018-07-21'; SET @enddate = '2018-08-21'; WITH calendardates AS ( SELECT date = @startd

我正在尝试为员工获取考勤,我的存储过程正在运行并返回数据。但是我看不到这一天。如果没有打卡或打卡,则不会向员工显示当天。如何使用下面的存储过程返回一天甚至不打卡

DECLARE @startdate DATETIME,
        @enddate DATETIME;

SET @startdate = '2018-07-21';
SET @enddate = '2018-08-21';

WITH calendardates AS 
(
     SELECT date = @startdate

     UNION ALL

     SELECT DATEADD(DAY, 1, date)
     FROM calendardates
     WHERE DATEADD(DAY, 1, date) = @enddate
)
SELECT       
    I.USERID,
    CONVERT(DATETIME, I.WORKDATE) WORKDATE,
    CONVERT(VARCHAR(15), CAST(I.CHECKTIME AS TIME), 100) AS INTIME, 
    CONVERT(VARCHAR(15), CAST(O.CHECKTIME AS TIME), 100) AS OUTTIME,  
    DATEDIFF(n, I.CHECKTIME, O.CHECKTIME) / 60.00 AS WORKHRS,
    DATENAME(dw, (SELECT CONVERT(VARCHAR(10), I.WORKDATE, 101))) AS DutyDay
FROM        
    vwInTime I 
LEFT JOIN 
    vwOutTime O ON I.UserID = O.UserID AND O.WorkDate = I.WorkDate
RIGHT JOIN 
    calendardates c ON I.WorkDate = c.date
WHERE     
    (I.WORKDATE BETWEEN @startdate AND @enddate) 
ORDER BY 
    UserID, WORKDATE 
例如,星期五在这里找不到的示例数据

84 2018-07-21 00:00:00.000 9:06AM 6:19PM 9.216666周六
84 2018-07-22 00:00:00.000 9:13上午6:22下午9.150000星期日
84 2018-07-23 00:00:00.000 9:02 AM 6:29 PM 9.450000星期一
84 2018-07-24 00:00:00.000 9:06AM 6:29PM 9.383333周二
84 2018-07-25 00:00:00.000 9:02上午6:55下午9.883333周三
84 2018-07-26 00:00:00.000 9:08AM 6:36 PM 9.466666周四
84 2018-07-28 00:00:00.000星期六下午1:06无效
84 2018-07-29 00:00:00.000 1:01 PM 10:00 PM 8.983333星期日
84 2018-07-30 00:00:00.000 1:08PM 10:06PM 8.966666星期一
84 2018-07-31 00:00:00.000 1:08PM 10:04PM 8.933333周二
84 2018-08-01 00:00:00.000 1:10PM 10:05PM 8.916666星期三
84 2018-08-02 00:00:00.000 1:12 PM 10:07 PM 8.916666周四

84 2018-08-04 00:00:00.000 9:07AM 6:25PM 9.300000周六
不确定,但试试这个。好吧,让我头疼。对于这种模式,从日历表开始,每天一行。然后在日期列左键联接表

我认为上面的错误是引用WHERE子句中的外部表,使外部联接成为内部联接

此外,如果您有一个calandar表,则不需要递归CTE。乙二醇

DECLARE @startdate DATETIME
,@enddate DATETIME;

SET @startdate = '2018-07-21';
SET @enddate = '2018-08-21';
WITH calendardates
AS ( 
SELECT  date
FROM Calendar
WHERE date between @startdate and  @enddate
)
SELECT       I.USERID,
             Convert(datetime,I.WORKDATE) WORKDATE,
             CONVERT(varchar(15),CAST(I.CHECKTIME AS TIME),100) As INTIME, 
             CONVERT(varchar(15),CAST(O.CHECKTIME AS TIME),100) As OUTTIME,  
             Datediff(n,I.CHECKTIME,O.CHECKTIME) / 60.00 AS WORKHRS,
             datename(dw,(SELECT CONVERT(VARCHAR(10), I.WORKDATE, 101))) As DutyDay
FROM calendardates c
LEFT JOIN vwInTime I 
  ON I.WorkDate = c.date
LEFT JOIN vwOutTime O
  ON I.UserID = O.UserID 
 AND O.WorkDate = I.WorkDate

ORDER BY UserID,WORKDATE 

我相信创建日历日期块的目的是生成从@startdate到@enddate(包括在内)的日期。如果是这样的话,你的日常生活不会这样做,是吗

DECLARE @startdate DATETIME,
        @enddate DATETIME;

SET @startdate = '20180721';
SET @enddate = '20180821';

WITH calendardates
AS (
SELECT TOP (DATEDIFF(d, @startdate, @enddate) + 1)
           DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY t1.object_id) - 1, @startdate) AS [date]
    FROM sys.all_columns t1
    INNER JOIN sys.all_columns t2
      ON t2.object_id = t1.object_id
)
SELECT       I.USERID,
             coalesce(Convert(datetime,I.WORKDATE), c.Date) as WORKDATE,
             CONVERT(varchar(15),CAST(I.CHECKTIME AS TIME),100) As INTIME, 
             CONVERT(varchar(15),CAST(O.CHECKTIME AS TIME),100) As OUTTIME,  
             Datediff(n,I.CHECKTIME,O.CHECKTIME) / 60.00 AS WORKHRS,
             datename(dw,coalesce(Convert(datetime,I.WORKDATE,101), c.Date)) As DutyDay
FROM        calendardates c 
LEFT JOIN  vwInTime I ON I.WorkDate = c.date 
LEFT JOIN vwOutTime O ON I.UserID = O.UserID AND 
                                                 O.WorkDate = I.WorkDate
Order By UserID,coalesce(Convert(datetime,I.WORKDATE), c.Date);
注意:您将如何处理空用户标识

编辑:以下是处理空用户标识的版本:

declare @workTable TABLE
    ([USERID] int, [WORKDATE] datetime, [INTIME] varchar(10), [OUTTIME] varchar(10), [WORKHRS] varchar(8), [DutyDay] varchar(9));

INSERT INTO @workTable
    ([USERID], [WORKDATE], [INTIME], [OUTTIME], [WORKHRS], [DutyDay])
VALUES
    (84, '2018-07-21', '9:06AM', '6:19PM', '9.216666', 'Saturday'),
    (84, '2018-07-22', '9:13AM', '6:22PM', '9.150000', 'Sunday'),
    (84, '2018-07-23', '9:02AM', '6:29PM', '9.450000', 'Monday'),
    (84, '2018-07-24', '9:06AM', '6:29PM', '9.383333', 'Tuesday'),
    (84, '2018-07-25', '9:02AM', '6:55PM', '9.883333', 'Wednesday'),
    (84, '2018-07-26', '9:08AM', '6:36PM', '9.466666', 'Thursday'),
    (84, '2018-07-28', '1:06PM', NULL, NULL, 'Saturday'),
    (84, '2018-07-29', '1:01PM', '10:00PM', '8.983333', 'Sunday'),
    (84, '2018-07-30', '1:08PM', '10:06PM', '8.966666', 'Monday'),
    (84, '2018-07-31', '1:08PM', '10:04PM', '8.933333', 'Tuesday'),
    (84, '2018-08-01', '1:10PM', '10:05PM', '8.916666', 'Wednesday'),
    (84, '2018-08-02', '1:12PM', '10:07PM', '8.916666', 'Thursday'),
    (84, '2018-08-04', '9:07AM', '6:25PM', '9.300000', 'Saturday');

DECLARE @startdate DATETIME,
        @enddate DATETIME;

SET @startdate = '20180721';
SET @enddate = '20180821';

WITH calendardates
AS (
SELECT TOP (DATEDIFF(d, @startdate, @enddate) + 1)
            DATEADD(DAY, ROW_NUMBER() OVER (ORDER BY t1.object_id) - 1, @startdate) AS [date]
    FROM sys.all_columns t1
    INNER JOIN sys.all_columns t2
        ON t2.object_id = t1.object_id
),
userDates AS 
(
  SELECT [date], USERID
  FROM calendardates
  CROSS JOIN (SELECT DISTINCT userid FROM @workTable) t 
)
    SELECT       c.USERID,
                 c.Date as WORKDATE,
                 INTIME, 
                 OUTTIME,  
                 WORKHRS,
                 datename(dw,c.Date) As DutyDay
    FROM        
                userdates c 
                LEFT JOIN @WorkTable wt
    ON wt.WorkDate = c.date AND wt.USERID = c.USERID
    Order By UserID,c.Date;

下面是一个问题,因为where子句不包含calendardates的条件。添加条件,它应该会工作

DECLARE @startdate DATETIME,
        @enddate DATETIME;

SET @startdate = '2018-07-21';
SET @enddate = '2018-08-21';

WITH calendardates AS 
(
     SELECT date = @startdate

     UNION ALL

     SELECT DATEADD(DAY, 1, date)
     FROM calendardates
     WHERE DATEADD(DAY, 1, date) = @enddate
)
SELECT       
    I.USERID,
    CONVERT(DATETIME, I.WORKDATE) WORKDATE,
    CONVERT(VARCHAR(15), CAST(I.CHECKTIME AS TIME), 100) AS INTIME, 
    CONVERT(VARCHAR(15), CAST(O.CHECKTIME AS TIME), 100) AS OUTTIME,  
    DATEDIFF(n, I.CHECKTIME, O.CHECKTIME) / 60.00 AS WORKHRS,
    DATENAME(dw, (SELECT CONVERT(VARCHAR(10), I.WORKDATE, 101))) AS DutyDay
FROM        
    vwInTime I 
LEFT JOIN 
    vwOutTime O ON I.UserID = O.UserID AND O.WorkDate = I.WorkDate
RIGHT JOIN 
    calendardates c ON I.WorkDate = c.date
WHERE     
    (I.WORKDATE BETWEEN @startdate AND @enddate) OR c.date is NULL
ORDER BY 
    UserID, WORKDATE 
你可以尝试的另一种方法是用vwIntime来改变这种情况

DECLARE @startdate DATETIME,
        @enddate DATETIME;

SET @startdate = '2018-07-21';
SET @enddate = '2018-08-21';

WITH calendardates AS 
(
     SELECT date = @startdate

     UNION ALL

     SELECT DATEADD(DAY, 1, date)
     FROM calendardates
     WHERE DATEADD(DAY, 1, date) = @enddate
)
SELECT       
    I.USERID,
    CONVERT(DATETIME, I.WORKDATE) WORKDATE,
    CONVERT(VARCHAR(15), CAST(I.CHECKTIME AS TIME), 100) AS INTIME, 
    CONVERT(VARCHAR(15), CAST(O.CHECKTIME AS TIME), 100) AS OUTTIME,  
    DATEDIFF(n, I.CHECKTIME, O.CHECKTIME) / 60.00 AS WORKHRS,
    DATENAME(dw, (SELECT CONVERT(VARCHAR(10), I.WORKDATE, 101))) AS DutyDay
FROM        
    (SELECT * FROM vwInTime
     WHERE (WORKDATE BETWEEN @startdate AND @enddate)
    ) I 
LEFT JOIN 
    vwOutTime O ON I.UserID = O.UserID AND O.WorkDate = I.WorkDate
RIGHT JOIN 
    calendardates c ON I.WorkDate = c.date
ORDER BY 
    UserID, WORKDATE 

你为什么不去做?日历日期未生成日期。如果生成的话,它会工作,没有正确的连接应该工作。我只是从来不使用它们。容易犯错误,比如在外桌上过滤。“应该行吗”?你是说可能不会?这将是微软的一个可怕的评论。“应该有效”的意思是,如果你的操作顺序正确,并且没有犯任何其他错误。请注意,问题似乎出现在WHERE子句中,其中所有没有打孔的行都被过滤掉。不,问题不在那里(这是问题的一部分),但主要问题是他根本没有生成日历日期。它返回的结果与我得到的结果相同。如果员工没有打卡,例如,emp101-他没有打卡2018-08-01,当我生成上述查询时,它将不会显示2018-08-01。只跳到第二天。我怎么能看到,即使是那天,对他来说,那也是休息日。在那一天,你有两个(三个)问题。1) 您暗示了一个过滤器,因此将缺少的日期从结果中删除2)您没有从CalendarDates中引入任何日期。我将为您编辑它,并将其转换为更易于管理的左连接(但忽略右连接不起作用,它们起作用)。@NAJEEB,您应该考虑如何处理空ID。(可能先进行笛卡尔连接?-如果数据不多)抱歉,我没有任何空用户ID。请检查我的第二次尝试。从那个剧本里什么也没有回来。我手动将工作日添加到表中。示例数据和期望的结果将非常有帮助。我只是将示例数据放在我现在得到的数据中。此示例仅返回我一天,这是所有用户ID的第一天“2018-07-21”。