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

SQL Fiddle Link

В таблице DurationType хранится, следует ли отправлять SMS-сообщения с интервалом в часы, дни, недели, месяцы. Упоминается в SMSConfiguration

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

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

enter image description here

Конфигурация SMS . Которая определяет конфигурацию для отправки автоматического SMS. Его можно отправить до / после. До = 0, После = 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 минут для совпадения записей дня / недели / месяца.

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

  1. Час, при бронировании 10:15 утра в тот же день 9:15 утра, если настроено до 1 часа

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

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

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

4
Billa 7 Июл 2014 в 15:12
В данной ссылке скрипта я удалил символ завершения (;), изменил неправильное имя псевдонима (S на SMS) и поменял местами даты в DATEDIFF. Это сработало хорошо
 – 
Jesuraja
7 Июл 2014 в 15:52

6 ответов

Лучший ответ

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

СПИСОК ЧАСОВ - запускать каждые 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

СПИСОК ДНЕЙ / НЕДЕЛЬ / МЕСЯЦЕВ - запускать каждый день утром один раз

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
Murali Murugesan 14 Июл 2014 в 18:27
Идея выглядит хорошо. Но вы использовали 30 дней для расчета месяца. Как насчет месяца с 28/31 дня?
 – 
Billa
14 Июл 2014 в 18:40

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

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
0
Tristan 7 Июл 2014 в 15:19
Извините.. Обновлено. Мой фактический вопрос касается получения данных на основе набора конфигурации. Не использовать час жесткого кода и значение 1
 – 
Billa
7 Июл 2014 в 15:29
Ах, так логичнее ;) можете ли вы указать результаты, которые вы хотите увидеть?
 – 
Tristan
7 Июл 2014 в 15:31

Это работает

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) в вашем операторе выбора были столбцы

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

0
Sid M 7 Июл 2014 в 15:42
Обновил мой вопрос с ожидаемым результатом. Проверь это. Он основан не только на часах, но и на настроенных месяцах/днях.
 – 
Billa
7 Июл 2014 в 15:57

Просто функция 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))
0
Jesuraja 7 Июл 2014 в 18:27
Это не работает. Я попытался скопировать ваш код в скрипт и получил ошибку Must declare the scalar variable "@currentTime"
 – 
Billa
7 Июл 2014 в 15:56
Он дает только одну запись. Смотрите мой ожидаемый результат в моем вопросе. Я хочу настроить другие записи на основе дней/недель/месяцев :(
 – 
Billa
7 Июл 2014 в 17:02
Обновлено... проверьте сейчас
 – 
Jesuraja
7 Июл 2014 в 17:28
Проверьте это sqlfiddle.com/#!3/66e97/3. Вы правы на 50%. Но когда я добавляю еще одну запись в таблицу конфигурации и бронирования SMS, она показывает больше записей. Rows 2, 4, 5, 8 should not be displayed in a result, as they are not meeting the criteria
 – 
Billa
7 Июл 2014 в 18:14
Спасибо. Но все же Row #2 ->It should not be shown as it has more than one hour(1 day) и Row 5 -> Should not shown now. But it will be shown when the @currentTime is 2014-07-12 10:15:00. Надеюсь, вы получили мое требование. Процедура SQL будет запускаться каждые 15 минут и находить записи, соответствующие отправке SMS.
 – 
Billa
7 Июл 2014 в 18:36

Вы можете использовать вложенные выражения 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;

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

0
Senthil 11 Июл 2014 в 10:55

Пытаться:

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. Таким образом, добавляя или вычитая время из BookingStartTime

[Изменить]

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

Вариант 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)
0
CamperWill 12 Июл 2014 в 00:11
Для одного бронирования я могу отправить несколько смс. Например, за час до, через 2 часа, через 1 месяц, через 3 месяца и т. д. Как вы думаете, сработает ли эта логика?
 – 
Billa
11 Июл 2014 в 14:10
Я думаю, что мое последнее редактирование должно работать, но вам нужно больше данных, чтобы тщательно его протестировать. Может быть лучше, если вы сохраните «До» как -1 и «После» как 1. Тогда вам не придется выполнять дополнительную математику.
 – 
CamperWill
11 Июл 2014 в 14:45
Что-то еще рассмотреть. Можно ли считать 30 дней 1 месяцем? Если это так, сохраните продолжительность в часах для всех типов продолжительности: 1 час = 1, 1 день = 24, 1 неделя = 24*7, 1 месяц = ​​24*7*30. Тогда вам не нужно проверять DurationType; просто DATEADD(HOUR) во всех случаях
 – 
CamperWill
11 Июл 2014 в 14:50
Что такое 2*SMS.BeforeAfter? В феврале всего 28, а в некоторых месяцах 31 день. Вот почему у меня есть функция UD для этого
 – 
Billa
11 Июл 2014 в 15:25
Я объяснил 2*SMS.BeforeAfter-1 внизу своего ответа. По сути, вам нужно взять время бронирования и добавить или вычесть время. Итак, нам нужно -1*длительность для «До» и 1*длительность для «После». У вас есть До=0 и После=1. Что вам действительно нужно, так это «До=-1» и «После=1». Я прокомментировал это в предыдущем посте.
 – 
CamperWill
11 Июл 2014 в 20:48