【问题标题】:SQL select from multiple tables based on datetimeSQL 根据日期时间从多个表中选择
【发布时间】:2014-12-19 17:34:44
【问题描述】:

我正在编写一个脚本来分析 SQL Server 2008 数据库上数千个表中包含的一些数据。

为简单起见,可以将表分解为 4-8 个半相关表的组。半相关是指它们是同一项目的数据集合,但它们没有任何实际的 SQL 关系。每个表都包含一个日期时间戳(datetime2 数据类型)、值(可以是 bitintfloat,具体取决于特定项目)和其他一些当前不属于兴趣。日期时间戳在几秒钟内每 15 分钟(一刻钟)设置一次;但是,并非所有数据都被精确地同时记录...

例如:

表 1:

TIMESTAMP                 VALUE
2014-11-27 07:15:00.390      1
2014-11-27 07:30:00.390      0
2014-11-27 07:45:00.373      0
2014-11-27 08:00:00.327      0

表 2:

TIMESTAMP                 VALUE
2014-11-19 08:00:07.880      0
2014-11-19 08:15:06.867      0.0979999974370003
2014-11-19 08:30:08.593      0.0979999974370003
2014-11-19 08:45:07.397      0.0979999974370003

表3

TIMESTAMP                 VALUE
2014-11-27 07:15:00.390        0
2014-11-27 07:30:00.390        0
2014-11-27 07:45:00.373        1
2014-11-27 08:00:00.327        1

如您所见,并非所有表格都以相同的季度TIMESTAMP 开头。基本上,我所追求的是一个查询,该查询将从 3 个表中最早的 TIMESTAMP 开始,每 15 分钟间隔返回 3 个表中的每一个的 VALUE。对于给出的示例,我想从 2014-11-27 07:15 开始(不关心秒数......因此,需要允许时间戳为 +- 1 分钟左右)。当没有特定 TIMESTAMP 的记录时,为该值返回 NULL 是可以的。因此,我列出的示例的查询将返回如下内容:

TIMESTAMP                 VALUE1   VALUE2             VALUE3
2014-11-27 07:15           1    NULL                  0
2014-11-27 07:30           0    NULL                  0
2014-11-27 07:45           0    NULL                  1
2014-11-27 08:00           0    NULL                  1
...
2014-11-19 08:00           0         0                        1
2014-11-19 08:15           0         0.0979999974370003       0
2014-11-19 08:30           0         0.0979999974370003       0
2014-11-19 08:45           0         0.0979999974370003       0

我希望这是有道理的。任何帮助/指针/指导将不胜感激。

