如何用一系列日期填充表格?

Posted

技术标签:

【中文标题】如何用一系列日期填充表格?【英文标题】:How to populate a table with a range of dates? 【发布时间】:2012-04-25 07:08:50 【问题描述】:

我需要一个 mysql 表来保存 2011 年 1 月 1 日到 2011 年 12 月 31 日之间的所有日期。我创建了一个表,其中一列名称为“_date”,输入 DATE。

我可以通过什么查询在表格中填充所有所需的日期(而不必手动输入)?

【问题讨论】:

而您只使用mysql?没有像 php 之类的? 为了回答你的问题,我可以访问 PHP,但是 (i) 真的不知道如何快速计算 PHP 范围内的所有日期和 (ii) 我猜 MySQL 可以效率高得多? @hjpotter92:如果您对两者都同样了解,那么在 PHP 中确实不容易。在 MySQL 中执行此操作效率更高。 Here is an example 使用一个简单的命令来生成所需范围内的日期,并将它们通过管道传输到 sqlite 进行导入。我想最后一部分适应mysql会很容易。 【参考方案1】:

试试这个:

DROP PROCEDURE IF EXISTS filldates;
DELIMITER |
CREATE PROCEDURE filldates(dateStart DATE, dateEnd DATE)
BEGIN
  WHILE dateStart <= dateEnd DO
    INSERT INTO tablename (_date) VALUES (dateStart);
    SET dateStart = date_add(dateStart, INTERVAL 1 DAY);
  END WHILE;
END;
|
DELIMITER ;
CALL filldates('2011-01-01','2011-12-31');

这里是使用它的 SQL Fiddle:http://sqlfiddle.com/#!2/65d13/1

按照Andrew Fox 的要求编辑(检查日期是否已存在)。

CREATE PROCEDURE filldates(dateStart DATE, dateEnd DATE)

BEGIN

DECLARE adate date;

    WHILE dateStart <= dateEnd DO

        SET adate = (SELECT mydate FROM MyDates WHERE mydate = dateStart);

        IF adate IS NULL THEN BEGIN

            INSERT INTO MyDates (mydate) VALUES (dateStart);

        END; END IF;

        SET dateStart = date_add(dateStart, INTERVAL 1 DAY);

    END WHILE;

END;//

这是使用它的 SQL Fiddle:http://sqlfiddle.com/#!2/66f86/1

【讨论】:

至少在我的服务器上不起作用。 alter routine command denied to user 'name'@'localhost' for routine 'name.filldates': DROP PROCEDURE IF EXISTS filldates 这次出现不同的错误:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER | CREATE PROCEDURE filldates(dateStart DATE, dateEnd DATE) BEGIN WHI' at line 1: DELIMITER | CREATE PROCEDURE filldates(dateStart DATE, dateEnd DATE) BEGIN WHILE dateStart &lt;= dateEnd 你在运行自己的mysql服务器吗?如果是这样,请尝试以 root 用户身份登录,甚至向您当前的用户帐户授予必要的权限。示例:GRANT ALL ON mydb.* TO 'someuser'@'somehost'; 更多信息:dev.mysql.com/doc/refman/5.1/en/… 致那些遇到语法错误的人。如果您通过 MySQL Workbench 连接到数据库并尝试运行创建过程脚本,您将收到语法错误。要运行此脚本,请右键单击“存储过程”菜单并选择“创建存储过程”,然后粘贴创建过程脚本,删除脚本末尾的“//”并点击“应用”。它对我有用。 Wana 给你双倍的分数!【参考方案2】:

如果您有一张表,其中包含足够大的连续 id 集,您可以使用 -

INSERT INTO tablename (_date)
SELECT '2011-01-01' + INTERVAL (id - 1) DAY
FROM some_table_with_lots_of_ids
WHERE id BETWEEN 1 AND 365

注意:但请注意,这可能会在闰年(有 366 天)期间给您带来麻烦

【讨论】:

【参考方案3】:

我发现这种粘贴式变体有效:

DROP PROCEDURE IF EXISTS FillCalendar;
DROP TABLE IF EXISTS calendar;
CREATE TABLE IF NOT EXISTS calendar(calendar_date DATE NOT NULL PRIMARY KEY);

DELIMITER $$
    CREATE PROCEDURE FillCalendar(start_date DATE, end_date DATE)
    BEGIN
    DECLARE crt_date DATE;
    SET crt_date = start_date;
    WHILE crt_date <= end_date DO
        INSERT IGNORE INTO calendar VALUES(crt_date);
        SET crt_date = ADDDATE(crt_date, INTERVAL 1 DAY);
    END WHILE;
    END$$
