【问题标题】:Counts on Date Range and Individual Days计算日期范围和个别天数
【发布时间】:2013-03-29 19:28:57
【问题描述】:

我不得不重新编写一个项目,该项目是在 ColdFusion 中使用 SQL 查询和 Query-of-Queries 组合完成的。有几十个查询引用了原始 SQL 查询结果集,但它没有被抽象为适用于不同的事件。所以我想通过将大部分计数转移到 SQL 中来改进它。我得到了他们需要工作的前 6 个计数(不确定是否以最佳方式)。但是,除此之外,我不仅需要对整个日期范围进行细分,而且还需要对该日期范围内的每一天进行细分,以获取唯一计数。

目前的查询是:

 SELECT Count(CASE
           WHEN type IN ( 1, 3, 4, 5, 9 ) THEN barcode
           ELSE NULL
         END)              AS total_scans,
   Count(CASE
           WHEN type IN ( 2, 8 ) THEN barcode
           ELSE NULL
         END)              AS total_creds,
   Count(barcode)          AS total_scans,
   Count(DISTINCT CASE
                    WHEN type IN ( 1, 3, 4, 5, 9 ) THEN barcode
                    ELSE NULL
                  END)     AS unique_scans,
   Count(DISTINCT CASE
                    WHEN type IN ( 2, 8 ) THEN barcode
                    ELSE NULL
                  END)     AS unique_creds,
   Count(DISTINCT barcode) AS unique_scans 
FROM   (SELECT c.id,
           a.barcode,
           d.type,
           c.location,
           Datepart(mm, a.scan_time)     AS scan_month,
           Datepart(dd, a.scan_time)     AS scan_day,
           Datepart(hour, a.scan_time)   AS scan_hour,
           Datepart(minute, a.scan_time) AS scan_min
    FROM   [scan_11pc_gate_entries] AS a
           INNER JOIN scan_units AS b
                   ON a.scanner = b.id
           INNER JOIN scan_gates AS c
                   ON b.gate = c.id
           INNER JOIN [scan_11pc_gate_allbarcodes] AS d
                   ON a.barcode = d.barcode
    WHERE  ( c.id IN (SELECT id
                      FROM   scan_gates
                      WHERE  ( event_id = 21 )) )
           AND ( a.valid IN ( 1, 8 ) )
           AND a.scan_time >= '20110808'
           AND a.scan_time <= '20110814') data  

【问题讨论】:

  • scan_11pc_gate_entries.scan_time的数据类型是什么?您是否发现像abc 等无意义的别名可读?
  • 获取核心数据的内部查询不是我写的,数据类型是datetime。我想我也可以重写内部查询,但我真的只需要计数
  • 嗯,按天获取计数的最简单方法至少需要对内部查询稍作更改,除非您想按这些分隔的日/月/年列进行分组。你试过我的答案了吗?

标签: sql-server sql-server-2005 count date-range


【解决方案1】:

我看到这里还有很多其他改进/优化的空间,但要满足当前的需求:

 SELECT d, Count(CASE WHEN type IN ( 1, 3, 4, 5, 9 ) THEN barcode
           ELSE NULL END) AS total_scans,
   Count(CASE WHEN type IN ( 2, 8 ) THEN barcode
           ELSE NULL END) AS total_creds,
   Count(barcode)         AS total_scans,
   Count(DISTINCT CASE WHEN type IN ( 1, 3, 4, 5, 9 ) THEN barcode
           ELSE NULL END) AS unique_scans,
   Count(DISTINCT CASE WHEN type IN ( 2, 8 ) THEN barcode
           ELSE NULL END) AS unique_creds,
   Count(DISTINCT barcode) AS unique_scans 
FROM   (SELECT c.id, -- is this column necessary?
           a.barcode,
           d.type,
           c.location, -- is this column necessary?
           d = DATEADD(DAY, DATEDIFF(DAY, 0, a.scan_time), 0)
    FROM   [scan_11pc_gate_entries] AS a
           INNER JOIN scan_units AS b
                   ON a.scanner = b.id
           INNER JOIN scan_gates AS c
                   ON b.gate = c.id
                  AND c.event_id = 21 -- join criteria, 
                     -- shouldn't be an extra IN clause
           INNER JOIN [scan_11pc_gate_allbarcodes] AS d
                   ON a.barcode = d.barcode
    WHERE  ( a.valid IN ( 1, 8 ) )
           AND a.scan_time >= '20110808'
           AND a.scan_time <= '20110814') data  