【问题讨论】:

    标签: sql sql-server join datetime2


    【解决方案1】:

    使用Full Outer Join

    SELECT COALESCE(a.[TIMESTAMP], b.[TIMESTAMP], c.[TIMESTAMP]) [TIMESTAMP],
           Isnull(Max(a.VALUE), 0)                               VALUE1,
           Max(b.VALUE)                                          VALUE2,
           Isnull(Max(c.VALUE), 0)                               VALUE3
    FROM   TABLE1 a
           FULL OUTER JOIN TABLE2 b
                        ON CONVERT(SMALLDATETIME, a.[TIMESTAMP]) = CONVERT(SMALLDATETIME, b.[TIMESTAMP])
           FULL OUTER JOIN TABLE3 c
                        ON CONVERT(SMALLDATETIME, a.[TIMESTAMP]) = CONVERT(SMALLDATETIME, c.[TIMESTAMP])
    GROUP  BY COALESCE(a.[TIMESTAMP], b.[TIMESTAMP], c.[TIMESTAMP])
    ORDER  BY [TIMESTAMP] DESC 
    

    【讨论】:

    • 时间戳有不同的值——它们不会用简单的 = 加入
    • @Hogan - 是的,你是对的,起初我没有明白你在说什么。更新了答案
    • 是的,已修复,我更喜欢我的答案(我认为它会更快),但这会起作用。
    • 谢谢!我一直在玩弄完整的外部连接,但一直在匹配日期方面遇到问题。感谢您的帮助
    • @mkolker - 如果任何一个答案满足您的需要,请将其标记为答案,这将有助于将来遇到类似问题的其他用户
    【解决方案2】:

    我要做的第一件事是将时间戳标准化为分钟。您可以通过更新现有列来做到这一点

    UPDATE TABLENAME
      SET TIMESTAMP = dateadd(minute,datediff(minute,0,TIMESTAMP),0)
    

    或在新列中

    ALTER TABLE TABLENAME ADD COLUMN NORMTIME DATETIME;
    
    UPDATE TABLENAME
      SET NORMTIME = dateadd(minute,datediff(minute,0,TIMESTAMP),0)
    

    有关地板日期的详细信息,请参阅此帖子:Floor a date in SQL server


    下一步是创建一个表,其中包含您希望看到的所有时间戳(标准化)——即每 15 个——每行一个。对于我的示例,让我们将此表称为 TIME_PERIOD 和列 EVENT_TIME(您可以随意称呼它)。

    有很多方法可以使这样的表递归CTE,ROW_NUMBER(),甚至是蛮力。我把这部分留给你。


    现在的问题是使用左连接和过滤有效值的简单选择,如下所示:

    SELECT TP.EVENT_TIME, a.VALUE as VALUE1, b.VALUE as VALUE2, c.VALUE as VALUE3
    FROM  TIME_PERIOD TP
    LEFT JOIN TABLE1 a ON a.[TIMESTAMP] = TP.EVENT_TIME
    LEFT JOIN TABLE2 b ON b.[TIMESTAMP] = TP.EVENT_TIME
    LEFT JOIN TABLE3 c ON c.[TIMESTAMP] = TP.EVENT_TIME
    WHERE COALESCE(a.[TIMESTAMP], b.[TIMESTAMP], c.[TIMESTAMP]) is not null
    ORDER  BY TP.EVENT_TIME DESC 
    

    如果它们是不同的类型,where 可能会变得更复杂,因此您可以始终使用它(虽然不如 coalesce,但始终可以使用):

    WHERE a.[TIMESTAMP] IS NOT NULL OR
          b.[TIMESTAMP] IS NOT NULL OR
          c.[TIMESTAMP] IS NOT NULL
    

    【讨论】:

      【解决方案3】:

      这是 NoDisplayName 答案的更新版本,可以满足您的需求。它适用于 SQL 2012,但您可以将 DATETIMEFROMPARTS 函数替换为一系列其他函数以获得相同的结果。

      ;WITH 
      NewT1 as (
      SELECT DATETimeFROMPARTS( DATEPART(year,Timestamp) , DATEPART(month,timestamp) , datepart(day,timestamp),datepart(hour,timestamp), datepart(minute,timestamp),0,0 ) as TimeStamp, Value
      FROM Table1),
      NewT2 as (
      SELECT DATETimeFROMPARTS( DATEPART(year,Timestamp) , DATEPART(month,timestamp) , datepart(day,timestamp),datepart(hour,timestamp), datepart(minute,timestamp),0,0 ) as TimeStamp, Value
      FROM Table2),
      NewT3 as (
      SELECT DATETimeFROMPARTS( DATEPART(year,Timestamp) , DATEPART(month,timestamp) , datepart(day,timestamp),datepart(hour,timestamp), datepart(minute,timestamp),0,0 ) as TimeStamp, Value
      FROM Table3)
      SELECT COALESCE(a.[TIMESTAMP], b.[TIMESTAMP], c.[TIMESTAMP]) [TIMESTAMPs],
             Isnull(Max(a.VALUE), 0)                            VALUE1,
             Isnull(Max(b.VALUE), 0)                                       VALUE2,
             Isnull(Max(c.VALUE), 0)                                       VALUE3
      FROM   NewT1 a
             FULL OUTER JOIN NewT2 b
                          ON a.[TIMESTAMP] = b.[TIMESTAMP]
             FULL OUTER JOIN TABLE3 c
                          ON a.[TIMESTAMP] = b.[TIMESTAMP]
      GROUP  BY COALESCE(a.[TIMESTAMP], b.[TIMESTAMP], c.[TIMESTAMP])
      ORDER  BY [TIMESTAMPs] 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-14
        • 2018-03-30
        • 1970-01-01
        相关资源
        最近更新 更多