SQL Server:为员工创建复杂的日历视图
我被要求将大型Excel日历转换为SQL表结构,以便我们可以在内部应用程序中包含它的功能。为了便于解释,我举了一个虚拟示例: 这仅仅是一个星期,但它在一年中的所有星期里一直持续到最右边。你可以明白为什么这是大量的体力劳动。 这个日历告诉我们:SQL Server:为员工创建复杂的日历视图,sql,sql-server,pivot,common-table-expression,Sql,Sql Server,Pivot,Common Table Expression,我被要求将大型Excel日历转换为SQL表结构,以便我们可以在内部应用程序中包含它的功能。为了便于解释,我举了一个虚拟示例: 这仅仅是一个星期,但它在一年中的所有星期里一直持续到最右边。你可以明白为什么这是大量的体力劳动。 这个日历告诉我们: 由于1月1日的国定假日,大家都休息了 每个人每天都在办公室工作 詹姆斯·亚当斯不在办公室,在客户A咨询 我们使用颜色和代码,使它更容易和更快地解释。 我已经制作了一个结构,以在SQL Server中镜像这一点: 计划日 计划日是最重要的一天。 它代表着
计划日
是最重要的一天。
它代表着一些特殊的东西:比如假期或咨询任务。
它链接到一个员工
,有一个日期值,用布尔值表示AM或PM(AM为true或false)。PlanningDay
始终链接到一个PlanningDay详细信息
重要的是要知道,当在表PlanningDay
中找不到任何条目时,应显示默认的(办公室),周末除外)。仅注册“例外情况”
计划日详细信息
包含有关常规PlanningDayType
(如缺席)的信息,以及更具体的名称和缩写,如Holiday和HOL
计划日类型
包含可供使用的常规类型及其相应的颜色。所有缺席类型都标记为紫色,例如,无论是度假还是生病
我们的目标是将此结构纳入一份漂亮的SSRS报告中。报告将具有开始日期和结束日期的过滤器(因此必须是动态的)和员工过滤器(便于搜索)
我想创建一个存储过程,然后报告可以使用它,但我一直无法返回这个概述。我想为每个员工返回1行,2个列(AM和PM),其中包含链接到计划日
(如果为该日期AM/PM注册)的详细信息的显示名
,每个日期介于开始日期和结束日期参数之间。基本上,与Excel完全相同
我一直在考虑将用于呈现所有日期的常用表表达式与数据透视相结合,以使数据显示为列,但还没有真正变得明智。我希望你能帮助我
编辑:
表格脚本
CREATE TABLE [Mrd].[Employee]
(
[Id] INT IDENTITY(1,1) CONSTRAINT [PK_Mrd_Employee_Id] PRIMARY KEY,
[FirstName] NVARCHAR(100) NOT NULL,
[LastName] NVARCHAR(100) NULL,
)
CREATE TABLE [Mrd].[PlanningDay]
(
[Id] INT IDENTITY(1,1) CONSTRAINT [PK_Mrd_PlanningDay_Id] PRIMARY KEY,
[EmployeeId] INT NOT NULL,
[Date] DATE NOT NULL,
[AM] BIT NOT NULL,
[PlanningDayDealId] INT NOT NULL,
CONSTRAINT [FK_Mrd_PlanningDay_PlanningDayDealId_Mrd_PlanningDayDetail_Id]
FOREIGN KEY ([PlanningDayDealId])
REFERENCES [Mrd].[PlanningDayDetail] ([Id]),
CONSTRAINT [FK_Mrd_PlanningDay_EmployeeId_Mrd_Employee_Id]
FOREIGN KEY ([EmployeeId])
REFERENCES [Mrd].[Employee] ([Id])
)
CREATE TABLE [Mrd].[PlanningDayDetail]
(
[Id] INT IDENTITY(1,1) CONSTRAINT [PK_Mrd_PlanningDayDetail_Id] PRIMARY KEY,
[PlanningDayTypeId] INT NOT NULL,
[Name] NVARCHAR(255) NOT NULL,
[DisplayName] NVARCHAR(3) NOT NULL,
CONSTRAINT [FK_Mrd_PlanningDayDetail_PlanningDayTypeId_Mrd_PlanningDayType_Id]
FOREIGN KEY ([PlanningDayTypeId])
REFERENCES [Mrd].[PlanningDayType] ([Id])
)
CREATE TABLE [Mrd].[PlanningDayType]
(
[Id] INT IDENTITY(1,1) CONSTRAINT [PK_Mrd_PlanningDayType_Id] PRIMARY KEY,
[Name] NVARCHAR(255) NOT NULL,
[ReportColor] NVARCHAR(10) NOT NULL
)
虚拟数据
INSERT INTO [Mrd].[Employee] ([FirstName],[LastName])
VALUES
('Sandra','Cooper'),
('James','Adams'),
('Martha','Reid');
INSERT INTO [Mrd].[PlanningDayType] ([Name],[ReportColor])
VALUES
('Absent','#8b4789'),
('Consultancy','#c7c700');
INSERT INTO [Mrd].[PlanningDayDetail] ([PlanningDayTypeId],[Name],[DisplayName])
VALUES
(1,'New Year','HOL'),
(2,'Customer A','C-A');
INSERT INTO [Mrd].[PlanningDay] ([EmployeeId],[Date],[AM],[PlanningDayDealId])
VALUES
(1,'2018-01-01',1,1),
(1,'2018-01-01',0,1),
(2,'2018-01-01',1,1),
(2,'2018-01-01',0,1),
(3,'2018-01-01',1,1),
(3,'2018-01-01',0,1),
(2,'2018-01-03',1,2),
(2,'2018-01-03',0,2),
(2,'2018-01-04',1,2),
(2,'2018-01-04',0,2),
(2,'2018-01-05',1,2);
预期结果(如果可能的话)
我尝试过的
在开始编写报告之前,我试图创建一个查询/存储过程,每个员工每天返回2条记录,一条记录显示AM,另一条记录显示一名员工当天的PM部分
我最近的一次尝试包括创建一系列日期的CTE:
DECLARE @StartDate DATE = '2018-01-01';
DECLARE @EndDate DATE = '2018-01-31';
WITH AllDays AS
(SELECT @StartDate AS [Day]
UNION ALL
SELECT DATEADD(DAY, 1, [Day])
FROM AllDays
WHERE [Day] < @EndDate)
SELECT ad.[Day] AS 'Date', pd.[AM], pdd.[DisplayName], e.[FirstName] + ' ' + e.[LastName] AS 'Employee'
FROM AllDays ad
LEFT JOIN [Mrd].[PlanningDay] pd
ON ad.[Day] = pd.[Date]
LEFT JOIN [Mrd].[PlanningDayDetail] pdd
ON pd.[PlanningDayDealId] = pdd.[Id]
LEFT JOIN [Mrd].[PlanningDayType] pdt
ON pdd.[PlanningDayTypeId] = pdt.[Id]
LEFT JOIN [Mrd].[Employee] e
ON pd.[EmployeeId] = e.[Id]
OPTION (MAXRECURSION 0);
这给了我1月1日的正确结果,因为这是一个假期,每个员工都有计划日
。我首先要实现的目标是(很抱歉列出了这么多):
要求的结果
+------------+------+-------------+---------------+
| Date | AM | DisplayName | Employee |
+------------+------+-------------+---------------+
| 2018-01-01 | 1 | HOL | Sandra Cooper |
| 2018-01-01 | 0 | HOL | Sandra Cooper |
| 2018-01-01 | 1 | HOL | James Adams |
| 2018-01-01 | 0 | HOL | James Adams |
| 2018-01-01 | 1 | HOL | Martha Reid |
| 2018-01-01 | 0 | HOL | Martha Reid |
| 2018-01-02 | NULL | NULL | NULL |
| 2018-01-03 | 1 | C-A | James Adams |
| 2018-01-03 | 0 | C-A | James Adams |
| 2018-01-04 | 1 | C-A | James Adams |
| 2018-01-04 | 0 | C-A | James Adams |
| 2018-01-05 | 1 | C-A | James Adams |
| 2018-01-06 | NULL | NULL | NULL |
| 2018-01-07 | NULL | NULL | NULL |
+------------+------+-------------+---------------+
+------------+----+-------------+---------------+
| Date | AM | DisplayName | Employee |
+------------+----+-------------+---------------+
| 2018-01-01 | 1 | HOL | Sandra Cooper |
| 2018-01-01 | 0 | HOL | Sandra Cooper |
| 2018-01-01 | 1 | HOL | James Adams |
| 2018-01-01 | 0 | HOL | James Adams |
| 2018-01-01 | 1 | HOL | Martha Reid |
| 2018-01-01 | 0 | HOL | Martha Reid |
| 2018-01-02 | 1 | OF | Sandra Cooper |
| 2018-01-02 | 0 | OF | Sandra Cooper |
| 2018-01-02 | 1 | OF | James Adams |
| 2018-01-02 | 0 | OF | James Adams |
| 2018-01-02 | 1 | OF | Martha Reid |
| 2018-01-02 | 0 | OF | Martha Reid |
| 2018-01-03 | 1 | OF | Sandra Cooper |
| 2018-01-03 | 0 | OF | Sandra Cooper |
| 2018-01-03 | 1 | C-A | James Adams |
| 2018-01-03 | 0 | C-A | James Adams |
| 2018-01-03 | 1 | OF | Martha Reid |
| 2018-01-03 | 0 | OF | Martha Reid |
| 2018-01-04 | 1 | OF | Sandra Cooper |
| 2018-01-04 | 0 | OF | Sandra Cooper |
| 2018-01-04 | 1 | C-A | James Adams |
| 2018-01-04 | 0 | C-A | James Adams |
| 2018-01-04 | 1 | OF | Martha Reid |
| 2018-01-04 | 0 | OF | Martha Reid |
| 2018-01-05 | 1 | OF | Sandra Cooper |
| 2018-01-05 | 0 | OF | Sandra Cooper |
| 2018-01-05 | 1 | C-A | James Adams |
| 2018-01-05 | 0 | OF | James Adams |
| 2018-01-05 | 1 | OF | Martha Reid |
| 2018-01-05 | 0 | OF | Martha Reid |
+------------+----+-------------+---------------+
请注意,在1月2日,我希望为每个员工呈现而不是NULL。在1月3日,我想将of(Office)作为Sandra和Martha的默认值,但James有一个特殊的PlanningDay
注册,以便在客户a进行咨询,等等。。。现在我知道我可以使用COALESCE将空值转换为默认值,但在此之前,我需要为每个未注册PlanningDay
的员工设置空值
如果这是可以实现的,那么我可以尝试将这个结果转化为预期的最终结果(见上文)
提前谢谢你 我想你已经接近解决方案了。我刚刚重新调整了初始连接以使用交叉连接,它将为每天、上午/下午和员工的每个组合生成一行。生成后,现有的左JOIN将在您的计划日内插入,您可以用问题中指定的默认值替换空值
你可以试试这个
DECLARE@StartDate DATE='2018-01-01';
声明@EndDate日期='2018-01-31';
全天(
选择@StartDate作为[日期]
联合所有
挑选
DATEADD(天,1,[天])
从…起
整天
哪里
[日期]<@EndDate
)
挑选
广告[日期]作为“日期”
,句号[AM]
,pdd.[DisplayName]
,e.[FirstName]+“”+e.[LastName]作为“员工”
从…起
全天候广告
交叉连接
(选择强制转换(0作为位)作为[AM]联合所有选择强制转换(1作为位))作为期间
交叉连接
[雇员]e
左外连接
[计划日]pd开启(
pd.[日期]=广告[日期]
和pd.EmployeeId=e[Id]
和pd.[AM]=周期。[AM]
)
左外连接
[PlanningDayDetail]pdd开启(pdd.[Id]=pd.[PlanningDayDealId])
左外连接
[PlanningDayType]pdt开启(pdt.[Id]=pdd.[PlanningDayTypeId])
订购人
[日期],[员工],[上午]
这里是一个很好的起点@我相信你是在问一个小项目要用一个问题来实施吗?@SathiyaKumar这不是一个小项目。这是一个相当大的任务。谢谢你的链接肖恩。我已经添加了我的表脚本和相同的虚拟数据来使用。您已经尝试了什么?你需要一张日历表或理货表。在输出中每天返回一行的内容。非常感谢Nik,这就完成了!我以前读过关于交叉联接的文章,但在我挣扎的过程中,它并没有出现在我的脑海中:-)我已经可以开始为一个示例报告使用它了。你知道一种像这样的数据透视方法吗
+------------+----+-------------+---------------+
| Date | AM | DisplayName | Employee |
+------------+----+-------------+---------------+
| 2018-01-01 | 1 | HOL | Sandra Cooper |
| 2018-01-01 | 0 | HOL | Sandra Cooper |
| 2018-01-01 | 1 | HOL | James Adams |
| 2018-01-01 | 0 | HOL | James Adams |
| 2018-01-01 | 1 | HOL | Martha Reid |
| 2018-01-01 | 0 | HOL | Martha Reid |
| 2018-01-02 | 1 | OF | Sandra Cooper |
| 2018-01-02 | 0 | OF | Sandra Cooper |
| 2018-01-02 | 1 | OF | James Adams |
| 2018-01-02 | 0 | OF | James Adams |
| 2018-01-02 | 1 | OF | Martha Reid |
| 2018-01-02 | 0 | OF | Martha Reid |
| 2018-01-03 | 1 | OF | Sandra Cooper |
| 2018-01-03 | 0 | OF | Sandra Cooper |
| 2018-01-03 | 1 | C-A | James Adams |
| 2018-01-03 | 0 | C-A | James Adams |
| 2018-01-03 | 1 | OF | Martha Reid |
| 2018-01-03 | 0 | OF | Martha Reid |
| 2018-01-04 | 1 | OF | Sandra Cooper |
| 2018-01-04 | 0 | OF | Sandra Cooper |
| 2018-01-04 | 1 | C-A | James Adams |
| 2018-01-04 | 0 | C-A | James Adams |
| 2018-01-04 | 1 | OF | Martha Reid |
| 2018-01-04 | 0 | OF | Martha Reid |
| 2018-01-05 | 1 | OF | Sandra Cooper |
| 2018-01-05 | 0 | OF | Sandra Cooper |
| 2018-01-05 | 1 | C-A | James Adams |
| 2018-01-05 | 0 | OF | James Adams |
| 2018-01-05 | 1 | OF | Martha Reid |
| 2018-01-05 | 0 | OF | Martha Reid |
+------------+----+-------------+---------------+
DECLARE @StartDate DATE = '2018-01-01';
DECLARE @EndDate DATE = '2018-01-31';
WITH AllDays AS (
SELECT @StartDate AS [Day]
UNION ALL
SELECT
DATEADD(DAY, 1, [Day])
FROM
AllDays
WHERE
[Day] < @EndDate
)
SELECT
ad.[Day] AS 'Date'
, Period.[AM]
, pdd.[DisplayName]
, e.[FirstName] + ' ' + e.[LastName] AS 'Employee'
FROM
AllDays ad
CROSS JOIN
(SELECT CAST( 0 AS BIT ) AS [AM] UNION ALL SELECT CAST( 1 AS BIT )) AS Period
CROSS JOIN
[Employee] e
LEFT OUTER JOIN
[PlanningDay] pd ON (
pd.[Date] = ad.[Day]
AND pd.EmployeeId = e.[Id]
AND pd.[AM] = Period.[AM]
)
LEFT OUTER JOIN
[PlanningDayDetail] pdd ON (pdd.[Id] = pd.[PlanningDayDealId])
LEFT OUTER JOIN
[PlanningDayType] pdt ON (pdt.[Id] = pdd.[PlanningDayTypeId])
ORDER BY
[Date], [Employee], [AM]