【问题标题】:Merging SQL Statements (mssql)合并 SQL 语句 (mssql)
【发布时间】:2014-09-29 04:11:09
【问题描述】:

我想合并这 2 条语句以获得 7 行(每天一条),并告诉我每天有多少事件和警报。如果什么都没有,我想为每一行(天)获取一个“NULL”值。

当前语句有效,但“事件”和“警报”位于不同的行,如果没有事件,我什么也得不到...

我想我可能会使用“WITH”子句,但我有点迷茫:S

SELECT 'events' as Type, 
CAST(extended_timestamp AS DATE) as DateField, 
count(*) as SumField 
FROM [dbauditor_repo].[dbo].[dbauditor_repo_events] 
WHERE extended_timestamp > (select DATEADD(day, DATEDIFF(day, 0, GETDATE())-7, 0)) 
GROUP BY CAST(extended_timestamp AS DATE) 
UNION 
SELECT 'alarms' as Type, 
CAST(extended_timestamp AS DATE) as DateField, count(*) as SumField 
FROM [dbauditor_repo].[dbo].[dbauditor_repo_events] 
WHERE extended_timestamp > (select DATEADD(day, DATEDIFF(day, 0, GETDATE())-7, 0)) 
AND returncode = 1 
GROUP BY CAST(extended_timestamp AS DATE) 
ORDER BY DateField

感谢您的帮助!

【问题讨论】:

  • 你要求把这个语句放在 CTE 中????

标签: sql sql-server sql-server-2008 tsql sql-server-2012


【解决方案1】:

根据您当前的查询,当事件的returncode 为 1 时,它也可以是警报,因此它对这两个计数都有贡献。

此查询将在同一行返回两个计数:

SELECT
  x.DateField,
  Events = COUNT(*),
  Alarms = COUNT(CASE e.returncode WHEN 1 THEN 1 END)
FROM
  [dbauditor_repo].[dbo].[dbauditor_repo_events] AS e
CROSS APPLY
  (SELECT CAST(e.extended_timestamp AS DATE)) AS x (DateField)
WHERE
  e.extended_timestamp > DATEADD(day, DATEDIFF(day, 0, GETDATE())-7, 0)
GROUP BY
  x.DateField
;

Alarms 表达式使用条件聚合:它只在 e.returncode 匹配 1 时计算行数,而 Events 只是“计算所有行数”。

上述查询的唯一问题是它只会返回表中表示的日期的数据。要根据需要返回整个间隔的结果集,可以使用如下日历表:

SELECT
  DateField = c.Date,
  Events    = COUNT(*),
  Alarms    = COUNT(CASE returncode WHEN 1 THEN 1 END)
FROM
  [dbauditor_repo].[dbo].[dbauditor_repo_events] AS e
CROSS APPLY
  (SELECT CAST(e.extended_timestamp AS DATE)) AS x (DateField)
RIGHT JOIN
  dbo.Calendar AS c ON c.Date = x.DateField
                   AND e.extended_timestamp > DATEADD(day, DATEDIFF(day, 0, GETDATE())-7, 0)
GROUP BY
  c.Date
;

如果您没有日历表,则可以轻松地在 7 天之类的短时间内“即时”创建它,例如使用以下方法:

SELECT
  Date = DATEADD(DAY, DayNumber, StartingDate)
FROM
  (VALUES (0), (1), (2), (3), (4), (5), (6)) AS v (DayNumber)
CROSS APPLY
  (SELECT DATEADD(DAY, -7, CAST(GETDATE() AS date))) AS x (StartingDate)
;

所以,结合前面的查询,你会得到这样的结果:

WITH Calendar AS (
  SELECT
    Date = DATEADD(DAY, DayNumber, StartingDate)
  FROM
    (VALUES (0), (1), (2), (3), (4), (5), (6)) AS v (DayNumber)
  CROSS APPLY
    (SELECT DATEADD(DAY, -7, CAST(GETDATE() AS date))) AS x (StartingDate)
)
SELECT
  DateField = c.Date,
  Events    = COUNT(*),
  Alarms    = COUNT(CASE returncode WHEN 1 THEN 1 END)
FROM
  [dbauditor_repo].[dbo].[dbauditor_repo_events] AS e
CROSS APPLY
  (SELECT CAST(e.extended_timestamp AS DATE)) AS x (DateField)
