我正在研究一个用于安排餐厅员工的系统(ASP.NET / MSSQL/C#) 我遇到的问题是我需要每周“自动旋转”班次“InTimes”。
用户需要能够将一天的时间表复制到下周的同一天,所有员工轮班时间都会旋转一个时段。
例如,在下表中,莫妮卡本周一上午10:30,所以她将在下周的上午11:00,亚当将从中午12:00到上午10:30。
轮班之间的时间不是一成不变的,每班轮班的员工人数也不一样
任何有关如何执行此操作的想法(理想情况下使用SQL语句)都将非常感激。
请记住,我是一个相对的新手。
RecordID EmpType Date Day Meal ShiftOrder InTime EmployeeID
1 Server 29-Aug-11 Monday Lunch 1 10:30:00 AM Monica
2 Server 29-Aug-11 Monday Lunch 2 11:00:00 AM Sofia
3 Server 29-Aug-11 Monday Lunch 3 11:30:00 AM Jenny
4 Server 29-Aug-11 Monday Lunch 4 12:00:00 PM Adam
5 Server 29-Aug-11 Monday Dinner 1 4:30:00 PM Adam
6 Server 29-Aug-11 Monday Dinner 2 4:45:00 PM Jenny
7 Server 29-Aug-11 Monday Dinner 3 5:00:00 PM Shauna
8 Server 29-Aug-11 Monday Dinner 4 5:15:00 PM Sofia
10 Server 29-Aug-11 Monday Dinner 5 5:30:00 PM Monica
答案 0 :(得分:1)
不知何故,员工需要完成最后(几次)轮班
SELECT TOP 3 * FROM shift WHERE EmployeeID LIKE 'monica' ORDER BY [date] DESC
接下来,他/她需要输入他希望下周工作的时间和日期偏移,相对于之前的时间表。
INSERT INTO shift SELECT
recordID
,[date]
,CASE [Intime]
WHEN [Intime] BETWEEN 00:00 AND 10:00 THEN 'Breakfast'
WHEN [Intime] BETWEEN 10:01 AND 04:29 THEN 'Lunch'
WHEN [Intime] BETWEEN 04:30 AND 23:59 THEN 'Dinner'
END as Meal
,No_idea_how_to_generate_this AS ShiftOrder
,[Intime]
,EmployeeID
FROM (SELECT
NULL as recordID
,DATEADD(DAY, 7+@dateoffset, ls.[date]) as [date]
,CAST(DATEADD(MINUTE, @timeoffset, ls.[time] AS TIME) as [Intime]
,EmployeeId
FROM Shift WHERE recordID = @recordID ) AS subselect
下面:
- @recordID
是员工选择作为新约会起点的记录
- @dateoffset
是添加起始记录的天数
- @timeoffset
是要添加到起始记录的分钟数
所有其余部分由用户用作起点的行确定。
答案 1 :(得分:1)
以下是我提出的建议:
CREATE TABLE #tmp
(
[RecordID] INT ,
[EmpType] VARCHAR(20) ,
[Date] DATE ,
[Day] VARCHAR(10) ,
[Meal] VARCHAR(10) ,
[ShiftOrder] INT ,
[InTime] TIME ,
[EmployeeID] VARCHAR(50)
)
INSERT INTO [#tmp]
( [RecordID] ,
[EmpType] ,
[Date] ,
[Day] ,
[Meal] ,
[ShiftOrder] ,
[InTime] ,
[EmployeeID]
)
VALUES (1,'Server','29-Aug-11','Monday','Lunch',1,'10:30:00 AM','Monica'),
(2,'Server','29-Aug-11','Monday','Lunch',2,'11:00:00 AM','Sofia'),
(3,'Server','29-Aug-11','Monday','Lunch',3,'11:30:00 AM','Jenny'),
(4,'Server','29-Aug-11','Monday','Lunch',4,'12:00:00 PM','Adam'),
(5,'Server','29-Aug-11','Monday','Dinner',1,'4:30:00 PM','Adam'),
(6,'Server','29-Aug-11','Monday','Dinner',2,'4:45:00 PM','Jenny'),
(7,'Server','29-Aug-11','Monday','Dinner',3,'5:00:00 PM','Shauna'),
(8,'Server','29-Aug-11','Monday','Dinner',4,'5:15:00 PM','Sofia'),
(10,'Server','29-Aug-11','Monday','Dinner',5,'5:30:00 PM','Monica');
WITH CountByShift AS (SELECT *, COUNT(1) OVER (PARTITION BY EmpType, [Day], [Meal]) AS [CountByShiftByDayByEmpType]
FROM [#tmp]
),
NewShiftOrder AS (
SELECT *, ([ShiftOrder] + 1) % [CountByShiftByDayByEmpType] AS [NewShiftOrder]
FROM [CountByShift]
)
SELECT [RecordID] ,
[EmpType] ,
[Date] ,
[Day] ,
[Meal] ,
[ShiftOrder] ,
CASE WHEN [NewShiftOrder] = 0 THEN [CountByShiftByDayByEmpType] ELSE [NewShiftOrder] END AS [NewShiftOrder],
[InTime] ,
[EmployeeID]
FROM NewShiftOrder
ORDER BY [RecordID]
答案 2 :(得分:1)
你需要一张包含所有变化的表格:
create table dbo.Shifts (
[Day] varchar(9) not null,
Meal varchar(6) not null,
ShiftOrder integer not null,
InTime time not null,
constraint PK__dbo_Shifts primary key ([Day], Meal, ShiftOrder)
);
如果该表已正确填充,则可以运行此表以获取当天Day,Meal,ShiftOrder n-tuple到该日的下一个,$ Mep对的映射:
with numbers_per_shift as (
select [Day], Meal, max(ShiftOrder) as ShiftOrderCount
from dbo.Shifts s
group by [Day], Meal
)
select s.[Day], s.Meal, s.ShiftOrder,
s.ShiftOrder % n.ShiftOrderCount + 1 as NextShiftOrder
from dbo.Shifts as s
inner join numbers_per_shift as n
on s.[Day] = n.[Day]
and s.Meal = n.Meal;
对于正确填充的表格,每个班次订单必须以1开头,并且在一天中没有跳过或重复的情况下增加一个。
答案 3 :(得分:1)
从@Ben Thul借用大部分#tmp表定义,假设您有一个身份字段,而不是假设您将日期和时间存储为日期和时间......这应该反复运行,复制最新日期进入下一周:
CREATE TABLE #tmp
(
[RecordID] INT ,
[EmpType] VARCHAR(20) ,
[Date] VARCHAR(9) ,
[Day] VARCHAR(10) ,
[Meal] VARCHAR(10) ,
[ShiftOrder] INT ,
[InTime] VARCHAR(11) ,
[EmployeeID] VARCHAR(50)
)
INSERT INTO [#tmp]
( [RecordID] ,
[EmpType] ,
[Date] ,
[Day] ,
[Meal] ,
[ShiftOrder] ,
[InTime] ,
[EmployeeID]
)
VALUES (1,'Server','29-Aug-11','Monday','Lunch',1,'10:30:00 AM','Monica'),
(2,'Server','29-Aug-11','Monday','Lunch',2,'11:00:00 AM','Sofia'),
(3,'Server','29-Aug-11','Monday','Lunch',3,'11:30:00 AM','Jenny'),
(4,'Server','29-Aug-11','Monday','Lunch',4,'12:00:00 PM','Adam'),
(5,'Server','29-Aug-11','Monday','Dinner',1,' 4:30:00 PM','Adam'),
(6,'Server','29-Aug-11','Monday','Dinner',2,' 4:45:00 PM','Jenny'),
(7,'Server','29-Aug-11','Monday','Dinner',3,' 5:00:00 PM','Shauna'),
(8,'Server','29-Aug-11','Monday','Dinner',4,' 5:15:00 PM','Sofia'),
(10,'Server','29-Aug-11','Monday','Dinner',5,' 5:30:00 PM','Monica');
with
Shifts as (
select EmpType, [Day], Meal, ShiftOrder, InTime
from #tmp
where [Date] = (select max(cast([Date] as datetime)) from #tmp)
),
MaxShifts as (
select EmpType, [Day], Meal, max(ShiftOrder) as MaxShiftOrder
from #tmp
where [Date] = (select max(cast([Date] as datetime)) from #tmp)
group by EmpType, [Day], Meal
)
insert into #tmp (EmpType, [Date], [Day], Meal, ShiftOrder, InTime, EmployeeID)
select s.EmpType
, replace(convert(varchar(11), dateadd(dd, 7, cast(a.[Date] as datetime)), 6), ' ', '-') as [Date]
, s.Day
, s.Meal
, s.ShiftOrder
, s.InTime
, a.EmployeeID
from #tmp as a
join MaxShifts as m on a.EmpType = m.EmpType
and a.[Day] = m.[Day]
and a.Meal = m.Meal
join Shifts as s on a.EmpType = s.EmpType
and a.[Day] = s.[Day]
and a.Meal = s.Meal
and 1 + a.ShiftOrder % m.MaxShiftOrder = s.ShiftOrder
where a.[Date] = (select max(cast([Date] as datetime)) from #tmp)
答案 4 :(得分:1)
我假设时间表与下一个答案中的用餐和工作日有关。
另外我想注意,ShiftOrder和Day列不应该是列。日显然由日期确定,因此它是一个完全浪费的空间(计算列或在UI侧确定它)和ShiftOrder由Date和InTime列确定(可能很容易在带有RANK()函数的查询中计算或在UI方面)。这说它会使这个查询更容易:)
declare @dt date = cast('29-Aug-11' as date)
/* note: the date above may be passed from UI or it maybe calculated based on getdate() and dateadd function or s.t. like that */
INSERT INTO [table] (EmpType,Date,Day,Meal,ShiftOrder,InTime,EmployeeID)
SELECT t1.EmpType, dateadd(day, 7, t1.date), t1.day, t1.meal, t2.ShiftOrder, t2.InTime, t1.EmployeeID
FROM [table] t1
INNER JOIN [table] t2
ON (t1.Date = t2.Date
and t1.Meal = t2.Meal
and (
t1.ShiftOrder = t2.ShiftOrder + 1
or
(
t1.ShiftOrder = (select max(shiftOrder) from [table] where meal = t1.meal and date =t1.date)
and
t2.ShiftOrder = (select min(shiftOrder) from [table] where meal = t1.meal and date =t1.date)
)
)
)
WHERE t1.Date = @dt
答案 5 :(得分:1)
这是一个非常简单的面向集合的问题。聚合(count(*)和max())和查找表是不必要的。您可以使用一个SQL语句来完成它。
第一步(设置)是识别那些只是在计划中滑落的员工。
下一步(设定)是将需要“环绕”的员工识别为计划的负责人。
以下是我提出的建议:
/* Set up the temp table for demo purposes */
DROP TABLE #tmp
CREATE TABLE #tmp
(
[RecordID] INT ,
[EmpType] VARCHAR(20) ,
[Date] DATE ,
[Day] VARCHAR(10) ,
[Meal] VARCHAR(10) ,
[ShiftOrder] INT ,
[InTime] TIME,
[EmployeeID] VARCHAR(50)
)
INSERT INTO [#tmp]
( [RecordID] ,
[EmpType] ,
[Date] ,
[Day] ,
[Meal] ,
[ShiftOrder] ,
[InTime] ,
[EmployeeID]
)
VALUES (1,'Server','29-Aug-11','Monday','Lunch',1,'10:30:00 AM','Monica'),
(2,'Server','29-Aug-11','Monday','Lunch',2,'11:00:00 AM','Sofia'),
(3,'Server','29-Aug-11','Monday','Lunch',3,'11:30:00 AM','Jenny'),
(4,'Server','29-Aug-11','Monday','Lunch',4,'12:00:00 PM','Adam'),
(5,'Server','29-Aug-11','Monday','Dinner',1,' 4:30:00 PM','Adam'),
(6,'Server','29-Aug-11','Monday','Dinner',2,' 4:45:00 PM','Jenny'),
(7,'Server','29-Aug-11','Monday','Dinner',3,' 5:00:00 PM','Shauna'),
(8,'Server','29-Aug-11','Monday','Dinner',4,' 5:15:00 PM','Sofia'),
(10,'Server','29-Aug-11','Monday','Dinner',5,' 5:30:00 PM','Monica');
/* the "fills" CTE will find those employees who "wrap around" */
;WITH fills AS (
SELECT
[d2].[EmpType],
[d2].[Date],
[d2].[Day],
[d2].[Meal],
1 AS [ShiftOrder],
[d2].[InTime],
[d2].[EmployeeID]
FROM
[#tmp] d1
RIGHT OUTER JOIN
[#tmp] d2 ON
([d1].[Meal] = [d2].[Meal])
AND ([d1].[ShiftOrder] = [d2].[ShiftOrder] + 1)
WHERE
[d1].[EmployeeID] IS NULL
)
INSERT INTO [table] (EmpType,Date,Day,Meal,ShiftOrder,InTime,EmployeeID)
SELECT
[d1].[EmpType],
DATEADD(DAY, 7, [d1].[Date]) AS [Date],
DATENAME(dw,(DATEADD(DAY, 7, [d1].[Date]))) AS [Day],
[d1].[Meal],
[d1].[ShiftOrder],
[d1].[InTime],
ISNULL([d2].[EmployeeID], [f].[EmployeeID]) AS [EmployeeID]
FROM
[#tmp] d1
LEFT OUTER JOIN
[#tmp] d2 ON
([d1].[Meal] = [d2].[Meal]) AND ([d1].[ShiftOrder] = [d2].[ShiftOrder] + 1)
LEFT OUTER JOIN
[fills] f ON
([d1].[Meal] = [f].[Meal]) AND ([d1].[ShiftOrder] = [f].[ShiftOrder])
答案 6 :(得分:0)
您可以使用子查询(有关子查询的教程,请参阅http://www.databasejournal.com/features/mssql/article.php/3464481/Using-a-Subquery-in-a-T-SQL-Statement.htm)以获取最后一个班次时间。 在此之后,它的琐碎添加和模块划分(如果您不知道那是什么,请查看this)。
希望这有帮助。我现在有点累了,所以我不能给你一个例子。
答案 7 :(得分:0)
我现在是一名SQL程序员和DBA 20年了。话虽如此,这个复杂的业务逻辑应该在系统的C#部分。然后,TDD构建的应用程序可以处理不可避免的变化,并且仍然可以重构和正确。
我的推荐是'推后'。您的回答应该是“这不仅仅是一些查找/填写空白逻辑。这种复杂的业务逻辑属于应用程序”。它属于可以进行单元测试的东西,并且每次更改时都会进行单元测试。
正确的答案有时候是'不',这是其中之一。
答案 8 :(得分:0)
如何为所有员工使用数据透视表,然后将换班时间添加为行?在初始日基于Shift订购名称。
像这样......
Date_time Shift_Order Monica Sofia Jenny Adam Shauna
08/29/11 1 10:30AM 11:00AM 11:30AM 12:00PM NULL
08/29/11 2 5:30PM 5:15PM 4:45PM 4:30PM 5:00PM