Получить записи на основе текущей даты и настроенных данных

Я работаю над автоматизацией части отправки SMS в своем веб-приложении.

Ссылка на скрипт SQL

Таблица

DurationType хранит, следует ли отправлять sms через интервал часов, дней, недель, месяцев. Называется в SMSConfiguration

CREATE TABLE [dbo].[DurationType](
[Id] [int] NOT NULL PRIMARY KEY,
[DurationType] VARCHAR(10) NOT NULL
)

Таблица заказов Содержит исходные данные о бронировании. Для этого заказа мне нужно отправить SMS на основе конфигурации.

enter image description here

Конфигурация SMS. Определяет конфигурацию для отправки автоматического SMS. Его можно отправить до/после. Before = 0, After = 1. DurationType can be Hours=1, Days=2, Weeks=3, Months=4

enter image description here

Теперь мне нужно узнать список заказов, которым необходимо отправить SMS в текущее время на основе набора конфигурации SMS.

Пробовал SQL с помощью UNION

DECLARE @currentTime smalldatetime = '2014-07-12 11:15:00'


-- 'SMS CONFIGURED FOR HOURS BASIS'

SELECT  B.Id AS BookingId, 
        B.StartTime AS BookingStartTime,@currentTime As CurrentTime,  SMS.SMSText
FROM    Bookings B INNER JOIN
        SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE   (DATEDIFF(HOUR, @currentTime, B.StartTime) = SMS.Duration AND SMS.DurationType=1 AND BeforeAfter=0)
        OR
        (DATEDIFF(HOUR, B.StartTime, @currentTime) = SMS.Duration AND SMS.DurationType=1 AND BeforeAfter=1)


--'SMS CONFIGURED FOR DAYS BASIS'

UNION

SELECT  B.Id AS BookingId, 
        B.StartTime AS BookingStartTime,@currentTime As CurrentTime,  SMS.SMSText
FROM    Bookings B INNER JOIN
        SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE (DATEDIFF(DAY, @currentTime, B.StartTime) = SMS.Duration AND SMS.DurationType=2 AND BeforeAfter=0)
        OR
      (DATEDIFF(DAY, B.StartTime, @currentTime) = SMS.Duration AND SMS.DurationType=2 AND BeforeAfter=1)
--'SMS CONFIGURED FOR WEEKS BASIS'

UNION

SELECT  B.Id AS BookingId, 
        B.StartTime AS BookingStartTime, @currentTime As CurrentTime, SMS.SMSText
FROM    Bookings B INNER JOIN
        SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE (DATEDIFF(DAY, @currentTime, B.StartTime)/7 = SMS.Duration AND SMS.DurationType=3 AND BeforeAfter=0)
        OR
      (DATEDIFF(DAY, B.StartTime, @currentTime)/7 = SMS.Duration AND SMS.DurationType=3 AND BeforeAfter=1)
--'SMS CONFIGURED FOR MONTHS BASIS'

UNION

SELECT  B.Id AS BookingId, 
        B.StartTime AS BookingStartTime, @currentTime As CurrentTime, SMS.SMSText
FROM    Bookings B INNER JOIN
        SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE   (dbo.FullMonthsSeparation(@currentTime, B.StartTime) = SMS.Duration AND SMS.DurationType=4 AND BeforeAfter=0)
         OR
        (dbo.FullMonthsSeparation(B.StartTime, @currentTime) = SMS.Duration AND SMS.DurationType=4 AND BeforeAfter=1)

Результат

enter image description here

Проблема:

Процедура SQL будет выполняться каждые 15 минут. В текущем запросе сохраняются отчеты о возврате дней/недель/месяцев даже для текущего времени "2014-07-12 11:30:00", "2014-07-12 11:45:00" и т.д.

Мне нужен один запрос, который будет обслуживать все часы/дни/недели/месяцы расчет, и я должен получать записи только один раз, когда они встречаются правильное время. В противном случае я буду отправлять sms снова и снова каждые 15 минут для записи в день/неделю/месяц.

