Как я могу «автоматически вращать» добавленные записи в SQL (1 - 2, 2 - 3, 3 - 4, 4 - 1)? - PullRequest
4 голосов
/ 02 сентября 2011

Я работаю над системой (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

Ответы [ 9 ]

1 голос
/ 22 февраля 2012

Это довольно простая задача, ориентированная на множество. Агрегирования (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])
1 голос
/ 04 ноября 2011

Заимствование большей части определения таблицы #tmp от @Ben Thul, при условии, что у вас есть поле идентификации, а не при условии, что вы сохраняете даты и время как даты и время ... это должно выполняться много раз, копируя самую последнюю датуна следующую неделю:

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)
1 голос
/ 10 февраля 2012

Я предполагаю, что график действительно связан с едой и днем ​​недели в ответе ниже.

Также хотелось бы отметить, что столбцы ShiftOrder и Day не должны быть столбцами. День, очевидно, определяется датой, поэтому он является пустой тратой пространства (вычисляемый столбец ИЛИ определяет его на стороне пользовательского интерфейса), а ShiftOrder определяется столбцами Date и InTime (вероятно, их легко вычислить в запросе с помощью функции RANK () или в Пользовательский интерфейс). Тем не менее, это сделает этот запрос немного проще:)

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
1 голос
/ 05 сентября 2011

Вам нужен стол со всеми сменами в нем:

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) 
);

Если эта таблица правильно заполнена , вы можете запустить ее, чтобы получить карту текущего дня, еды, ShiftOrder и следующей пары в этот день, пары еды:

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 голос
/ 03 сентября 2011

Вот что я придумал:

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]
1 голос
/ 03 сентября 2011

Каким-то образом работник должен был бы получить свои последние (несколько) смен

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 - количество минут, которое нужно добавить к начальной записи

Все остальное определяется строкой, которую пользователь использовал в качестве отправной точки.

0 голосов
/ 13 марта 2012

Как насчет использования сводной таблицы для всех сотрудников и последующего добавления времени смены в виде строк?Заказать имена на основе 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
0 голосов
/ 28 января 2012

Я программист SQL и администратор баз данных уже 20 лет.С учетом сказанного, бизнес-логика этого комплекса должна быть в C # части системы.Тогда созданное TDD приложение может обрабатывать неизбежные изменения и при этом быть способным к рефакторингу и корректировать.

Моя рекомендация - «откат».Ваш ответ должен выглядеть примерно так: «Это не просто поиск / заполнение пустой логики. Этот вид сложной бизнес-логики принадлежит приложению».Он принадлежит к тому, что может быть проверено модулем, и будет проверяться модулем каждый раз, когда его изменяют.

Правильный ответ иногда - «Нет», это один из них.

0 голосов
/ 03 сентября 2011

Вы можете использовать подзапрос (для учебника по подзапросам см. http://www.databasejournal.com/features/mssql/article.php/3464481/Using-a-Subquery-in-a-T-SQL-Statement.htm), чтобы получить время последней смены. После этого, его тривиальное добавление и модульное деление (если вы не знаете, что это такое,взгляните на это ).

Надеюсь, это помогло. Я немного устала, поэтому не могу предоставить вам пример.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...