Групповые записи, когда время последовательно в течение заданного минутного интервала

В SQL Server 2017 я пытаюсь сгруппировать несколько записей в одну запись, когда идентификатор учителя и идентификатор клиента совпадают и когда запланированное время составляет сразу 50 минут после предыдущей записи, т.е.

Таблица: Расписание

----------------------------------------------------------------
id  custormerid teacherid   schedule
----------------------------------------------------------------
571654  1085        46      2022-02-22 07:00:00.000
571657  1085        46      2022-02-25 07:00:00.000
571658  1085        46      2022-02-26 07:00:00.000
571659  1085        46      2022-02-26 07:50:00.000
571660  1085        46      2022-02-26 08:40:00.000
571666  1085        46      2022-02-26 10:20:00.000
571661  1085        46      2022-02-28 07:00:00.000
571662  1085        46      2022-02-28 07:50:00.000
571663  1085        11      2022-02-28 08:40:00.000
571664  1085        46      2022-02-24 07:00:00.000
571665  1085        46      2022-02-24 07:50:00.000

Желаемый результат

    --------------------------------------------------------------------------
    custormerid     teacherid   schedule            Qty
    --------------------------------------------------------------------------
    1085        46      2022-02-22 07:00:00.000     1
    1085        46      2022-02-25 07:00:00.000     1
    1085        46      2022-02-26 07:00:00.000     3
    1085        46      2022-02-26 10:20:00.000     1
    1085        46      2022-02-28 07:00:00.000     2
    1085        11      2022-02-28 08:40:00.000     1
    1085        46      2022-02-24 07:00:00.000     2

DDL-скрипт:

CREATE TABLE [dbo].[Schedule](
    [id] [int] NOT NULL,
    [custormerid] [int] NULL,
    [teacherid] [int] NULL,
    [schedule] [datetime] NULL) 
    

INSERT [dbo].[Schedule] ([id], [custormerid], [teacherid], [schedule]) VALUES (571654, 1085, 46, CAST(N'2022-02-22T07:00:00.000' AS DateTime))
INSERT [dbo].[Schedule] ([id], [custormerid], [teacherid], [schedule]) VALUES (571657, 1085, 46, CAST(N'2022-02-25T07:00:00.000' AS DateTime))
INSERT [dbo].[Schedule] ([id], [custormerid], [teacherid], [schedule]) VALUES (571658, 1085, 46, CAST(N'2022-02-26T07:00:00.000' AS DateTime))
INSERT [dbo].[Schedule] ([id], [custormerid], [teacherid], [schedule]) VALUES (571659, 1085, 46, CAST(N'2022-02-26T07:50:00.000' AS DateTime))
INSERT [dbo].[Schedule] ([id], [custormerid], [teacherid], [schedule]) VALUES (571660, 1085, 46, CAST(N'2022-02-26T08:40:00.000' AS DateTime))
INSERT [dbo].[Schedule] ([id], [custormerid], [teacherid], [schedule]) VALUES (571661, 1085, 46, CAST(N'2022-02-28T07:00:00.000' AS DateTime))
INSERT [dbo].[Schedule] ([id], [custormerid], [teacherid], [schedule]) VALUES (571662, 1085, 46, CAST(N'2022-02-28T07:50:00.000' AS DateTime))
INSERT [dbo].[Schedule] ([id], [custormerid], [teacherid], [schedule]) VALUES (571663, 1085, 11, CAST(N'2022-02-28T08:40:00.000' AS DateTime))
INSERT [dbo].[Schedule] ([id], [custormerid], [teacherid], [schedule]) VALUES (571664, 1085, 46, CAST(N'2022-02-24T07:00:00.000' AS DateTime))
INSERT [dbo].[Schedule] ([id], [custormerid], [teacherid], [schedule]) VALUES (571665, 1085, 46, CAST(N'2022-02-24T07:50:00.000' AS DateTime))
INSERT [dbo].[Schedule] ([id], [custormerid], [teacherid], [schedule]) VALUES (571666, 1085, 46, CAST(N'2022-02-24T10:20:00.000' AS DateTime))

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


