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”。