获取当月的所有日期

ind*_*ago 7 mysql datetime date

我在 MySQL 中有一个查询,它通过获取当月内的所有记录来很好地为我服务;

SELECT date_field,val FROM MY_TABLE WHERE date_field>=(CURDATE()-INTERVAL 1 MONTH);
Run Code Online (Sandbox Code Playgroud)

上面的查询运行良好。所以如果这个月我们只有两个记录和 28 天,它只会带来两个记录。

date_field | val
========================
2015-02-08 | 567
2015-02-09 | 345
Run Code Online (Sandbox Code Playgroud)

但我希望返回的记录数与当月的天数完全相同。如果当月有 28 天并且只有两个记录,它应该带来;

date_field | val
========================
2015-02-01 | 0
2015-02-02 | 0
2015-02-03 | 0
2015-02-04 | 0
2015-02-05 | 0
2015-02-06 | 0
2015-02-07 | 0
2015-02-08 | 567
2015-02-09 | 345
2015-02-10 | 0
2015-02-11 | 0
2015-02-12 | 0
2015-02-13 | 0
2015-02-14 | 0
2015-02-15 | 0
2015-02-16 | 0
2015-02-17 | 0
2015-02-18 | 0
2015-02-19 | 0
2015-02-20 | 0
2015-02-21 | 0
2015-02-22 | 0
2015-02-23 | 0
2015-02-24 | 0
2015-02-25 | 0
2015-02-26 | 0
2015-02-27 | 0
2015-02-28 | 0
Run Code Online (Sandbox Code Playgroud)

如何修改我的查询以获得上述结果?

Rol*_*DBA 8

您可以创建当月的动态日期表

SELECT date_field
FROM
(
    SELECT
        MAKEDATE(YEAR(NOW()),1) +
        INTERVAL (MONTH(NOW())-1) MONTH +
        INTERVAL daynum DAY date_field
    FROM
    (
        SELECT t*10+u daynum
        FROM
            (SELECT 0 t UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) A,
            (SELECT 0 u UNION SELECT 1 UNION SELECT 2 UNION SELECT 3
            UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
            UNION SELECT 8 UNION SELECT 9) B
        ORDER BY daynum
    ) AA
) AAA
WHERE MONTH(date_field) = MONTH(NOW());
Run Code Online (Sandbox Code Playgroud)

注意:如果您按原样剪切并粘贴上述查询,它将为您生成整个月

然后,您 LEFT JOIN 到您的原始查询

SELECT
    AAA.date_field,
    IFNULL(BBB.val,0) val
FROM
(
    SELECT date_field
    FROM
    (
        SELECT MAKEDATE(YEAR(NOW()),1) +
        INTERVAL (MONTH(NOW())-1) MONTH +
        INTERVAL daynum DAY date_field
        FROM
        (
            SELECT t*10+u daynum FROM
            (SELECT 0 t UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) A,
            (SELECT 0 u UNION SELECT 1 UNION SELECT 2 UNION SELECT 3
            UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7
            UNION SELECT 8 UNION SELECT 9) B ORDER BY daynum
        ) AA
    ) AA WHERE MONTH(date_field) = MONTH(NOW())
) AAA LEFT JOIN (SELECT date_field,val FROM MY_TABLE) BBB
USING (date_field);
Run Code Online (Sandbox Code Playgroud)

试一试 !!!


ype*_*eᵀᴹ 6

首先,条件WHERE date_field >= (CURDATE()-INTERVAL 1 MONTH)不会将您的结果限制为当月。它将获取从 30-31 天前到当前日期(以及未来,如果表中有未来日期的行)的所有日期。

它应该是:

WHERE date_field >= LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
  AND date_field < LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY
Run Code Online (Sandbox Code Playgroud)

现在,对于主要问题,要创建 28-31 个日期,即使表中没有包含所有日期的行,您也可以使用Calendar表(包含所有日期,例如 1900 年到 2200 年)或动态创建它们,使用这样的东西(该days表可以是临时表,也可以将其设为派生表,查询比这更复杂一些):

CREATE TABLE days
( d INT NOT NULL PRIMARY KEY ) ;

INSERT INTO days
VALUES (0), (1), (2), ....
                  ..., (28), (29), (30) ;

SELECT 
    cal.my_date        AS date_field, 
    COALESCE(t.val, 0) AS val
FROM 
    ( SELECT 
          s.start_date + INTERVAL (days.d) DAY  AS my_date
      FROM 
          ( SELECT LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
                       AS start_date,
                   LAST_DAY(CURRENT_DATE) 
                       AS end_date
          ) AS s
          JOIN days  
              ON  days.d <= DATEDIFF(s.end_date, s.start_date)
    ) AS cal
    LEFT JOIN my_table AS t 
        ON  t.date_field >= cal.my_date 
        AND t.date_field  < cal.my_date + INTERVAL 1 DAY ; 
Run Code Online (Sandbox Code Playgroud)

以上应该适用于任何类型的date_field列(日期、日期时间、时间戳)。如果date_field列的类型为DATE,则最后一个连接可以简化为:

    LEFT JOIN my_table AS t 
        ON  t.date_field = cal.my_date ;
Run Code Online (Sandbox Code Playgroud)