Я работаю над автоматизацией части отправки SMS в моем веб-приложении.
В таблице DurationType хранится, следует ли отправлять SMS-сообщения с интервалом в часы, дни, недели, месяцы. Упоминается в SMSConfiguration
CREATE TABLE [dbo].[DurationType](
[Id] [int] NOT NULL PRIMARY KEY,
[DurationType] VARCHAR(10) NOT NULL
)
Таблица Bookings Содержит исходные данные Bookings. Для этого бронирования мне нужно отправить SMS в зависимости от конфигурации.
Конфигурация SMS . Которая определяет конфигурацию для отправки автоматического SMS. Его можно отправить до / после. До = 0, После = 1. DurationType can be Hours=1, Days=2, Weeks=3, Months=4
Теперь мне нужно узнать список бронирований, для которых необходимо отправить 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)
Результат
Проблема:
Процедура SQL будет выполняться каждые 15 минут. Текущий запрос продолжает возвращать записи дней / недель / месяцев даже для текущего времени «2014-07-12 11:30:00», «2014-07-12 11:45:00» и т. Д.
Мне нужен один запрос, который заботится обо всех расчетах часов / дней / недель / месяцев, и я должен получать записи только один раз, когда они соответствуют правильному времени. В противном случае я буду отправлять sms снова и снова каждые 15 минут для совпадения записей дня / недели / месяца.
Следует учитывать следующие сценарии.
Час, при бронировании 10:15 утра в тот же день 9:15 утра, если настроено до 1 часа
День (разница в 24 часа), при бронировании в 10:15 утра 3-й день утром в 10:15 утра, если он настроен через 3 дня в SMSConfiguration
Неделя матчей. Если бронирование выполняется сегодня в 10:15 утра (среда), то после 14 дней утром в 10:15 утра, если оно настроено через 2 недели.
Месяц тоже такая же логика, как и выше.
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
Похоже, вы забыли выбрать несколько столбцов для возврата.
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
Это работает
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
в качестве одного из столбцов для получения записей, вы можете изменить его в соответствии с вашими требованиями.
Просто функция 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))
Must declare the scalar variable "@currentTime"
Rows 2, 4, 5, 8 should not be displayed in a result, as they are not meeting the criteria
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.
Вы можете использовать вложенные выражения 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;
Вы можете автоматизировать два расписания: одно для получения информации о бронировании для ежечасного смс, а другое для ежедневного / еженедельного / ежемесячного бронирования.
Пытаться:
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)
Похожие вопросы
Связанные вопросы
Новые вопросы
sql
Язык структурированных запросов (SQL) - это язык запросов к базам данных. Вопросы должны включать примеры кода, структуру таблицы, примеры данных и тег для используемой реализации СУБД (например, MySQL, PostgreSQL, Oracle, MS SQL Server, IBM DB2 и т. Д.). Если ваш вопрос относится исключительно к конкретной СУБД (использует определенные расширения / функции), используйте вместо этого тег этой СУБД. Ответы на вопросы, помеченные SQL, должны использовать стандарт ISO / IEC SQL.
;
), изменил неправильное имя псевдонима (S на SMS) и поменял местами даты в DATEDIFF. Это сработало хорошо