person Marcoscdoni    schedule 21.02.2021    source источник
comment
Задавая вопрос, вы должны предоставить минимальный воспроизводимый пример. Перейдите по следующей ссылке: stackoverflow.com/help/minimal-reproducible-example Укажите следующее: ( 1) Заполнение DDL и выборочных данных, т. е. таблицы CREATE плюс операторы INSERT T-SQL. (2) Что вам нужно сделать, т. е. логика и ваш код попытаются реализовать ее в T-SQL. (3) Желаемый результат, основанный на примерах данных в № 1 выше. (4) Ваша версия SQL Server (SELECT @@version;)   -  person Yitzhak Khabinsky    schedule 21.02.2021
comment
@YitzhakKhabinsky Спасибо за руководство. Я отредактировал вопрос, теперь это адекватно?   -  person Marcoscdoni    schedule 21.02.2021
comment
@Marcoscdoni В своем ответе я добавил новое обновление под названием Final Update. Пожалуйста, проверьте мое обновление и дайте мне знать, правильно ли оно служит цели или нет. С наилучшими пожеланиями :-)   -  person SJNF    schedule 21.02.2021


Ответы (2)


Я думаю, вам не нужны никакие CTE для решения этой проблемы. Используя Group by, вы можете сделать это =›

SELECT [custormerid],[teacherid],MIN([schedule]) [schedule],
       COUNT(*) Qty
 FROM [dbo].[Schedule]
GROUP BY [custormerid],[teacherid],CAST([schedule] AS DATE)

Обновление: Теперь, если вы хотите сохранить последовательность вывода, указанную в вопросе, вы можете использовать CTE, как показано ниже.

WITH CTE AS
(
    SELECT MAX([id]) MyID,[custormerid],[teacherid],MIN([schedule]) [schedule],
           COUNT(*) Qty
     FROM [dbo].[Schedule]
    GROUP BY [custormerid],[teacherid],CAST([schedule] AS DATE)
) 
SELECT [custormerid],[teacherid],[schedule],Qty FROM CTE ORDER BY MyID

Вывод:

введите здесь описание изображения

Последнее обновление: теперь я использовал несколько методов, чтобы добиться желаемого. Вы сказали в комментарии, что вам нужно считать их с разницей в 50 минут. Итак, я сделал это с несколькими CTE. Я считаю, что запрос обеспечивает идеальный результат для небольшого объема данных, но я не тестировал его для большого объема данных. Другое дело, что производительность запроса может быть низкой для большого объема данных. Итак, пожалуйста, попробуйте и дайте мне знать =›

WITH myCTE AS
(
 SELECT *,
      --I have used DENSE_RANK for Ordering the rows by [schedule]
      DENSE_RANK() OVER(PARTITION BY [custormerid],[teacherid],CAST([schedule] AS DATE) ORDER BY ID,CAST([schedule] AS DATE)) DRNK
 FROM [dbo].[Schedule]
),
CTE AS
(
    --Finding the parentID using logic of 50 min and others
    SELECT *,
    CASE WHEN DATEADD(MINUTE, -50,  [schedule])<=LAG([schedule],1) OVER(ORDER BY (SELECT NULL)) 
              AND [teacherid]=LAG([teacherid],1) OVER(ORDER BY (SELECT NULL))
              AND [custormerid]=LAG([custormerid],1) OVER(ORDER BY (SELECT NULL))
              THEN LAG(ID,1) OVER(ORDER BY (SELECT NULL)) 
              ELSE NULL END AS parentid
    FROM myCTE
),
myY AS
(   
   --Grouping the items Using Tree concept
    SELECT CTE.*, id AS rootid FROM CTE
    WHERE parentid IS NULL
    UNION ALL
    SELECT C.*, P.rootid FROM myY AS P
    INNER JOIN CTE AS C ON P.id = C.parentid
)
SELECT
    MAX([custormerid]) [custormerid],
    MAX([teacherid]) [teacherid],
    MIN([schedule]) [schedule],
    COUNT(*) Qty