DELIMITER ;

CALL FillCalendar('2013-01-01', '2013-01-03');
CALL FillCalendar('2013-01-01', '2013-01-07');

【讨论】:

如果有人想要一些 pi: CALL FillCalendar('2013-01-01', '2099-01-04');从日历中选择计数(*); 31415【参考方案4】:

我不希望我的 SQL 查询需要外部依赖项(需要有一个日历表、使用日期填充临时表的过程等)。这个查询的最初想法来自 http://jeffgarretson.wordpress.com/2012/05/04/generating-a-range-of-dates-in-mysql/,我对其进行了轻微优化为了清晰和易于使用。

SELECT (CURDATE() - INTERVAL c.number DAY) AS date
FROM (SELECT singles + tens + hundreds number FROM 
( SELECT 0 singles
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
) singles JOIN 
(SELECT 0 tens
UNION ALL SELECT  10 UNION ALL SELECT  20 UNION ALL SELECT  30
UNION ALL SELECT  40 UNION ALL SELECT  50 UNION ALL SELECT  60
UNION ALL SELECT  70 UNION ALL SELECT  80 UNION ALL SELECT  90
) tens  JOIN 
(SELECT 0 hundreds
UNION ALL SELECT  100 UNION ALL SELECT  200 UNION ALL SELECT  300
UNION ALL SELECT  400 UNION ALL SELECT  500 UNION ALL SELECT  600
UNION ALL SELECT  700 UNION ALL SELECT  800 UNION ALL SELECT  900
) hundreds
ORDER BY number DESC) c  
WHERE c.number BETWEEN 0 and 364

优化和扩展此表以用于其他用途很简单。如果您只需要一周的数据,您可以轻松摆脱数十和数百张表。

如果您需要更大的数字集,添加千位表很容易。您只需要复制并粘贴数百个表格并添加一个零到九个数字。

【讨论】:

这是一个非常有趣的概念,在处理很多日期时有点慢,但是,确实不错。不过也许比其他替代品更快。 非常聪明。如果有人问自己:内部生成的c表选择0-9、10-90、100-900之间的总和,这是一个很大的区间,数字从0到999(总共1000个数字)。外部选择是选择从 0 到 364(即 365 天)的数字列表,并选择一年到今天的所有日期(CURDATE)。【参考方案5】:

感谢 IvanD。 我有一个更好的解决方案,它允许您创建指定的日历表。 例如,如果我试图创建一个 2014-04 的表,它看起来像这样:

SELECT (CURDATE() - INTERVAL c.number DAY) AS DATE
FROM 
(
    SELECT singles + tens + hundreds number FROM 
    ( 
        SELECT 0 singles
        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
    ) singles JOIN 
    (
        SELECT 0 tens
        UNION ALL SELECT  10 UNION ALL SELECT  20 UNION ALL SELECT  30
        UNION ALL SELECT  40 UNION ALL SELECT  50 UNION ALL SELECT  60
        UNION ALL SELECT  70 UNION ALL SELECT  80 UNION ALL SELECT  90
    ) tens  JOIN 
    (
        SELECT 0 hundreds
        UNION ALL SELECT  100 UNION ALL SELECT  200 UNION ALL SELECT  300
        UNION ALL SELECT  400 UNION ALL SELECT  500 UNION ALL SELECT  600
        UNION ALL SELECT  700 UNION ALL SELECT  800 UNION ALL SELECT  900
    ) hundreds
    ORDER BY number DESC
) c  
WHERE c.number BETWEEN 
DAYOFYEAR(NOW()) - DAYOFYEAR('2014-04-01')-  DAY(LAST_DAY('2014-04-01')) +1
AND 
DAYOFYEAR(NOW()) - DAYOFYEAR('2014-04-01')

【讨论】:

这不会返回table of 2014-04,而是在所选月份和当年中包含已经过去的天数的表格!【参考方案6】:

受到 IvanD 的大量加入的启发,我来到了这里:

