【问题标题】:COUNT total by weekly and monthly SQL按每周和每月 SQL 统计的总计
【发布时间】:2016-07-09 06:28:34
【问题描述】:

我有一张表,我必须在其中按每天、每周和每月计算分配给每个用户的总行数。

已发行桌书

BOOKID    ISSUEDUSER    DATE
 1            A        20160708
 2            A        20160709
 3            A        20160708
 4            A        20150102
 5            B        20160709
 6            C        20160708
 7            C        20160708

现在我必须计算每天、每周和每月发给每个用户的书籍

每天是今天 (20160709) 每周是周日到周六 每月是整月

结果应该是

ISSUEDUSER    DAILYBOOKS WEEKLYBOOKS  MONTHLYBOOKS
  A               1       3           3
  B               1       1           1
  C               0       2           2

我已经为每日发布完成了这个 SQL

SELECT ISSUEDUSER, COUNT(BOOKID) AS DAILYBOOKS
FROM BOOKSISSUED
WHERE DATE = CONVERT(VARCHAR(11), SYSDATETIME(), 112)
GROUP BY ISSUEDUSER

有人可以帮我写一个组合 SQL吗?

谢谢

艾登

【问题讨论】:

  • 您的表已将日期存储为整数......哇,他们没有考虑。 DATE 无论如何都没有默认格式.... :/
  • 如何知道他们是否有每日图书出租? BookID 是否每天重复?或者你只是用现在来计算每天有多少书(一周以内的)和每周的书(几周内的?)和月?对您想看到的内容有点困惑。
  • 书籍应仅按当前日、当前周(周日至周六)和当前月计算。 Previousd 和任何其他不应计算和显示,ID 不重复。
  • 如您稍后的评论中或者您是否只是使用现在来计算每天有多少书籍(一周以下的书籍)。仅当周和当月。我只是不想数一下发行了多少。
  • 看看我的酷回答。 :) DATEPART 是一头野兽。

标签: sql sql-server database


【解决方案1】:

您可能需要添加 WHERE 子句以仅检索当前月份的记录

SELECT ISSUEDUSER, 
       SUM(CASE WHEN DATE = DATEADD(DAY, DATEDIFF(DAY, 0, SYSDATETIME()), 0))
                THEN 1 ELSE 0 END) AS DAILYBOOKS,
       SUM(CASE WHEN DATE >= DATEADD(WEEK, DATEDIFF(WEEK, 0, SYSDATETIME()), 0)
                AND  DATE <  DATEADD(WEEK, DATEDIFF(WEEK, 0, SYSDATETIME()) + 1, 0)
                THEN 1 ELSE 0 END) AS WEEKLYBOOKS,
       COUNT(*) AS MONTHLYBOOKS
FROM   BOOKSISSUED
WHERE  DATE >= DATEADD(MONTH, DATEDIFF(MONTH, 0, SYSDATETIME()), 0)
AND    DATE <  DATEADD(MONTH, DATEDIFF(MONTH, 0, SYSDATETIME()) + 1, 0)
GROUP BY ISSUEDUSER

编辑:对于 [DATE] 列是 INT

SELECT ISSUEDUSER, 
       SUM(CASE WHEN DATE = CONVERT(INT, CONVERT(VARCHAR(8), SYSDATETIME(), 112))
                THEN 1 ELSE 0 END) AS DAILYBOOKS,
       SUM(CASE WHEN DATE >= CONVERT(INT, CONVERT(VARCHAR(8), DATEADD(WEEK, DATEDIFF(WEEK, 0, SYSDATETIME()), 0), 112))
                AND  DATE <  CONVERT(INT, CONVERT(VARCHAR(8), DATEADD(WEEK, DATEDIFF(WEEK, 0, SYSDATETIME()) + 1, 0), 112))
                THEN 1 ELSE 0 END) AS WEEKLYBOOKS,
       COUNT(*) AS MONTHLYBOOKS
