我正在尝试使工作人员出勤,我的存储过程正在工作并返回数据。但我看不到休息日。如果没有打孔或打孔,则不会向工作人员显示该天。如何使用以下存储过程返回日期,甚至不进行输入或输出。
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 Saturday
84 2018-07-22 00:00:00.000 9:13AM 6:22PM 9.150000 Sunday
84 2018-07-23 00:00:00.000 9:02AM 6:29PM 9.450000 Monday
84 2018-07-24 00:00:00.000 9:06AM 6:29PM 9.383333 Tuesday
84 2018-07-25 00:00:00.000 9:02AM 6:55PM 9.883333 Wednesday
84 2018-07-26 00:00:00.000 9:08AM 6:36PM 9.466666 Thursday
84 2018-07-28 00:00:00.000 1:06PM NULL NULL Saturday
84 2018-07-29 00:00:00.000 1:01PM 10:00PM 8.983333 Sunday
84 2018-07-30 00:00:00.000 1:08PM 10:06PM 8.966666 Monday
84 2018-07-31 00:00:00.000 1:08PM 10:04PM 8.933333 Tuesday
84 2018-08-01 00:00:00.000 1:10PM 10:05PM 8.916666 Wednesday
84 2018-08-02 00:00:00.000 1:12PM 10:07PM 8.916666 Thursday
84 2018-08-04 00:00:00.000 9:07AM 6:25PM 9.300000 Saturday
谢谢。
其他尝试
DECLARE @calendar AS TABLE (
FullDate DATETIME NOT NULL)
declare @startdate datetime='2018-07-21',
@enddate datetime='2018-08-21'
WHILE @startdate <= @enddate
BEGIN
INSERT INTO @calendar
(FullDate)
Values(@startdate)
SET @startdate=DATEADD(DAY, 1, @startdate)
END
SELECT I.USERID,
Convert(datetime,I.WORKDATE) WORKDATE,
c.FullDate,
I.CHECKTIME INTIME,
O.CHECKTIME 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 Outer Join @calendar c On I.WorkDate=c.FullDate
WHERE (I.WORKDATE BETWEEN @startdate AND @enddate) Order By WORKDATE
vwInTime
SELECT USERID, CONVERT(VARCHAR(10), CHECKTIME, 112) AS WORKDATE, MIN(CHECKTIME) AS CHECKTIME, CHECKTYPE
FROM dbo.CHECKINOUT
WHERE (CHECKTYPE = 'I')
GROUP BY USERID, CONVERT(VARCHAR(10), CHECKTIME, 112), CHECKTYPE
nwOutTime
SELECT USERID, CONVERT(VARCHAR(10), CHECKTIME, 112) AS WORKDATE, MAX(CHECKTIME) AS CHECKTIME, CHECKTYPE
FROM dbo.CHECKINOUT
WHERE (CHECKTYPE = 'O')
GROUP BY USERID, CONVERT(VARCHAR(10), CHECKTIME, 112), CHECKTYPE
答案 0 :(得分:0)
不确定,但是尝试这个。正确的加入只会使我的头部受伤。对于这种模式,请从您的日历表开始,每天一行。然后在日期列上左联接表。
我认为您上面的错误是在WHERE子句中引用外部表,从而使外部联接成为内部联接。
如果您有calandar表,也不需要递归CTE。 EG
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
答案 1 :(得分:0)
我相信使用块创建日历日期的目的是生成从@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);
注意:使用NULL用户ID怎么办?
编辑:这也是处理NULL用户ID的版本:
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;
答案 2 :(得分:0)
问题来了,因为where子句不包含日历日期的条件。添加条件,它应该可以工作
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