SQL считать разное время с разницей в 30 минут - PullRequest
3 голосов
/ 28 июня 2010

Я пытаюсь найти SQL-запрос, который будет подсчитывать количество различных начальных времен, которые различаются как минимум на 30 минут.

У меня есть ряд сотрудников, которым выплачивают кредит, когда они приступают к работе, по крайней мере, три раза в неделю, причем время начала не менее чем на 30 минут отличается от времени начала работы. Например:

select count(distinct (CONVERT(VARCHAR(10), starttime, 108))), employeecode
from schedule 
where CONVERT(VARCHAR(10), starttime, 108) >= 
(select min(CONVERT(VARCHAR(10), dateadd (mi, 30, s2.starttime), 108)) from schedule s2)  
group by starttime, employeecode

Я надеюсь получить результат с кодом сотрудника и количеством разных и разных времен начала. например. Код сотрудника = 9999, разное время начала = 4 Я неуклюже проходил через это, и мне еще предстоит что-то сделать ...

Кто-нибудь может подсказать, где я ошибаюсь, или подходящее решение, которое может мне помочь? Заранее спасибо за помощь:)

Ответы [ 5 ]

1 голос
/ 28 июня 2010

В ожидании уточнения точных требований я подумал, что я бы предложил другой подход. Я положу плюсы и минусы с этим ...

Если время начала обычно в определенное время или около него (ваши примеры всегда были на получасе или на часе), тогда вы можете просто разделить все времена начала на то, в какой «полосе» они находятся, и затем посчитать количество разных полос.

например, 00: 00-00: 30 = группа 1 00: 30-01: 00 = группа 2 ... 07: 00-07: 30 = группа 15 ... 23: 30-00: 00 = группа 48

Чтобы получить группы, вам просто понадобится простое (хотя и довольно длинное) описание случая.

Основная проблема этого подхода заключается в том, что он падает, когда ваше время приближается к порогу. например, 07:29 и 07:31 были бы в двух разных группах, но на самом деле они разнесены на 2 минуты. Это можно немного смягчить, если вы начинаете примерно в одно и то же время, заставляя свои группы начинать и заканчивать в 15 и 45 минут после каждого часа. Тогда, если время начала все в середине группы, тогда вы получите это в основном правильно ...

Хотя, на мой взгляд, проблема не в том, что подходит для SQL, так что если вы можете сделать это на другом языке, который может быть лучше ...

Возможно, вы могли бы сделать это в SQL с некоторыми хитрыми соединениями, но я не способен написать для него надежный SQL ... Алгоритмически, хотя вы хотите сделать следующее.

1) примите самое раннее время начала дня и назовите его своим первым временем запуска. 2) возьмите следующее самое раннее время, которое по крайней мере на 30 минут позже, чем время из вашего предыдущего шага. 3) Повторите шаг 2, пока не закончится время. 4) Подсчитайте время.

Проблема с этим с точки зрения SQL состоит в том, что он пытается создать данные, основанные на предыдущей строке, что будет означать работу с курсорами для циклического прохождения вашего времени и сохранения вещей в переменных.

0 голосов
/ 28 июня 2010

Просто чтобы дать вам представление:

  • Сборка SQL только за одну неделю
  • Выполните SELF JOIN из schedule, вычитая время начала каждого из другого
  • Теперь посчитайте все DISTINCT различия больше 30 минут для каждого клиента -> дает вам количество раз начала

Это должно дать вам желаемый результат.

0 голосов
/ 28 июня 2010

Используя временные диапазоны (не путать с временными бандитами), о которых говорил Крис:

CREATE TABLE Start_Periods
(
    begin_time    TIME        NOT NULL,
    end_time      TIME        NOT NULL,
    time_period   TINYINT     NOT NULL
    CONSTRAINT PK_Start_Periods PRIMARY KEY CLUSTERED (begin_time),
    CONSTRAINT CK_Start_Periods_begin_before_end CHECK (begin_time < end_time OR end_time = '00:00:00.000')
)
INSERT INTO Start_Periods (begin_time, end_time, time_period)
SELECT '00:00:00.000', '00:15:00.000', 1 UNION ALL
SELECT '00:15:00.000', '00:45:00.000', 2 UNION ALL
SELECT '00:45:00.000', '01:15:00.000', 3 UNION ALL
SELECT '01:15:00.000', '01:45:00.000', 4 UNION ALL
SELECT '01:45:00.000', '02:15:00.000', 5 UNION ALL
SELECT '02:15:00.000', '02:45:00.000', 6 UNION ALL
SELECT '02:45:00.000', '03:15:00.000', 7 UNION ALL
SELECT '03:15:00.000', '03:45:00.000', 8 UNION ALL
--...
SELECT '23:15:00.000', '23:45:00.000', 48 UNION ALL
SELECT '23:45:00.000', '00:00:00.000', 1

Ваш запрос становится:

SELECT
    SCH.employee_code,
    COUNT(DISTINCT SP.time_period) AS different_time_starts
FROM
    Schedule SCH
INNER JOIN Start_Periods SP ON
    SP.begin_time <= SCH.start_time AND
    SP.end_time > SCH.start_time
GROUP BY
    SCH.employee_code
0 голосов
/ 28 июня 2010

Я предполагаю, что вашим продуктом базы данных является SQL Server на основе вашего OP, но вы не упомянули версию.Если вы используете SQL Server 2005 и более поздние версии, вы можете попробовать что-то вроде:

With StartTimes As
    (
    Select StartDateTime 
        , Row_Number() Over( Order By StartDateTime ) As Seq 
        , DatePart(hh, StartDateTime) * 60 + DatePart(mi, StartDateTime) As Minutes
    From Schedule
    )
Select *
From StartTimes As S1
Where Exists(
            Select 1
            From StartTimes As S2
            Where S1.Seq <> 1
                And Abs(S2.Minutes - S1.Minutes) >= 30
            )
0 голосов
/ 28 июня 2010

[Обновление: Судя по разъяснению автора проблемы в комментарии к этому ответу, проблема, которую я решал с помощью этого ответа, явно не является проблемой, которую пытается решить автор.Я оставляю ответ, чтобы показать решение проблемы other и не удалять комментарии, поясняющие формулировку проблемы]

Разбить проблему на две части:Идентификация «уникальных» (в течение 30 минут) запусков и затем их подсчет.Первая часть - та, с которой, я думаю, у тебя проблемы.Вот подход:

SELECT employeecode, starttime FROM schedule S1
    WHERE NOT EXISTS (SELECT * FROM schedule S2 
        WHERE S2.employeecode = S1.employeecode AND
              S2.starttime > DATEADD(mi, -29, S1.starttime)

Несколько замечаний:

  • Я скопировал математическую логику даты из вашего исходного запроса, а не искал синтаксис.

  • Я предполагаю, что время начала - DATETIME.

  • Я использовал 29 минут, чтобы они получали бонус, если бы время начала было на расстоянии 30 или более минут (какуказано в вашей постановке задачи).На самом деле, вы должны сделать это, выполнив математику даты, используя секунды и вычитая (29 * 60) + 59. Моя версия немного более щедра к сотрудникам, чем указано в вашем заявлении о проблеме.

  • Вы можете инкапсулировать этот запрос в представление или внутренний запрос и сделать что-то вроде (при условии, что это представление):

    ВЫБРАТЬ код сотрудника, количество () ОТ уникального_старта_ГДЕ время начала между (начало периода) И (конецпериода) код сотрудника BY BY COUNT COUNT ()> = 3

  • Техника NOT EXISTS может быть медленной, поэтому лучше ограничить этот запрос интересующим периодом.

...