FROM   BOOKSISSUED
WHERE  DATE >= CONVERT(INT, CONVERT(VARCHAR(6), SYSDATETIME(), 112) + '01')
AND    DATE <  CONVERT(INT, CONVERT(VARCHAR(6), DATEADD(MONTH, 1, SYSDATETIME()), 112) + '01')
GROUP BY ISSUEDUSER

【讨论】:

  • 非常感谢!我现在将尝试并发表评论:)
  • 它说操作数类型冲突:日期与 int 不兼容。我想我需要一个演员表
  • SELECT CONVERT(DATE, SYSDATETIME()) 给出的日期为 2016-07-09,我必须使用 11 将其格式化为 yyyMMdd 格式
  • 您能告诉我在哪里添加 WHERE 以仅获取当前月份的记录吗?-
  • 编辑了查询。添加了WHERE 并将MONTHLYBOOKS 更改为COUNT(*)
【解决方案2】:
  • 您应该考虑投资一个合法的 Date_Time 表。

它使比较一周的正式开始和结束变得更加容易和实用。嘿,你甚至可以使用索引!

但是,还有另一种方法。如您所见,DATEPART 返回我们正在寻找的 ISO 月份和星期。

所以如果我们的年份是正确的,我们现在知道我们的界限在哪里,并且可以轻松地在 COUNT(&lt;column&gt;) 中使用 IIF(&lt;boolean_expression&gt;, &lt;true_expression&gt;, &lt;false_expression&gt;) 语句。 COUNT 忽略 NULLs,因此我们将 TRUE 设置为 1,将 FALSE 设置为 NULL。 :D

-- Note, I changed the column [Date] to [Dates]
DECLARE @Date INT
SET @Date = CAST(CAST(SYSDATETIME() AS VARCHAR(4) ) + '0101' AS INT)

SELECT  ISSUEDUSER--DATEPART(YYYY, CAST(Dates AS VARCHAR(10) ) )
      , COUNT( IIF(DATEDIFF(MM, CAST(Dates AS VARCHAR(10) ), GETDATE() ) = 0
           , 1
           , NULL) ) AS MONTHS
      , COUNT( IIF(DATEDIFF(WW, CAST(Dates AS VARCHAR(10) ), GETDATE() ) = 0
           , 1
           , NULL) ) AS Weeks
      , COUNT( IIF(DATEDIFF(DD, CAST(Dates AS VARCHAR(10) ), GETDATE() ) = 0
           , 1
           , NULL) ) AS Days
FROM    #BookReport
WHERE DATES >= @Date
GROUP BY ISSUEDUSER
--results    
ISSUEDUSER  MONTHS  Weeks   Days
A           3       3       1
B           1       1       1 
C           2       2       0

请注意,您可以通过调整boolean 语句来扩大允许的日期差异!无需额外编码。

还请注意,您的示例实际上只有一个日期与月份、周或日(一天内)不同,尽管在我的示例中,我要求 Days 与查询的日期相同看起来有点不同。

酷观察:

  • DATE 根据定义没有格式,DATEPART 可以从格式良好的 Datetime string 中猜测,因此没有理由双重转换您的 Date 列。但是,如果您的模式发生变化,您可能需要添加CONVERT。
  • DATEPART 为您提供公认的标准 (ISO) 月和周,这意味着此处不需要 Date_Time 表。 :)
  • DATEDIFF 是这里的魔力,它使您的 Boolean 语句非常易于使用。
  • 很漂亮,不是吗?
  • DATEPART 上的 MSDN 页面值得一看。

【讨论】:

  • 这个公式的唯一限制是Predicate 没有使用SARGable 参数。这可以很容易地修复,具体取决于数据库的大小。由你决定。
  • 解决得那么快。使用CAST( CAST(SYSDATETIME AS VARCHAR(4) ) + '0101' AS INT)。 SYSDATETIME 返回您要查找的年份,这使得剩下的事情变得简单。事实上,在进行查询之前,我会使用@variable 来嵌入这个值。有人说INDEX SEEK吗? :D
  • 感谢详细解答!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多