我正在使用SQL Server2014。我有这样的行主数据:
EmpNo DeptName DateIn TimeIn
---------------------------------------
001 HR 2018-10-04 08:10:00
001 HR 2018-10-05 08:11:00
001 HR 2018-10-08 07:00:00
002 HR 2018-10-04 08:15:00
002 HR 2018-10-05 08:12:00
002 HR 2018-10-08 08:10:00
003 HR 2018-10-04 08:30:00
能否请您帮我找到一个最佳查询结果,如下所示:
EmpNo DeptName DateIn TimeIn 001 HR 2018-10-04 08:10:00 001 HR 2018-10-05 08:11:00 001 HR 2018-10-06 n/a 001 HR 2018-10-07 n/a 001 HR 2018-10-08 07:00:00 002 HR 2018-10-04 08:15:00 002 HR 2018-10-05 08:12:00 002 HR 2018-10-06 n/a 002 HR 2018-10-07 n/a 002 HR 2018-10-08 08:10:00 003 HR 2018-10-04 08:30:00 003 HR 2018-10-05 --sickleave-- 003 HR 2018-10-06 n/a 003 HR 2018-10-07 n/a
到目前为止,我的查询是
DECLARE @tbltgl TABLE (tgl DATE)
DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME
DECLARE @m VARCHAR(2)
DECLARE @y VARCHAR(4)
DECLARE @DeptName VARCHAR(100)
SET @m = '10';
SET @y = '2018';
SET @DeptName = 'HR'
SET @StartDate = @y + '-' + @m + '-01';
SET @EndDate = DATEADD(d, 30, @StartDate)
WHILE @StartDate <= @EndDate
BEGIN
INSERT INTO @tbltgl
SELECT @StartDate
SET @StartDate = DATEADD(dd, 1, @StartDate)
END
SELECT
b.BADGENUMBER AS NIK,
b.NAME,
b.DEPTNAME,
b.TITLE,
a.tgl AS 'DATE',
CONVERT(VARCHAR, b.StartDateTime, 108) AS 'TIME IN'
FROM
@tbltgl a
LEFT JOIN
(SELECT *
FROM VW_ATT_20181205b_DM b
WHERE b.DEPTNAME = @DeptName
AND MONTH(b.StartDateTime) = @m
AND YEAR(b.StartDateTime) = @y) b ON a.tgl = b.TGL
ORDER BY
b.BADGENUMBER,
CAST(a.TGL AS DATE)
答案 0 :(得分:0)
如果是我,我将使用/创建一对尺寸表;日期和员工(假设您希望员工完全没有数据)。此时,将开始日期和结束日期放入变量中。递归CTE也可以解决日期问题。这是CTE的一个示例。如果您有日期尺寸,只需删除CTE并替换为该尺寸即可。只需确保在联接中的日期维度上放置一个范围,否则您将获得很多垃圾数据。
DECLARE @StartDate DATE,
@EndDate DATE;
SET @StartDate = ...;
SET @EndDate = ...;
WITH DATES AS (
SELECT
@StartDate AS DateId
UNION ALL
SELECT
DATEADD(DD,1,DateId) AS DateId
FROM DATES
WHERE DATEADD(DD,1,DateId) <= @EndDate
)
SELECT
COALESCE(m.EmpNo,e.EmpNo) AS EmpNo,
COALESCE(m.DeptName,e.DeptName) AS DeptName,
COALESCE(m.DateIn,d.DateId) AS DateIn,
COALESCE(m.TimeIn,'n/a') AS TimeIn
FROM VW_ATT_20181205b_DM AS m
FULL JOIN DATES AS d -- DateId is UNIQUE in the recursive CTE
ON m.DateIn = d.DateId
FULL JOIN Employees AS e -- EmpNo is UNIQUE in this table assumption
ON m.EmpNo = e.EmpNo;
如果需要,您还可以从员工中提取数据。这是一个非常愚蠢的版本...显然,所以请原谅任何可能关闭的版本。可以将其视为更多可指导的伪代码。
希望这会有所帮助。
答案 1 :(得分:0)
使用日历表链接,这是一种处理方法。注意日历和“实体”列表之间的交叉连接(使用显式的“交叉连接”语法)。这样便可以为所有实体的所有日期生成行。目前尚不清楚您的源数据代表什么。是“输入”信息中部门名称的一部分,还是员工的属性。如果是后者,则可以将代码简化一些。如果您有一个雇员表,则不需要该CTE,您可以直接将日历与雇员表交叉加入。
还有另一条评论。没有人,但是您知道“ --sickleave--”来自何处。我忽略了这个
set nocount on;
use tempdb;
go
declare @src table (empno smallint, deptname varchar(5), datein date, timein time(0));
insert @src (empno, deptname, datein, timein)
values (1, 'HR', '2018-10-04', '08:10:00'),
(1, 'HR', '2018-10-05', '08:11:00'),
(1, 'HR', '2018-10-08', '07:00:00'),
(2, 'HR', '2018-10-04', '08:15:00'),
(2, 'HR', '2018-10-05', '08:12:00'),
(2, 'HR', '2018-10-08', '08:10:00'),
(3, 'HR', '2018-10-04', '08:30:00');
select * from @src;
declare @StartDate date;
declare @CutoffDate date;
set @StartDate = '20181101';
--alternatively, use the current month when script runs
--set @start = getdate();
--set @start = dateadd(day, 1 - day(@start), @start);
set @CutoffDate = dateadd(day, 1, EOMONTH(@StartDate));
select @StartDate, @CutoffDate;
-- To keep things simple, use your date range to match "desired output"
set @StartDate = '20181004';
set @CutoffDate = '20181008';
select @StartDate, @CutoffDate;
-- see what the cte calendar generates
-- from: https://www.mssqltips.com/sqlservertip/4054/creating-a-date-dimension-or-calendar-table-in-sql-server/
with calendar as (
SELECT d = DATEADD(DAY, rn - 1, @StartDate)
FROM
(
SELECT TOP (DATEDIFF(DAY, @StartDate, @CutoffDate))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
-- on my system this would support > 5 million days
ORDER BY s1.[object_id]
) AS x
)
select * from calendar order by d;
with calendar as (
SELECT d = DATEADD(DAY, rn - 1, @StartDate)
FROM
(
SELECT TOP (DATEDIFF(DAY, @StartDate, @CutoffDate))
rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
-- on my system this would support > 5 million days
ORDER BY s1.[object_id]
) AS x
),
emplist as (
select distinct empno, deptname from @src
)
select calendar.d, emplist.empno, emplist.deptname,
src.timein,
isnull(convert(char(8), src.timein, 108), 'n/a') as ftimein
from calendar cross join emplist
left join @src as src
on emplist.empno = src.empno and emplist.deptname = src.deptname
and calendar.d = src.datein
order by emplist.empno, emplist.deptname, calendar.d;
供以后参考,这种方法(包括完整的或至少完整的)是如何发布有效的sql问题。这为其他人提供了一个进行实验并建立有效解决方案的平台。最好包括约束(如果考虑性能,还应包括索引)-我不是因为那是您应该做的工作。我还将在格式化方面花费更多的精力-但这又是一个快速,简短的演示,而不是可用于生产的代码。
也请注意评论。当您从某处“借用”代码时,提供指向源代码的链接总是一个好主意。由于链接可能会过时,因此您可能需要保存网页的副本,以防万一。归因是好的。提供该链接将使其他人更好地了解您在做什么。
答案 2 :(得分:0)
在将一个月的所有日期都当作月份日期的情况下,尝试此操作。
--To get all the dates of a month
declare @month int, @year int
set @month = 10
set @year = 2018
SELECT * INTO #tblAllMonthDate FROM(
SELECT
Convert(Varchar(20),CAST(CAST(@year AS VARCHAR) + '-' + CAST(@Month AS VARCHAR) + '-01' AS DATETIME) + Number,110) as MonthDate
FROM master..spt_values
WHERE type = 'P'
AND
(CAST(CAST(@year AS VARCHAR) + '-' + CAST(@Month AS VARCHAR) + '-01' AS DATETIME) + Number )
<
DATEADD(mm,1,CAST(CAST(@year AS VARCHAR) + '-' + CAST(@Month AS VARCHAR) + '-01' AS DATETIME) )
)a
create table #temp (EmpNo varchar(20),
DeptName varchar(2),
DateIn varchar(20),
TimeIn varchar(20)
)
insert into #temp values ('001', 'HR', '10-04-2018', '08:10:00')
insert into #temp values ('001', 'HR', '10-05-2018', '08:11:00')
insert into #temp values ('001', 'HR', '10-08-2018', '07:00:00')
insert into #temp values ('002', 'HR', '10-04-2018', '08:15:00')
insert into #temp values ('002', 'HR', '10-05-2018', '08:12:00')
insert into #temp values ('002', 'HR', '10-08-2018', '08:10:00')
insert into #temp values ('003', 'HR', '10-04-2018', '08:30:00')
select distinct #temp.* from #temp
union
select distinct #temp.EmpNo as EmpNo,'HR' as DeptName, monthdate, 'na/a' from(
select #tblAllMonthDate.MonthDate, EmpNo,ISNULL(DeptName,'HR') as DeptName, isnull(DateIn,#tblAllMonthDate.MonthDate) as DateIn, Isnull(TimeIn,'n/a') TimeIn from #tblAllMonthDate
left join #temp on Convert(Varchar(20),Convert(Datetime,#temp.DateIn),112) = Convert(Varchar(20),Convert(Datetime,#tblAllMonthDate.MonthDate),112)
where DAY(MonthDate) <=8 and DAY(MonthDate) >= 4
)as a , #temp
where a.EmpNo is null
drop table #tblAllMonthDate
drop table #temp
此处#temp是原始表,其中包含出勤数据。首先获取原始表中的所有数据,然后使用并集获取缺少empno的所有日期。
程序的输出如下:
EmpNo DeptName DateIn TimeIn
001 HR 10-04-2018 08:10:00
001 HR 10-05-2018 08:11:00
001 HR 10-06-2018 na/a
001 HR 10-07-2018 na/a
001 HR 10-08-2018 07:00:00
002 HR 10-04-2018 08:15:00
002 HR 10-05-2018 08:12:00
002 HR 10-06-2018 na/a
002 HR 10-07-2018 na/a
002 HR 10-08-2018 08:10:00
003 HR 10-04-2018 08:30:00
003 HR 10-06-2018 na/a
003 HR 10-07-2018 na/a
您只需要加入请假表即可请病假。您可以通过现有表中的另一个联合获取此请假数据。
答案 3 :(得分:-2)
使用子查询尝试此查询以获取当前范围内的所有日期:
DECLARE @DeptName VARCHAR(100) = 'HR'
DECLARE @FromDate datetime = '20181001'
DECLARE @ToDate datetime= '20181030'
;WITH DateRange AS
(
SELECT TOP (DATEDIFF(DAY, cast(@FromDate as date), cast (@ToDate as date)) + 1)
ROW_NUMBER() OVER (ORDER BY [object_id]) as DateValue
FROM sys.all_objects
),
DateRangeTable as
(
SELECT CAST(DATEADD(DAY, DateRange.DateValue-1, @FromDate) as Date) as DateValue
FROM DateRange
)
SELECT
b.BADGENUMBER AS NIK,
b.NAME,
b.DEPTNAME,
b.TITLE,
a.DateValue AS 'DATE',
MIN(CASE WHEN a.DateValue = b.DEPTNAME AND b.StartDateTime IN NOT NULL THEN CONVERT(VARCHAR, b.StartDateTime, 108) ELSE 'n/a' END) AS 'TIME IN'
FROM DateRangeTable a,VW_ATT_20181205b_DM b
GROUP BY b.BADGENUMBER,
b.Name,
a.DateValue,
CONVERT(VARCHAR, b.StartDateTime, 108) ELSE 'n/a' END)
ORDER BY
b.BADGENUMBER,
DateValue