SELECT DATE_ADD('2015-10-21', INTERVAL c.number DAY)    AS DATE
FROM 
(
    SELECT singles + tens + hundreds+thousands number FROM 
    ( 
        SELECT 0 singles
        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
    ) singles JOIN 
    (
        SELECT 0 tens
        UNION ALL SELECT  10 UNION ALL SELECT  20 UNION ALL SELECT  30
        UNION ALL SELECT  40 UNION ALL SELECT  50 UNION ALL SELECT  60
        UNION ALL SELECT  70 UNION ALL SELECT  80 UNION ALL SELECT  90
    ) tens  JOIN 
    (
        SELECT 0 hundreds
        UNION ALL SELECT  100 UNION ALL SELECT  200 UNION ALL SELECT  300
        UNION ALL SELECT  400 UNION ALL SELECT  500 UNION ALL SELECT  600
        UNION ALL SELECT  700 UNION ALL SELECT  800 UNION ALL SELECT  900
    ) hundreds
     JOIN 
    (
        SELECT 0 thousands
        UNION ALL SELECT  1000 UNION ALL SELECT  2000 UNION ALL SELECT  3000
        UNION ALL SELECT  4000 UNION ALL SELECT  5000 UNION ALL SELECT  6000
        UNION ALL SELECT  7000 UNION ALL SELECT  8000 UNION ALL SELECT  9000
    ) thousands
    ORDER BY number DESC
) c  
WHERE c.number BETWEEN 
0 
AND
DATEDIFF('2016-10-08', '2015-10-21')

【讨论】:

@Leniel Macaferi,你能帮忙***.com/questions/45490612/…【参考方案7】:

这可以通过使用简单的 for 循环在 PHP 中实现。有几种方法可以做到这一点。一种方法是将原始日期放在一个变量中,并通过在每个循环上添加 +1 天让循环每天运行它,例如,您将从 01/01/2011 开始,然后循环将第一次添加 0,下一次添加 1 天,然后是 2 天,依此类推,以此类推到 $i 变量。然后您可以打印出日期或将它们添加到您的数据库中。在这种情况下,$i 将代表计数器,以 0 为起点,

date('Ymd' 将日期转换为 yyyy-mm-dd。使用大写 Y 为您提供完整的 4 位数年份,而使用小写 y 将为您提供年份的最后 2 位数。您要保留它按此顺序将其添加到 mySQL 中的日期字段中。

strtotime($originalDate 将日期解析为 Unix 时间戳,."+".$i."day") 基本上将 $i 的值(以天为单位)添加到日期。

最后是 mysqli 查询。 $db 代表数据库连接变量,这将需要更改为您为连接设置的任何变量。接下来是实际的查询。只需将单词 table 替换为您的表名,并将 VALUES 之前的日期替换为您的日期行名,您就可以开始了。

以下是一个例子:

<?php
for($i=0;$i<=365;$i++) 
$originalDate = "01/01/2011";
$date = date('Y-m-d',strtotime($originalDate . "+".$i." day"));
mysqli_query($db, "INSERT INTO table (date)VALUES('$date')");

使用 for 函数实现此目的的另一种方法是将 strtotime 日期直接包含在 for 操作中,作为对 counter 变量的反对,这是一段更短的代码。将 $i=0 (起始计数器点)替换为起始日点,然后使用小于或等于结束日点(循环数),然后最后将您的 +1 放在第一个语句中变量可供使用。

最后,将日期转换为 Y-m-d 格式,准备好放入数据库并运行查询。

同样,与第一个示例一样,可以将其打印出来或直接放入您的数据库中。

以下是一个例子:

<?php
for ($startdate = strtotime("2011-01-01"); $startdate <= strtotime("2011-12-31"); $startdate = strtotime("+1 day", $startdate)) 
$date= date("Y-m-d", $startdate);
mysqli_query($db, "INSERT INTO tracking (date)VALUES('$date')");

我可能让它听起来比实际更令人困惑,但希望它至少能让您了解它是如何工作的。

【讨论】:

虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性 cmets 挤满你的代码,因为这会降低代码和解释的可读性! 抱歉,这是我第一次发帖。我已经编辑了答案以包含描述。我还回复了另一个帖子,他想在另一篇文章中只添加一年中的周一至周五。我想你可以在我的个人资料中找到...【参考方案8】:

如果您遇到像我这样程序被禁止,并且您的 sql 用户没有插入权限,因此 不允许插入,但您想要生成特定时期内的日期列表,比如当前年份进行一些聚合,使用这个

select * from 
(select adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) gen_date from
 (select 0 t0 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) t0,
 (select 0 t1 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) t1,
 (select 0 t2 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) t2,
 (select 0 t3 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) t3,
 (select 0 t4 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) t4) v
