【问题标题】:SQL for counting events by date按日期计算事件的 SQL
【发布时间】:2010-10-17 19:58:52
【问题描述】:

我觉得我以前看过这个问题,但是 SO 搜索和谷歌都没有帮助我......也许我只是不知道如何表达这个问题。我需要计算给定时间跨度内每天的事件数量(在本例中为登录),以便制作网站使用情况图表。到目前为止我的查询是这样的:

select 
   count(userid) as numlogins, 
   count(distinct userid) as numusers, 
   convert(varchar, entryts, 101) as date 
from 
   usagelog 
group by 
   convert(varchar, entryts, 101)

这完成了我需要的大部分工作(我在每个日期得到一行作为输出,其中包含该日期的登录总数和唯一用户数)。问题是,如果没有人在给定日期登录,则该日期的数据集中不会有一行。我希望它在行中添加指示这些日期的零登录。我可以想到两种方法来解决这个问题,但都没有让我觉得很优雅。

  1. 在结果集中添加一列,列出从周期开始到当前行日期之间的天数。在构建图表输出时,我将跟踪该值,如果下一行不等于当前行加一,则在图表中为缺失的每一天插入零。
  2. 创建一个“日期”表,其中包含感兴趣期间的所有日期并针对它进行外部联接。可悲的是,我正在开发的系统已经有一个用于此目的的表格,其中包含未来每个日期的一行......我不喜欢这样,我宁愿避免使用它,特别是因为table 用于系统的另一个模块,因此会引入对我当前正在开发的内容的依赖。

任何更好的解决方案或提示更好的谷歌搜索词?谢谢。

【问题讨论】:

    标签: sql logging date


    【解决方案1】:

    我最近不得不做同样的事情。这就是我在 T-SQL 中的做法( YMMV 的速度,但我发现它的性能足以超过一百万行事件数据):

    DECLARE @DaysTable TABLE ( [Year] INT, [Day] INT )
    
    DECLARE @StartDate DATETIME
    SET @StartDate = whatever
    
    WHILE (@StartDate <= GETDATE())
    BEGIN
    
      INSERT INTO @DaysTable ( [Year], [Day] )
      SELECT DATEPART(YEAR, @StartDate), DATEPART(DAYOFYEAR, @StartDate)
    
      SELECT @StartDate = DATEADD(DAY, 1, @StartDate)
    END
    
    -- This gives me a table of all days since whenever
    -- you could select @StartDate as the minimum date of your usage log)
    
    SELECT days.Year, days.Day, events.NumEvents
    FROM @DaysTable AS days
    LEFT JOIN (
      SELECT
        COUNT(*) AS NumEvents
        DATEPART(YEAR, LogDate) AS [Year],
        DATEPART(DAYOFYEAR, LogDate) AS [Day]
      FROM LogData
      GROUP BY
        DATEPART(YEAR, LogDate),
        DATEPART(DAYOFYEAR, LogDate)
    ) AS events ON days.Year = events.Year AND days.Day = events.Day
    

    【讨论】:

      【解决方案2】:
      WITH q(n) AS
                (
                SELECT  0
                UNION   ALL
                SELECT  n + 1
                FROM    q
                WHERE   n < 99
                ),
          qq(n) AS 
                (
                SELECT  0
                UNION   ALL
                SELECT  n + 1
                FROM    q
                WHERE   n < 99
                ),
          dates AS
                (
                SELECT  q.n * 100 + qq.n AS ndate
                FROM    q, qq
                )
      SELECT    COUNT(userid) as numlogins,
                COUNT(DISTINCT userid) as numusers,
                CAST('2000-01-01' + ndate AS DATETIME) as date
      FROM      dates
      LEFT JOIN
                usagelog
      ON        entryts >= CAST('2000-01-01' AS DATETIME) + ndate
                AND entryts < CAST('2000-01-01' AS DATETIME) + ndate + 1
      GROUP BY
                ndate
      

      这将选择最多10,000 即时构建的日期,这应该足够30 年。

      SQL Server 每个CTE 的递归限制为100,这就是为什么内部查询每个都可以返回100 行。

      如果您需要更多 10,000,只需添加第三个 CTE qqq(n) 并在 dates 中交叉加入。

      【讨论】:

      • SQL Server 没有每个 CTE 100 行的限制。我认为它在 CTE 中的递归限制为 100 次,但这是非常不同的。
      • 刚刚检查过,实际上 DEFAULT 限制是 100 次递归。您可以使用 MAXRECURSION 将其设置为高达 32,767
      【解决方案3】:

      选项 1 您可以创建一个临时表并使用范围插入日期,并使用使用日志进行左外连接 选项 2 您可以在评估结果集以生成最终输出时以编程方式插入缺失的日期

      【讨论】:

        【解决方案4】:

        坦率地说,我会在构建最终输出时以编程方式执行此操作。您实际上是在尝试从数据库中读取不存在的内容(几天内没有数据的数据)。 SQL 并不真正适用于那种事情。

        但是,如果您真的想这样做,“日期”表似乎是您的最佳选择。为了使它更好一点,您可以使用 DB 的日期函数和派生表即时生成它。

        【讨论】:

        • 我最终在我的应用程序中使用了现有的日期表,并且只是容忍了它引入的依赖关系。这是最快的解决方案。
        【解决方案5】:

        我通常使用的策略是与查询相反的 UNION,通常是为不存在的行检索数据的查询。

        如果我想获得一门课程的平均分,但有些课程没有被任何学生选修,我需要将那些没有被任何人选修的课程联合起来,以便为每个课程显示一行:

        SELECT AVG(mark), course FROM `marks` 
            UNION
        SELECT NULL, course FROM courses WHERE course NOT IN
            (SELECT course FROM marks)
        

        您的查询会更复杂,但同样的原则应该适用。您可能确实需要一个日期表来进行第二次查询

        【讨论】:

          【解决方案6】:

          创建一个内存表(一个表变量),您可以在其中插入日期范围,然后根据它外部连接登录表。按开始日期分组,然后您可以执行聚合和计算。

          【讨论】:

            猜你喜欢
            • 2021-05-06
            • 1970-01-01
            • 1970-01-01
            • 2017-07-12
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-05-08
            • 1970-01-01
            相关资源
            最近更新 更多