GROUP BY d
ORDER BY d;

注意&gt;=&lt;=BETWEEN 相同,除非scan_timeDATE 列(或保证始终在午夜),否则您使用的方法不是安全的。最好说:

           AND a.scan_time >= '20110808'
           AND a.scan_time <  '20110815'

更多信息在这里:


编辑

了解这是基于我从 cmets 和问题中拼凑的信息,这与您的环境和工作负载的完整情况相去甚远,您可能会发现有用的索引视图是:

CREATE VIEW dbo.myview 
WITH SCHEMABINDING
AS
  SELECT c.event_id,
         a.barcode,
         [type] = CASE WHEN d.[type] IN (2,8) THEN 'c' ELSE 's' END,
         scan_date = DATEADD(DAY, DATEDIFF(DAY, 0, a.scan_time), 0),
         total = COUNT_BIG(*)
    FROM dbo.scan_11pc_gate_entries AS a
    INNER JOIN dbo.can_units AS b
            ON a.scanner = b.id
    INNER JOIN dbo.scan_gates AS c
            ON b.gate = c.id
    INNER JOIN dbo.scan_11pc_gate_allbarcodes AS d
            ON a.barcode = d.barcode
    WHERE (a.valid IN (1,8))
      AND d.[type] IN (2,3,4,5,8,9)
    GROUP BY 
      c.event_id,
      a.barcode,
      CASE WHEN d.[type] IN (2,8) THEN 'c' ELSE 's' END,
      DATEADD(DAY, DATEDIFF(DAY, 0, a.scan_time), 0);
GO
CREATE UNIQUE CLUSTERED INDEX x 
  ON dbo.myview(event_id, barcode, [type], scan_date);

现在您可以编写一个执行以下操作的查询:

SELECT [date] = CONVERT(CHAR(8), scan_date, 112), 
  SUM(CASE WHEN [type] = 's' THEN total ELSE 0 END) AS total_scans,
  SUM(CASE WHEN [type] = 'c' THEN total ELSE 0 END) AS total_creds,
  COUNT(CASE WHEN [type] = 's' THEN 1 END) AS unique_scans,
  COUNT(CASE WHEN [type] = 'c' THEN 1 END) AS unique_creds
FROM dbo.myview WITH (NOEXPAND) -- in case STD Edition
WHERE event_id = 21
  AND scan_date BETWEEN '20110808' AND '20110814'
GROUP BY scan_date
UNION ALL
SELECT [date] = 'weekly', 
  SUM(CASE WHEN [type] = 's' THEN total ELSE 0 END) AS total_scans,
  SUM(CASE WHEN [type] = 'c' THEN total ELSE 0 END) AS total_creds,
  COUNT(DISTINCT CASE WHEN [type] = 's' THEN barcode END) AS unique_scans,
  COUNT(DISTINCT CASE WHEN [type] = 'c' THEN barcode END) AS unique_creds
FROM dbo.myview WITH (NOEXPAND) -- in case STD Edition
WHERE event_id = 21
  AND scan_date BETWEEN '20110808' AND '20110814'
ORDER BY [date];

这一切都完全未经测试,因为您的架构尝试创建系统的完整复制品有点麻烦,但希望这能给您一个大致的工作思路......

【讨论】:

  • 感谢您对日期范围的帮助,我在写这篇文章之前阅读了您的文章,但我想我不太明白。我确实摆脱了用毫秒等放置字符串的旧方法。
  • 但我想我遗漏了一些东西,现在它显示“Msg 243, Level 16, State 1, Line 1 Type DATE is not a defined system type.”不过,这个解决方案似乎真的很聪明!
  • 你能告诉我们这会返回什么吗:SELECT SERVERPROPERTY('ProductVersion'), compatibility_level FROM sys.databases WHERE database_id = DB_ID();
  • 哦,我想我看到了一半的问题,我认为我们的开发环境中的 SQL 还没有升级到 2005 年以后。
  • 您的开发环境落后于生产环境的 3 个主要版本?这不好。
猜你喜欢
  • 2011-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多