Он должен учитывать следующие сценарии.

  • Час, если бронирование составляет 10:15 A.M в тот же день 9:15 A.M, если он настроен до 1 часа

  • День (разница в 24 часа), если бронирование 10:15 A.M на 3-й день утром 10:15 A.M, если он настроен через 3 дня в SMSConfiguration

  • Неделя матча. Если бронирование составляет 10:15 вечера по московскому времени (среда) утром, то после 14 дней утра 10:15 A.M, если он настроен через 2 недели.

  • Месяц также та же логика, что и выше.

Ответ 1

Попробуйте упрощенную версию FIDDLE, удалили союз и использовали условия OR

СПИСОК ДЛЯ ЧАСОВ - RUN Каждые 15 минут

DECLARE @currentTime smalldatetime = '2014-07-12 11:15:00'



SELECT  B.Id AS BookingId, C.Id, C.Name,
        B.StartTime AS BookingStartTime,@currentTime As CurrentTime,  SMS.SMSText
FROM    Bookings B INNER JOIN
        SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
        LEFT JOIN Category C ON C.Id=B.CategoryId
WHERE   
        (DATEDIFF(MINUTE, @currentTime, B.StartTime) = SMS.Duration*60 AND SMS.DurationType=1 AND BeforeAfter=0)
        OR
        (DATEDIFF(MINUTE, B.StartTime, @currentTime) = SMS.Duration*60 AND SMS.DurationType=1 AND BeforeAfter=1)

Order BY B.Id

GO

СПИСОК ДЛЯ ДНЕЙ/НЕДЕЛЕЙ/МЕСЯЦЕВ - RUN Каждый день Утро Однажды

DECLARE @currentTime smalldatetime = '2014-07-12 08:00:00'

SELECT  B.Id AS BookingId, C.Id, C.Name,
        B.StartTime AS BookingStartTime,@currentTime As CurrentTime,  SMS.SMSText
FROM    Bookings B INNER JOIN
        SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
        LEFT JOIN Category C ON C.Id=B.CategoryId
WHERE   
    (((DATEDIFF(DAY, @currentTime, B.StartTime) = SMS.Duration AND SMS.DurationType=2)
    OR (DATEDIFF(DAY, @currentTime, B.StartTime) = SMS.Duration*7 AND SMS.DurationType=3)
    OR (DATEDIFF(DAY, @currentTime, B.StartTime) = SMS.Duration*30 AND SMS.DurationType=4))
     AND BeforeAfter=0)

    OR
    (((DATEDIFF(DAY, B.StartTime, @currentTime) = SMS.Duration AND SMS.DurationType=2)
    OR (DATEDIFF(DAY, @currentTime, B.StartTime) = SMS.Duration*7 AND SMS.DurationType=3)
    OR (DATEDIFF(DAY, @currentTime, B.StartTime) = SMS.Duration*30 AND SMS.DurationType=4))
     AND BeforeAfter=1)

Order BY B.Id

Ответ 2

Кажется, вы забыли выбрать несколько столбцов для возврата.

DECLARE @currentTime smalldatetime = '2014-07-12 09:15:00'; 

SELECT [col1], [col2], [etcetera]
FROM

Bookings B
INNER JOIN SMSConfiguration SMS ON SMS.CategoryId=B.CategoryId
WHERE DATEDIFF(hour,B.StartTime,@currentTime)=1

Ответ 3

Просто swap the dates in DATEDIFF функция

Измените DATEDIFF (час, B.StartTime, @currentTime) на DATEDIFF (час, @currentTime, B.StartTime) - только для HOUR

Поскольку ваш возвращает значение в Отрицательный (-1).

DECLARE @currentTime smalldatetime = '2014-07-12 09:15:00'

SELECT  B.Id AS BookingId, @currentTime AS CurrentTime,
        B.StartTime AS BookingStartTime,  SMS.SMSText
