Get records based on Current Date and Configured Data

微笑、不失礼 提交于 2019-12-10 09:32:13

问题


I am working on automating the SMS sending part in my web application.

SQL Fiddle Link

DurationType table stores whether the sms should be sent out an interval of Hours, Days, Weeks, Months. Referred in SMSConfiguration

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

Bookings table Contains the original Bookings data. For this booking I need to send the SMS based on configuration.

SMS Configuration. Which defines the configuration for sending automatic SMS. It can be send before/after. Before=0, After=1. DurationType can be Hours=1, Days=2, Weeks=3, Months=4

Now I need to find out the list of bookings that needs SMS to be sent out at the current time based on the SMS Configuration Set.

Tried SQL using 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)

Result

Problem:

The SQL procedure will be running every 15 mins. Current query keep returning days/weeks/months records even for the current time '2014-07-12 11:30:00', '2014-07-12 11:45:00', etc

I want a single query that takes care of all Hours/Days/Weeks/Months calculation and I should be get records only one time when they meet the correct time. Otherwise I will be sending sms again and again every 15 mins for day/week/months records matched.

It should consider the following scenarios.

  1. Hour, If booking is 10:15 A.M same day 9:15 A.M if it is before 1 hour configured

  2. Day(24 Hour difference), If booking is 10:15 A.M 3rd day morning 10:15 A.M if it is configured after 3 days in SMSConfiguration

  3. Match Week. If booking is 10:15 A.M today(Wednesday) morning then after 14 days morning 10:15 A.M if it is configured after 2 weeks.

  4. Month also same logic like above.


回答1:


Try the simplified version FIDDLE , removed Union and used OR conditions

LIST FOR HOURS - RUN Every 15 mins

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

LIST FOR DAYS/WEEKS/MONTHS - RUN Every Day Morning Once

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:


You seem to have forgotten to pick some columns to return.

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:


This works

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

There were two problems in your sql fiddle

1) you didn't set the value of currentTime

2) there were columns in your select statement

Note: I took B.CategoryId as one of the column to fetch records, you can change it as per your requirement




回答4:


Just swap the dates in DATEDIFF function

Change DATEDIFF(hour, B.StartTime, @currentTime) to DATEDIFF(hour, @currentTime, B.StartTime) - for HOUR only

Because yours one return the value in Negative (-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))



回答5:


You can use the nested CASE expressions for achieve your results in single query. please try this,

    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;

you can automate in two schedules one for getting booking details of Hourly sms and other one for Daily/Weekly/Monthly booking details.




回答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))

The 2*SMS.BeforeAfter-1 converts Before(0) to -1 and After(1) to 1. Thus, adding or subtracting time from the BookingStartTime

[Edit]

The above solution should work with your data model as is. Below I propose simplifying the solution by changing your data model.

Option 1: Change Before and After meta data

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

Instead of Before = 0 and After = 1, use Before = -1 and After = 1. Then these can be used to change the direction of any date offset

For example:

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)

Updated query:

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))

Option 2: Allow 30-days = 1 month

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

On top of the Before/After change, if a 30 day period is close enough to represent a month, then all duration types could be converted to hours. The question is how particular your customers will be in regard to precision. If you are off by a day or two on a "3 month before" rule, I don't think you will have many complaints.

So your DurationType table could look like this

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)

With these changes, the query could be modified to:

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)


来源:https://stackoverflow.com/questions/24609263/get-records-based-on-current-date-and-configured-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!