where gen_date between '2017-01-01' and '2017-12-31'

【讨论】:

这是这里最干净和最简洁的解决方案,但它可以做得更简洁:使用 HAVING 而不是 WHERE,您可以摆脱外部 SELECT 包装器。 这太棒了 喜欢这个,我正在与谷歌数据工作室合作,这是我在三天的搜索中发现的在一次选择中获取日期且不更改数据库的唯一方法。 我有一个数据库,我只能访问它的视图,而不是表本身,这是完美的。【参考方案9】:

我最近需要创建一个calendar_date 表,如下所示:

CREATE TABLE `calendar_date` (
    `date`    DATE NOT NULL      -- A calendar date.
    , `day`   SMALLINT NOT NULL  -- The day of the year for the date, 1-366.
    , `month` TINYINT NOT NULL   -- The month number, 1-12.
    , `year`  SMALLINT NOT NULL  -- The year.
    , PRIMARY KEY (`id`));

然后,我使用以下查询将January 1, 2001December 31, 2100(包括两者)之间的所有可能日期填充:

INSERT INTO `calendar_date` (`date`
    , `day`
    , `month`
    , `year`)
SELECT
    DATE
    , INCREMENT + 1
    , MONTH(DATE)
    , YEAR(DATE)
FROM
    -- Generate all possible dates for every year from 2001 to 2100.
    (SELECT
        DATE_ADD(CONCAT(YEAR, '-01-01'), INTERVAL INCREMENT DAY) DATE
        , INCREMENT
    FROM
        (SELECT
            (UNITS + TENS + HUNDREDS) INCREMENT
        FROM
            (SELECT 0 UNITS 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) UNITS
        CROSS JOIN
            (SELECT 0 TENS UNION
            SELECT 10 UNION SELECT 20 UNION SELECT 30 UNION
            SELECT 40 UNION SELECT 50 UNION SELECT 60 UNION
            SELECT 70 UNION SELECT 80 UNION SELECT 90) TENS
        CROSS JOIN
            (SELECT 0 HUNDREDS UNION
            SELECT 100 UNION SELECT 200 UNION SELECT 300 UNION
            SELECT 400 UNION SELECT 500 UNION SELECT 600 UNION
            SELECT 700 UNION SELECT 800 UNION SELECT 900) HUNDREDS
        ) INCREMENT
        -- For every year from 2001 to 2100, find the number of days in the year.
        , (SELECT
            YEAR
            , DAYOFYEAR(CONCAT(YEAR, '-12-31')) - DAYOFYEAR(CONCAT(YEAR, '-01-01')) + 1 DAYS
        FROM
            -- Generate years from 2001 to 2100.
            (SELECT
                (2000 + UNITS + TENS) YEAR
            FROM
                (SELECT 0 UNITS 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) UNITS
            CROSS JOIN
                (SELECT 0 TENS UNION
                SELECT 10 UNION SELECT 20 UNION SELECT 30 UNION
                SELECT 40 UNION SELECT 50 UNION SELECT 60 UNION
                SELECT 70 UNION SELECT 80 UNION SELECT 90) TENS
            ) YEAR
        WHERE
            YEAR BETWEEN 2001 AND 2100
        ) YEAR
      WHERE
          INCREMENT BETWEEN 0 AND DAYS - 1
      ORDER BY
          YEAR
          , INCREMENT) DATE;

在我的本地 MySQL 数据库上,INSERT 查询只用了几秒钟。希望这对某人有所帮助。

【讨论】:

【参考方案10】:
INSERT INTO my_dates (\`_date\`) SELECT DATE_ADD('2011-01-01', INTERVAL @_tmp:=@_tmp+1 day) \`_date\`
FROM (SELECT @_tmp:=-1 d 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) a /\*10^1\*/
JOIN (SELECT 0 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 /\*10^2\*/
JOIN (SELECT 0 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) c /\*10^3\*/
WHERE @_tmp+1 BETWEEN 0 AND DATEDIFF('2011-12-31', '2011-01-01');

【讨论】:

以上是关于如何用一系列日期填充表格?的主要内容,如果未能解决你的问题,请参考以下文章

如果我有重复的日期,如何用 pandas 中两个日期之间计算的值填充一列?

如何用sql实现自动填充日期

如何用特定值填充列中的一系列单元格?

如何填充表格视图,而不是表格视图控制器?

如何用'0'填充日期时间字符串以在熊猫中正确排序

我如何用HTML属性填充javascript数组?