FROM    Bookings B INNER JOIN
        SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE   (SMS.CategoryId IS NOT NULL AND 
         DATEDIFF(HOUR, @currentTime, B.StartTime) = SMS.Duration) OR 
        (SMS.CategoryId IS NULL AND 
         (DATEDIFF(DAY, B.StartTime, @currentTime) = SMS.Duration OR 
          DATEDIFF(MONTH, B.StartTime, @currentTime) = SMS.Duration))

Ответ 4

Это работает

DECLARE @currentTime smalldatetime
set @currentTime= '2014-07-12 09:15:00'



SELECT B.CategoryId FROM
Bookings B
INNER JOIN SMSConfiguration SMS ON SMS.CategoryId=B.CategoryId
WHERE DATEDIFF(hh,B.StartTime,@currentTime)=1

В вашем скрипте sql были две проблемы.

1) вы не установили значение currentTime

2) в вашем операторе select были столбцы

Примечание. Я взял B.CategoryId как один из столбцов для извлечения записей, вы можете изменить его в соответствии с вашим требованием

Ответ 5

Вы можете использовать вложенные выражения CASE для достижения ваших результатов в одном запросе. попробуйте это,

    DECLARE @currentTime smalldatetime = '2014-07-12 11:15:00'





    SELECT  B.Id AS BookingId,
    SMS.Duration,
    B.StartTime AS BookingStartTime,
    @currentTime As CurrentTime,  
    SMS.SMSText
    FROM Bookings B INNER JOIN
    SMSConfiguration SMS 
    ON SMS.CategoryId = B.CategoryId 
    OR SMS.CategoryId IS NULL


    WHERE 
       SMS.Duration = 
       CASE 
       WHEN SMS.DurationType = 1 THEN  --'SMS CONFIGURED FOR HOURS BASIS'


           CASE WHEN BeforeAfter = 0 THEN 
                DATEDIFF(HOUR, @currentTime, B.StartTime)       
           ELSE 
                DATEDIFF(HOUR, B.StartTime, @currentTime)
           END 

       WHEN SMS.DurationType = 2 THEN  --'SMS CONFIGURED FOR DAY BASIS'


           CASE WHEN BeforeAfter = 0 THEN 
              DATEDIFF(DAY, @currentTime, B.StartTime)         
          ELSE 
              DATEDIFF(DAY, B.StartTime, @currentTime)
          END 

     WHEN SMS.DurationType = 3 THEN  --'SMS CONFIGURED FOR WEEK BASIS'


          CASE WHEN BeforeAfter = 0 THEN 
              DATEDIFF(DAY, @currentTime, B.StartTime)/7        
          ELSE 
              DATEDIFF(DAY, B.StartTime, @currentTime)/7 
          END 

      ELSE 
          CASE WHEN BeforeAfter = 0 THEN -- 'SMS CONFIGURED FOR MONTH BASIS'
              dbo.FullMonthsSeparation(@currentTime, B.StartTime)        
          ELSE 
              dbo.FullMonthsSeparation(B.StartTime, @currentTime) 
          END    

      END 

    ORDER BY BOOKINGID;

вы можете автоматизировать в двух расписаниях один для получения информации о бронировании часовых смс и других для ежедневных/еженедельных/ежемесячных данных бронирования.

Ответ 6

Try:

DECLARE @currentTime smalldatetime = '2014-07-12 09:15:00'

SELECT  B.Id AS BookingId, 
        B.StartTime AS BookingStartTime,  
        @currentTime CURRENT_DT,
        SMS.SMSText
FROM    Bookings B INNER JOIN
        SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE  (    SMS.DurationType = 1 
        AND @currentTime = DATEADD(HOUR, (2*SMS.BeforeAfter-1)*SMS.Duration, B.StartTime))
     OR 
       (    SMS.DurationType = 2
        AND @currentTime = DATEADD(DAY, (2*SMS.BeforeAfter-1)*SMS.Duration, B.StartTime))
     OR
       (    SMS.DurationType = 3
        AND @currentTime = DATEADD(WEEK, (2*SMS.BeforeAfter-1)*SMS.Duration, B.StartTime))
     OR
       (    SMS.DurationType = 4
        AND @currentTime = DATEADD(MONTH, (2*SMS.BeforeAfter-1)*SMS.Duration, B.StartTime))

