【问题标题】:Get records based on Current Date and Configured Data根据当前日期和配置数据获取记录
【发布时间】:2014-07-07 11:12:54
【问题描述】:

我正在致力于在我的 Web 应用程序中自动化 SMS 发送部分。

SQL Fiddle Link

DurationType 表存储是否应按小时、天、周、月的间隔发送短信。在 SMSConfiguration 中引用

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

Bookings 表 包含原始 Bookings 数据。对于此预订,我需要根据配置发送 SMS。

短信配置。其中定义了发送自动 SMS 的配置。它可以在之前/之后发送。之前=0,之后=1。 DurationType can be Hours=1, Days=2, Weeks=3, Months=4

现在我需要根据短信配置集找出当前需要发送短信的预订列表。

尝试使用 UNION 的 SQL

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”等时也会继续返回天/周/月记录

我想要一个处理所有小时/天/周/月的查询 计算,我应该只在他们相遇时得到一次记录 正确的时间。否则我会一次又一次地发送短信 每 15 分钟匹配日/周/月记录。

应该考虑以下场景。

  1. 小时,如果预订是同一天上午 10:15,如果在配置的 1 小时之前,则为上午 9:15

  2. 天(24 小时差),如果预订是 10:15 A.M 第三天早上 10:15 A.M 如果在 SMSConfiguration 中配置 3 天后

  3. 比赛周。如果预订时间是今天(星期三)上午 10:15,那么 14 天后的上午 10:15(如果配置为 2 周后)。

  4. 月份也和上面一样。

【问题讨论】:

  • 在给定的小提琴链接中,我删除了终止字符 (;) ,将错误的别名(S 更改为 SMS)和交换 DATEDIFF 中的日期。效果很好

标签: sql sql-server sql-server-2008-r2 datediff


【解决方案1】:

试试简化版 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

【讨论】:

  • 这个想法看起来不错。但是您使用 30 天来计算月份。有 28/31 天的月份呢?
【解决方案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

【讨论】:

  • 对不起.. 已更新。我的实际问题是关于根据配置集检索数据。不要硬编码小时和值 1
  • 啊,这样更有意义 ;) 你能指定你想看到的结果吗?
【解决方案3】:

这行得通

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 fiddle 有两个问题

1) 你没有设置currentTime的值

2) 您的选择语句中有列

注意:我将B.CategoryId作为获取记录的列之一,您可以根据需要更改它

【讨论】:

  • 用预期结果更新了我的问题。核实。它不仅基于小时,还基于配置的月/日
【解决方案4】:

只需swap the dates in DATEDIFF函数

DATEDIFF(hour, B.StartTime, @currentTime) 更改为 DATEDIFF(hour, @currentTime, B.StartTime) - 仅适用于 HOUR em>

因为你的返回值 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))

【讨论】:

  • 它不工作。我尝试在小提琴中复制粘贴您的代码并得到错误Must declare the scalar variable "@currentTime"
  • 它只给出一条记录。在我的问题中查看我的预期结果。我想要基于配置的天/周/月的其他记录:(
  • 已更新...请立即查看
  • 检查这个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
  • 谢谢。但仍然是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 分钟运行一次,并找到匹配的记录发送短信
【解决方案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】:

    试试:

    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 = -1After = 1,而不是Before = 0After = 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)
    

    【讨论】:

    • 对于一次预订,我可以发送多条短信。比如前一小时、两小时后、一个月后、三个月后等等。你认为这个逻辑会成立吗?
    • 我认为我的最新编辑应该可以工作,但您需要更多数据来彻底测试它。如果将 Before 存储为 -1 并将 After 存储为 1 可能会更好。那么您就不必做额外的数学运算了。
    • 还有其他需要考虑的。 30天可以算1个月吗?如果是这样,则以小时为单位存储所有持续时间类型的持续时间:1 小时 = 1、1 天 = 24、1 周 = 24*7、1 个月 = 24*7*30。然后你不需要检查 DurationType;在所有情况下都只是 DATEADD(HOUR)
    • 什么是 2*SMS.BeforeAfter?二月只有 28 天,有些月有 31 天。这就是为什么我有一个 UD 功能
    • 我在答案底部解释了 2*SMS.BeforeAfter-1 。基本上,您需要花费预订时间并增加或减少时间。所以我们需要 -1*duration 用于 Before 和 1*duration 用于 After。您有 Before=0 和 After=1。您真正需要的是 Before=-1 和 After=1。我在之前的帖子中对此发表了评论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多