我有一张表,其中包含用户休假日的记录。 一个样本是:
+---------+-----------+---------+------------+ | country | user_name | user_id | vac_date | +---------+-----------+---------+------------+ | canada | James | 1111 | 2015-02-13 | | canada | James | 1111 | 2015-02-17 | | canada | James | 1111 | 2015-02-18 | | canada | James | 1111 | 2015-02-10 | | canada | James | 1111 | 2015-02-11 | +---------+-----------+---------+------------+
根据以上数据,从第13天到第18天,计数将是3,因为第14和第15是周末,第16是加拿大的假期。基本上,如果用户下一个工作日休息,我试图保持并继续计算。我还有一张桌子,里面有所有假期,包括国家和假日日期。假期表的样本数据为:
+---------+-------------+-------------+ | country | holidayDesc | holidayDate | +---------+-------------+-------------+ | canada | Family Day | 2015-02-16 | +---------+-------------+-------------+
目前我在SQL中有一个通常计算日期的查询,因此它只计算假期表中的任何内容。例如:如果用户参加2015年3月3日,2015年4月4日,2015年3月5日关闭,那么它将计数为3,但对于上表示例,它将仅为第13和第2的计数为1从17日至18日。
SELECT DISTINCT user_name ,min(vac_date) as startDate ,max(vac_date) as endDate ,datediff(day, min(vac_date), max(vac_date)) as consecutiveCount FROM ( SELECT user_name ,vac_date ,user_id ,groupDate = DATEADD(DAY, - ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY vac_date ), vac_date) FROM mytable WHERE country = 'canada' AND vac_date BETWEEN '20150101' AND '20151231' ) z GROUP BY user_name ,groupDate HAVING datediff(day, min(vac_date), max(vac_date)) >= 0 ORDER BY user_name ,min(vac_date);
这是它目前从上述样本数据输出的内容:
+-----------+------------+------------+------------------+ | user_name | startDate | endDate | consecutiveCount | +-----------+------------+------------+------------------+ | James | 2015-02-10 | 2015-02-11 | 2 | | James | 2015-02-13 | 2015-02-13 | 1 | | James | 2015-02-17 | 2015-02-18 | 2 | +-----------+------------+------------+------------------+
理想情况下,我希望它是:
+-----------+------------+------------+------------------+ | user_name | startDate | endDate | consecutiveCount | +-----------+------------+------------+------------------+ | James | 2015-02-10 | 2015-02-11 | 2 | | James | 2015-02-13 | 2015-02-18 | 3 | +-----------+------------+------------+------------------+
但我不知道纯SQL是否可行。我也可以尝试将其合并到C#中。
如果有帮助我也使用C#和SQL Server Management Studio。任何帮助,将不胜感激。提前致谢
答案 0 :(得分:1)
这看起来像是经典的Gaps&岛屿有点扭曲。
Declare @YourTable table (country varchar(25),user_name varchar(25),user_id varchar(25),vac_date date)
Insert Into @YourTable values
('canada','James','1111','2015-02-13'),
('canada','James','1111','2015-02-17'),
('canada','James','1111','2015-02-18'),
('canada','James','1111','2015-02-10'),
('canada','James','1111','2015-02-11')
Declare @Holiday table (country varchar(25),holidayDate date)
Insert Into @Holiday values
('canada','2015-02-16')
Select user_name
,startDate = min(vac_date)
,endDate = max(vac_date)
,consecutiveCount = sum(DayCnt)
From (
Select *
,Grp = Day(vac_date) - Row_Number() over (Partition By country,user_id Order by vac_date)
From (Select Country,user_name,user_id,vac_date,DayCnt=1 from @YourTable
Union All
Select A.Country,user_name,user_id,vac_date=b.holidayDate,DayCnt=1
From @YourTable A
Join @Holiday B on A.country=B.country and abs(DateDiff(DD,vac_date,holidayDate))=1
Union All
Select A.Country,user_name,user_id,vac_date=b.retval,DayCnt=0
From @YourTable A
Join (
Select * From [dbo].[udf-Range-Date]('2015-01-01','2017-12-31','DD',1) where DateName(WEEKDAY,RetVal) in ('Saturday','Sunday')
) B on abs(DateDiff(DD,vac_date,RetVal))=1
) S
) A
Group By user_name,Grp
Having Sum(DayCnt)>1
返回
user_name startDate endDate consecutiveCount
James 2015-02-10 2015-02-11 2
James 2015-02-16 2015-02-18 3
生成动态日期范围的UDF - 可以是您自己的查询
CREATE FUNCTION [dbo].[udf-Range-Date] (@R1 datetime,@R2 datetime,@Part varchar(10),@Incr int)
Returns Table
Return (
with cte0(M) As (Select 1+Case @Part When 'YY' then DateDiff(YY,@R1,@R2)/@Incr When 'QQ' then DateDiff(QQ,@R1,@R2)/@Incr When 'MM' then DateDiff(MM,@R1,@R2)/@Incr When 'WK' then DateDiff(WK,@R1,@R2)/@Incr When 'DD' then DateDiff(DD,@R1,@R2)/@Incr When 'HH' then DateDiff(HH,@R1,@R2)/@Incr When 'MI' then DateDiff(MI,@R1,@R2)/@Incr When 'SS' then DateDiff(SS,@R1,@R2)/@Incr End),
cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a, cte1 b, cte1 c, cte1 d, cte1 e, cte1 f, cte1 g, cte1 h ),
cte3(N,D) As (Select 0,@R1 Union All Select N,Case @Part When 'YY' then DateAdd(YY, N*@Incr, @R1) When 'QQ' then DateAdd(QQ, N*@Incr, @R1) When 'MM' then DateAdd(MM, N*@Incr, @R1) When 'WK' then DateAdd(WK, N*@Incr, @R1) When 'DD' then DateAdd(DD, N*@Incr, @R1) When 'HH' then DateAdd(HH, N*@Incr, @R1) When 'MI' then DateAdd(MI, N*@Incr, @R1) When 'SS' then DateAdd(SS, N*@Incr, @R1) End From cte2 )
Select RetSeq = N+1
,RetVal = D
From cte3,cte0
Where D<=@R2
)
/*
Max 100 million observations -- Date Parts YY QQ MM WK DD HH MI SS
Syntax:
Select * from [dbo].[udf-Range-Date]('2016-10-01','2020-10-01','YY',1)
Select * from [dbo].[udf-Range-Date]('2016-01-01','2017-01-01','MM',1)
*/
答案 1 :(得分:1)
我尝试采用不同的路线,但随后找到了John Cappelletti解决方案的修复程序。
首先,您需要在holiday
表格中添加周末日期。
Get a list of dates between two dates using a function
然后假期UNION ALL
休假,但添加说明字段,以便您可以区分两者。
有一些CROSS JOIN
因此您可以为每个国家/地区和用户(need testing)
SELECT [country],
[user_name], [user_id], [vac_date], 'vacation' as description
FROM vacations
UNION ALL
SELECT c.[country],
u.[user_name],
u.[user_id],
[holidayDate],
'holiday' as description
FROM holidays
CROSS JOIN (SELECT DISTINCT [country] FROM vacations) c
CROSS JOIN (SELECT DISTINCT [user_name], [user_id] FROM vacations) u
然后最终查询与John建议的相同,但这次你只计算休假日。
WITH joinDates as (
SELECT [country],
[user_name], [user_id], [vac_date], 'vacation' as description
FROM vacations
UNION ALL
SELECT c.[country],
u.[user_name],
u.[user_id],
[holidayDate],
'holiday' as description
FROM holidays
CROSS JOIN (SELECT DISTINCT [country] FROM vacations) c
CROSS JOIN (SELECT DISTINCT [user_name], [user_id] FROM vacations) u
)
Select user_name
,startDate = min(vac_date)
,endDate = max(vac_date)
,consecutiveCount = count(*)
From (
Select *
,Grp = Day(vac_date) - Row_Number() over (Partition By country,user_id
Order by vac_date)
From joinDates S
) A
WHERE description = 'vacation' -- only count vacation days ignore holiday/weekend
Group By user_name, Grp
Having count(*)>1
ORDER BY startDate
<强> SQL DEMO 强>
<强>输出强>
RAW OUTPUT
在这里,您可以通过
查看分组前的数据答案 2 :(得分:0)
好的,我对这个问题的理解是,你想要做的只是一天的假日。许多企业将此称为“缺席”和#34;按原因区分缺勤。在这种情况下,您尝试将假期视为假期的延续(出于时间目的),如果假期发生在星期五,但该人在周一休假,那应该是一个连续的超时时间。
就个人而言,我在C#中这样做是因为DateTime
对象的属性可以使这比使用frankenquery容易得多。下面的代码假定您有一个名为Employee的对象,它包含自己的DateTimes
记录,如下所示:
public class Employee
{
public int ID {get;set;}
public string Name {get;set;}
public List<DateTime> DaysIWasOut {get;set;}
}
public static int TimeOut(IEnumerable employees)
{
int totalOutInstances = 0;
DataTable dt = HolidaysPlease(); //this refers to another method
//to fill the table. Just a basic SQLAdapter.Fill kind of thing.
//Basic so I won't waste time on it here.
foreach(var e in employees)
{
var holidays = dt.AsEnumerable().Where(t => Convert.ToDateTime(t[3]) == d) //holidays now has all of the holidays the employee had off.
totalOutInstances = e.DaysIWasOut.Count();
foreach(var d in e.DaysIWasOut)
{
int daystolook = 0;
if (d.DayOfWeek == DayOfWeek.Friday)
daystolook +=3;
else
daystolook +=1;
if(e.DaysIWasOut.Contains(d.AddDays(daystolook))
{totalOutInstances --; } //don't count that day
}
}
return totalOutInstances;
}