RIGHT JOIN
  Calendar AS c ON c.Date = x.DateField
               AND e.extended_timestamp > DATEADD(day, DATEDIFF(day, 0, GETDATE())-7, 0)
GROUP BY
  c.Date
;

【讨论】:

  • 太棒了!非常感谢:-)
【解决方案2】:

您最初的问题是如何合并到语句或不同的数据集。

答案是您需要找到一些东西将它们连接在一起。 UNION 会将结果合并为您发现的单独的行。

我认为这会满足您的需求,这会按 DATE 对每个单独的查询进行分组,然后在同一 DATE 上加入以显示您正在寻找的结果。

select a.DateField, a.events, b.alarms FROM 
(select CAST(extended_timestamp AS DATE) as DateField, count(*) as [events]
   FROM [dbauditor_repo].[dbo].[dbauditor_repo_events] 
   WHERE extended_timestamp > DATEADD(day,-7, getdate()) 
   GROUP BY CAST(extended_timestamp AS DATE)
) a
,(select CAST(extended_timestamp AS DATE) as DateField, count(*) as [alarms]
   FROM [dbauditor_repo].[dbo].[dbauditor_repo_events] 
   WHERE extended_timestamp > DATEADD(day, -7, getdate()) AND returncode = 1 
   GROUP BY CAST(extended_timestamp AS DATE) 
) b
where a.DateField = b.DateField
ORDER BY a.DateField

但话又说回来,在查看您的陈述后,看起来“警报”只是一种事件,所以我认为您可以在您的情况下选择一个选项来做到这一点:

SELECT DateField, max([events]) as [events], max([alarms]) as [alarms] FROM
(select CAST(extended_timestamp AS DATE) as DateField, count(*) as [events], 
       SUM(case when returncode = 1 as 1 else 0 end) as [alarms]
  FROM [dbauditor_repo].[dbo].[dbauditor_repo_events] 
  WHERE extended_timestamp > DATEADD(day, -7, getdate()) 
  GROUP BY CAST(extended_timestamp AS DATE)
UNION
select CAST(getdate() as DATE), NULL, NULL
UNION
select CAST(DATEADD(day, -1, getdate()) as DATE), NULL, NULL
UNION
select CAST(DATEADD(day, -2, getdate()) as DATE), NULL, NULL
UNION
select CAST(DATEADD(day, -3, getdate()) as DATE), NULL, NULL
UNION
select CAST(DATEADD(day, -4, getdate()) as DATE), NULL, NULL
UNION
select CAST(DATEADD(day, -5, getdate()) as DATE), NULL, NULL
UNION
select CAST(DATEADD(day, -6, getdate()) as DATE), NULL, NULL
) a
GROUP BY DateField
ORDER BY DateField

【讨论】:

  • 这些陈述没有给出任何结果。
  • 您之前提出的建议在最近 7 天内对我来说是完美的(日期、事件编号、警报编号)问题是您对 dbo.Numbers 使用了哪个确切的陈述 :-)
  • 那么您在过去 7 天内不得有任何数据。这些是您在问题中提供的相同查询。
【解决方案3】:

根据我对要求的误解,我们进行了多次修改后,我想到我上面回答中的 SQL 真的很愚蠢。它完成了工作,但一旦正确理解,就会有一个更清晰的版本:

declare @events table (ID int, extended_timestamp datetime, returncode int)
insert into @events values (1, dateadd(day, -6, GETDATE()), 0), (2, dateadd(day, -6, GETDATE()), 1), (3, dateadd(day, -6, GETDATE()), 1), (4, dateadd(day, -5, GETDATE()), 1);

select cast(DATEADD(day, n.N, 0) as Date) as DateField
     , NULLIF(SUM(CASE WHEN returncode IS NOT NULL THEN 1 ELSE 0 END),0) as [events]
     , NULLIF(SUM(CASE WHEN returncode = 1 THEN 1 ELSE 0 END),0) as [alarms]
from dbo.Numbers n
     left outer join @events on n.N = DATEDIFF(day, 0, extended_timestamp)
where n.N between DATEDIFF(day, 0, GETDATE()) - 6 and DATEDIFF(day, 0, GETDATE())
group by n.N
order by 1

结果是一样的:

DateField  events      alarms
---------- ----------- -----------
2014-07-31 3           2
2014-08-01 1           1
2014-08-02 NULL        NULL
2014-08-03 NULL        NULL
2014-08-04 NULL        NULL
2014-08-05 NULL        NULL
2014-08-06 NULL        NULL

【讨论】:

    【解决方案4】:

    在不知道表格背后的架构的情况下,并假设returncode = 1 是表示警报的原因:

    SELECT IIF(returncode = 1, 'alarms', 'events') as [Type]
         , CAST(extended_timestamp AS DATE) as DateField
         , count(*) as SumField 
    FROM [dbauditor_repo].[dbo].[dbauditor_repo_events] 
    WHERE extended_timestamp > DATEADD(day, DATEDIFF(day, 0, GETDATE())-7, 0)
    GROUP BY IIF(returncode = 1, 'alarms', 'events'), CAST(extended_timestamp AS DATE) 
    ORDER BY DateField
    

    更新

    抱歉,我似乎没有正确阅读您的问题。您需要将我上面的原始答案与PIVOT 相结合,以展平结果集并获得您的 3 列。

    CTE 几乎相同 - 它获取数据集并计算其 Type。之后,我们使用PIVOT 将每种类型的每天计数放入其自己的列中。

    declare @events table (ID int, extended_timestamp datetime, returncode int)
    insert into @events values (1, dateadd(day, -6, GETDATE()), 0), (2, dateadd(day, -6, GETDATE()), 1), (3, dateadd(day, -6, GETDATE()), 1), (4, dateadd(day, -5, GETDATE()), 1);
    
    WITH CTE AS(
        SELECT IIF(returncode = 1, 'alarms', 'events') as [Type]
             , DATEDIFF(day, 0, extended_timestamp) as DayNumber
             , count(*) as SumField 
        FROM @events
        WHERE extended_timestamp >= DATEADD(day, DATEDIFF(day, 0, GETDATE())-6, 0)
        GROUP BY IIF(returncode = 1, 'alarms', 'events'), DATEDIFF(day, 0, extended_timestamp)
    )
    , CTE2 AS(
        select DayNumber, [events] + [alarms] as [events], [alarms]
        from
        (select SumField, DayNumber, [Type] from CTE) as _S
        PIVOT (
            SUM(SumField)
            FOR [Type] IN ([events], [alarms])
        ) as _P
    )
    select cast(DATEADD(day, n.N, 0) as Date) as DateField, [events], [alarms]
    from dbo.Numbers n
         left outer join CTE2 on n.N = DayNumber
    where n.N between DATEDIFF(day, 0, GETDATE()) - 6 and DATEDIFF(day, 0, GETDATE())
    order by 1
    

    这会返回:

    DateField  events      alarms
    ---------- ----------- -----------
    2014-07-31 3           2
    2014-08-01 1           1
    2014-08-02 NULL        NULL
    2014-08-03 NULL        NULL
    2014-08-04 NULL        NULL
    2014-08-05 NULL        NULL
    2014-08-06 NULL        NULL
    

    这也使用 Numbers table 来获得“每天 1 行,不管”要求。请注意,查询已更改为使用 DayNumber,以便此连接尽可能干净。

    【讨论】:

    • 首先,非常感谢您的宝贵帮助!你所有的假设都是正确的。我确实以更优化的方式得到了与第一个语句相同的东西,这是一个非常好的观点:-) 但是,背后的想法是每行获取 3 个字段(天、事件、警报),并且可能为 NULL 值列事件、警报。目前,我仍然每天收到 2 行,如果没有条目,我什至没有。有什么想法吗?
    • 顺便说一下,事件包括每个返回码、警报,只有返回码=1的那些
    • 哇哦,这似乎好多了:-) 但是,我应该在哪里设置表名(dbauditor_repo_events)?
    • 只需将其替换为@events。我包含了 table 变量,以便可以轻松复制它。
    • 对不起,伙计,我又回来了。我正在使用您之前提供的代码,但我确实注意到事件数字不正确。一个事件对我来说包括一切(包括警报):因此,对于 2014-07-31,我应该得到 3 个事件而不是 1 个......
    猜你喜欢
    • 1970-01-01
    • 2011-07-05
    • 2011-02-08
    • 2018-08-28
    • 2013-05-11
    • 2010-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多