FROM myY
GROUP BY rootid
person SJNF    schedule 21.02.2021
comment
Кол-во не является полем в таблице, это значение не сохраняется, но должно отображаться при группировке записей, чтобы определить количество сгруппированных записей. - person Marcoscdoni; 21.02.2021
comment
@Маркоскдони Да. Вот почему я использую COUNT(*) для определения количества записей. Я также добавил обновление к своему ответу. Если вы все еще сталкиваетесь с проблемой, дайте мне знать. Я сделаю все возможное, чтобы помочь вам. :-) - person SJNF; 21.02.2021
comment
Но основная проблема состоит в том, чтобы сгруппировать только записи с точной разницей в 50 минут между ними. Записи с разницей более 50 минут или другого идентификатора учителя не должны группироваться. - person Marcoscdoni; 21.02.2021
comment
Ok. Подожди, дай мне попробовать. - person SJNF; 21.02.2021
comment
большое спасибо! Я попробую использовать больший объем данных, но пока это идеально! - person Marcoscdoni; 21.02.2021

Пожалуйста, попробуйте следующее решение.

SQL

-- DDL and sample data population, start
DECLARE @Schedule TABLE (
    id int NOT NULL,
    custormerid int NULL,
    teacherid int NULL,
    schedule datetime NULL) 

INSERT @Schedule (id, custormerid, teacherid, schedule) VALUES 
(571654, 1085, 46, '2022-02-22T07:00:00.000')
,(571657, 1085, 46,'2022-02-25T07:00:00.000')
,(571658, 1085, 46,'2022-02-26T07:00:00.000')
,(571659, 1085, 46,'2022-02-26T07:50:00.000')
,(571660, 1085, 46,'2022-02-26T08:40:00.000')
,(571661, 1085, 46,'2022-02-28T07:00:00.000')
,(571662, 1085, 46,'2022-02-28T07:50:00.000')
,(571663, 1085, 11,'2022-02-28T08:40:00.000')
,(571664, 1085, 46,'2022-02-24T07:00:00.000')
,(571665, 1085, 46,'2022-02-24T07:50:00.000');
-- DDL and sample data population, end

WITH rs AS
(
    SELECT *
        , DATEDIFF(minute, '1900-01-01', schedule) % 50 AS gr
    FROM @Schedule
)
SELECT custormerid, teacherid, MIN(schedule) AS schedule, COUNT(*) AS Qty
FROM rs
GROUP BY  custormerid, teacherid, gr
ORDER BY MIN(schedule);

Выход

+-------------+-----------+-------------------------+-----+
| custormerid | teacherid |        schedule         | Qty |
+-------------+-----------+-------------------------+-----+
|        1085 |        46 | 2022-02-22 07:00:00.000 |   1 |
|        1085 |        46 | 2022-02-24 07:00:00.000 |   2 |
|        1085 |        46 | 2022-02-25 07:00:00.000 |   1 |
|        1085 |        46 | 2022-02-26 07:00:00.000 |   3 |
|        1085 |        46 | 2022-02-28 07:00:00.000 |   2 |
|        1085 |        11 | 2022-02-28 08:40:00.000 |   1 |
+-------------+-----------+-------------------------+-----+

Способ 2

Это уродливо, и мне это не нравится, но попробуйте.

;WITH rs AS
(
   SELECT * 
      , LAG(schedule, 1) OVER (PARTITION BY custormerid, teacherid ORDER BY schedule) AS LagValue
      , ROW_NUMBER() OVER (PARTITION BY custormerid, teacherid, CAST(schedule AS DATE) ORDER BY schedule) AS seq
   FROM @Schedule
), cte AS (
    SELECT *
       , IIF(DATEDIFF(MINUTE, LagValue, schedule) <= 50 OR seq = 1, 1, 0) AS [legit]
       , DATEDIFF(minute, '1900-01-01', schedule) % 50 AS gr
    FROM rs
)
SELECT custormerid, teacherid, MIN(schedule) AS schedule --, legit, gr
    , Qty = SUM(IIF(legit=0,1,legit))
FROM cte
GROUP BY custormerid, teacherid, gr, legit
ORDER BY custormerid, teacherid, MIN(schedule);
person Yitzhak Khabinsky    schedule 21.02.2021
comment
Если вы вставите следующую запись в таблицу, вы увидите, что она группируется неправильно. 26 числа он посчитает всего 4 записи, но интервал больше 50 минут. ВСТАВИТЬ расписание (id, custormerid, id учителя, расписание) ЗНАЧЕНИЯ (571666, 1085, 46, CAST(N'2022-02-26T10:20:00.000' AS DateTime)) - person Marcoscdoni; 21.02.2021
comment
@Marcoscdoni, попробуйте метод № 2 - person Yitzhak Khabinsky; 21.02.2021