【问题标题】:Filling Gaps in Dates Returned from Database - pure SQL solution possible?填补从数据库返回的日期中的空白 - 纯 SQL 解决方案可能吗?
【发布时间】:2011-02-20 19:21:18
【问题描述】:

我有这个问题:

SELECT COUNT(*) as clicks, DATE_FORMAT(FROM_UNIXTIME(click_date), '%w %M %Y') as point 
FROM tracking 
WHERE click_date < $end_date AND click_date > $start_date 
GROUP BY DAY(FROM_UNIXTIME(click_date))

$start_date 是两周前,$end_date 是今天的日期。

我正在尝试查找特定日期范围内每天的所有点击次数。我还想包括没有点击的日子。由于我的数据库中自然没有这些条目,因此我需要将它们包括在内,如何在显示从开始日期到结束日期的所有日期的同时最好地做到这一点。这就是我目前所拥有的,这两周日期范围内有很多差距。

Array
(
    [0] => Array
        (
            [clicks] => 17
            [point] => 0 February 2011
        )

    [1] => Array
        (
            [clicks] => 3
            [point] => 1 February 2011
        )

    [2] => Array
        (
            [clicks] => 14
            [point] => 5 February 2011
        )

    [3] => Array
        (
            [clicks] => 1
            [point] => 1 February 2011
        )

    [4] => Array
        (
            [clicks] => 8
            [point] => 2 February 2011
        )

)

这可以通过纯 SQL 查询来完成,还是我必须使用一些 php 逻辑?

顺便说一句,为什么我的第一次约会是0 February 2011!嗯,我似乎也有重复的日期,这不应该发生,也许我的GROUP BY 工作不正常?

感谢大家的帮助。

【问题讨论】:

    标签: php sql mysql


    【解决方案1】:

    这可以通过纯 SQL 查询来完成,还是我必须使用一些 php 逻辑?

    是的,最好创建一个只包含数字 0 到 999 的数字表(单列 N)。它可以用于很多事情,尤其是像下面这样的查询:

    SELECT COUNT(t.click_date) as clicks,
        DATE_FORMAT(adddate($start_date, interval N day), '%d %M %Y') as point 
    FROM Numbers
    LEFT JOIN tracking t
        ON t.click_date >= adddate($start_date, interval N day)
        and t.click_date < adddate($start_date, interval (N+1) day)
    WHERE N between 0 and datediff($start_date, $end_date)
    GROUP BY N
    

    顺便说一句,为什么我的第一次约会是 2011 年 2 月 0 日

    您使用了错误的格式。对于星期几,大写的 W 不会小,所以 '%W %M %Y''%d %M %Y' 代表 day-of-月。 http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_date-format

    也许我的 GROUP BY 工作不正常?

    您正在使用 GROUP BY DAY(FROM_UNIXTIME(click_date)) 注意 "day" 不是工作日,但您正在显示(或尝试)"%W"(工作日) - 选择一个,不要混合它们。


    编辑:如果您不想具体化(创建为真实表)数字序列表,您可以即时构建一个。不会很漂亮

    注意:下面的 N1、N2 和 N3 组合得出的可能范围为 0-999

    SELECT COUNT(t.click_date) as clicks,
        DATE_FORMAT(adddate($start_date, interval N day), '%d %M %Y') as point 
    FROM (
        select N1 * 100 + N2 * 10 + N3 as N
        from (
        select 0 N1 union all select 1 union all select 2 union all
        select 3 union all select 4 union all select 5 union all
        select 6 union all select 7 union all
        select 8 union all select 9) N1
        cross join (
        select 0 N2 union all select 1 union all select 2 union all
        select 3 union all select 4 union all select 5 union all
        select 6 union all select 7 union all
        select 8 union all select 9) N2
        cross join (
        select 0 N3 union all select 1 union all select 2 union all
        select 3 union all select 4 union all select 5 union all
        select 6 union all select 7 union all
        select 8 union all select 9) N3
        ) Numbers
    LEFT JOIN tracking t
        ON t.click_date >= adddate($start_date, interval N day)
        and t.click_date < adddate($start_date, interval (N+1) day)
    WHERE N between 0 and datediff($start_date, $end_date)
    GROUP BY N
    

    编辑#2:直接日期表

    将它放在 phpMyAdmin 的新窗口中或作为批处理运行。它创建了一个名为 Dates 的表,每个日期都从 1900-01-01(或脚本中的更改)到 2300-01-01(或更改)。

    DROP PROCEDURE IF EXISTS FillDateTable;
    
    delimiter //
    CREATE PROCEDURE FillDateTable()
        LANGUAGE SQL
        NOT DETERMINISTIC
        CONTAINS SQL
        SQL SECURITY DEFINER
        COMMENT ''
    BEGIN
      drop table if exists datetable;
      create table datetable (thedate datetime primary key, isweekday smallint);
    
      SET @x := date('1900-01-01');
      REPEAT 
        insert into datetable (thedate, isweekday) SELECT @x, case when dayofweek(@x) in (1,7) then 0 else 1 end;
        SET @x := date_add(@x, interval 1 day);
        UNTIL @x > date('2300-01-01') END REPEAT;
    END//
    delimiter ;
    
    CALL FillDateTable;
    

    有了这样一个实用表,你的查询就可以了

    SELECT COUNT(t.click_date) as clicks,
        DATE_FORMAT(thedate, '%d %M %Y') as point 
    FROM Dates
    LEFT JOIN tracking t
        ON t.click_date >= thedate
        and t.click_date < adddate(thedate, interval 1 day)
    WHERE thedate between $start_date and $end_date
    GROUP BY thedate
    

    【讨论】:

    • 如果你打算去(轻微的)麻烦制作一个新表,在这种情况下,最好创建一个日历表。然后,您可以只对其进行外部联接,而不是对日期函数的所有调用。它可能也会运行得更快。
    • @Catcall - 多么真实。我在很多事情上都使用通用数字表,尽管我承认拥有数字以及日期表会更好。
    • 我很喜欢你的回答!我也对这个日期表感兴趣,它会如何工作?当然,不是日期表?!我将如何使用它?
    • 太棒了!现在我明白了它是如何工作的,它对我很有用。非常感谢理查德!
    • @Abs:我写了一个专门针对 PostgreSQL 的答案,但是语法很容易理解,而且这个想法是可移植的,虽然语句不是。见stackoverflow.com/questions/5030546/…
    【解决方案2】:

    在我看来,您最好在代码中执行这种类型的逻辑。但是如果你想用纯 SQL 来做,你可以构造一个查询来给你一天到下一天之间所有天的结果......通过插入临时表或内存表......然后离开将其加入您的结果中,这样无论当天是否有结果,您都可以获得所有的日子。

    【讨论】:

      【解决方案3】:

      我会坚持 PHP 逻辑,在最低日期和最高日期之间循环,并且每次递增一天。

      您可能可以在 SQL 中使用一些花哨的连接来完成它,但我什至不会开始考虑这种讨厌的事情!

      顺便说一句,%wis the day of the week,从 0=星期日开始。你可能想要%d

      【讨论】:

      • 啊,我明白了,我将使用%d。是的,我以为 SQL 会变得复杂!
      猜你喜欢
      • 2014-12-17
      • 1970-01-01
      • 1970-01-01
      • 2015-04-12
      • 1970-01-01
      • 2014-11-20
      • 2019-08-14
      • 2013-06-21
      • 2010-12-25
      相关资源
      最近更新 更多