2 * SMS.BeforeAfter-1 преобразует Before (0) в -1 и After (1) в 1. Таким образом, добавление или вычитание времени из ResStartTime

[изменить]

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

Вариант 1: изменить метаданные до и после

http://sqlfiddle.com/#!3/6e9e9/1

Вместо Before = 0 и After = 1 используйте Before = -1 и After = 1. Затем они могут использоваться для изменения направления любого смещения даты

Например:

INSERT INTO [dbo].[SMSConfiguration]
           ([CategoryId],[SMSText],[BeforeAfter],[Duration],[DurationType],[IsActive])
     VALUES
           (1,'Before 1 hour',-1,1,1,1)-- 

INSERT INTO [dbo].[SMSConfiguration]
           ([CategoryId],[SMSText],[BeforeAfter],[Duration],[DurationType],[IsActive])
     VALUES
           (NULL,'After 1 hour (no category id))',1,1,1,1)

Обновленный запрос:

DECLARE @currentTime smalldatetime = '2014-07-12 09:15:00'

SELECT  B.Id AS BookingId, 
        B.StartTime AS BookingStartTime,  
        @currentTime CURRENT_DT,
        SMS.SMSText
FROM    Bookings B INNER JOIN
        SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
WHERE  (    SMS.DurationType = 1 
        AND @currentTime = DATEADD(HOUR, SMS.BeforeAfter*SMS.Duration, B.StartTime))
     OR 
       (    SMS.DurationType = 2
        AND @currentTime = DATEADD(DAY, SMS.BeforeAfter*SMS.Duration, B.StartTime))
     OR
       (    SMS.DurationType = 3
        AND @currentTime = DATEADD(WEEK, SMS.BeforeAfter*SMS.Duration, B.StartTime))
     OR
       (    SMS.DurationType = 4
        AND @currentTime = DATEADD(MONTH, SMS.BeforeAfter*SMS.Duration, B.StartTime))

Вариант 2: Разрешить 30 дней = 1 месяц

http://sqlfiddle.com/#!3/808cd/1

В дополнение к изменению до/после, если 30-дневный период достаточно близок для представления месяца, то все типы продолжительности могут быть преобразованы в часы. Вопрос в том, насколько конкретными будут ваши клиенты в отношении точности. Если вы отключены на день или два в правиле "за 3 месяца до", я не думаю, что у вас будет много жалоб.

Итак, ваша таблица DurationType может выглядеть так:

CREATE TABLE [dbo].[DurationType](
[Id] [int] NOT NULL PRIMARY KEY,
[DurationType] VARCHAR(10) NOT NULL,
[HourConversion] [int] NOT NULL
)
GO

INSERT INTO [dbo].[DurationType]([Id],[DurationType],[HourConversion])
       VALUES(1,'Hours',1)
INSERT INTO [dbo].[DurationType]([Id],[DurationType],[HourConversion])
       VALUES(2,'Days',24)
INSERT INTO [dbo].[DurationType]([Id],[DurationType],[HourConversion])
       VALUES(3,'Weeks',24*7)
INSERT INTO [dbo].[DurationType]([Id],[DurationType],[HourConversion])
       VALUES(4,'Months',24*30)

С этими изменениями запрос может быть изменен на:

DECLARE @currentTime smalldatetime = '2014-07-12 09:15:00'

SELECT  B.Id AS BookingId, 
        B.StartTime AS BookingStartTime,  
        @currentTime CURRENT_DT,
        SMS.SMSText
FROM    Bookings B 
INNER JOIN
        SMSConfiguration SMS ON SMS.CategoryId = B.CategoryId OR SMS.CategoryId IS NULL
INNER JOIN
        DurationType DT ON DT.Id = SMS.DurationType
WHERE  @currentTime = DATEADD(HOUR, SMS.BeforeAfter*SMS.Duration*DT.HourConversion, B.StartTime)