【问题标题】:Generate data for range of dates including those that are not present in data为日期范围生成数据,包括数据中不存在的日期
【发布时间】:2011-07-26 13:23:00
【问题描述】:

这是我的表结构和数据:

CREATE TABLE event (
    EventID INT(11) NOT NULL AUTO_INCREMENT,
    EventDate DATETIME DEFAULT NULL,
    Description VARCHAR(50) DEFAULT NULL,
    PRIMARY KEY (EventID)
);

INSERT INTO event (EventID, EventDate, Description) VALUES
    (1, '2011-01-01 00:00:00', 'Event 1'),
    (2, '2011-03-01 00:00:00', 'Event 2'),
    (3, '2011-06-01 00:00:00', 'Event 3'),
    (4, '2011-09-01 00:00:00', 'Event 4');

这个查询和输出:

SELECT *
FROM EVENT
WHERE EventDate BETWEEN '2011-02-01' AND '2011-03-31'
+---------+---------------------+-------------+
| EventID | EventDate           | Description |
+---------+---------------------+-------------+
|       2 | 2011-03-01 00:00:00 | Event 2     |
+---------+---------------------+-------------+
1 row in set (0.00 sec)

我想像这样在结果中注入空日期:

+---------+---------------------+-------------+
| EventID | EventDate           | Description |
+---------+---------------------+-------------+
| NULL    | 2011-02-01 00:00:00 |  NULL       |
| NULL    | 2011-02-02 00:00:00 |  NULL       |
| NULL    | 2011-02-03 00:00:00 |  NULL       |
∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨
∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧
| NULL    | 2011-02-28 00:00:00 |  NULL       |
|       2 | 2011-03-01 00:00:00 |  Event 2    |
| NULL    | 2011-03-02 00:00:00 |  NULL       |
∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨∨
∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧∧
| NULL    | 2011-03-31 00:00:00 |  NULL       |
+---------+---------------------+-------------+

输出应包含 59 行:2 月为 28 行,3 月为 31 行。

我想避免任何硬编码;相反,我需要一个非常灵活的解决方案来适应指定的日期。

【问题讨论】:

  • 您可能希望避免“硬编码”,但有效地创建日期列表、比较、插入(甚至可能删除列表)将比尝试计算单个日期快得多。
  • @Dems:你可能是对的,我没想到

标签: mysql sql datetime date-range


【解决方案1】:

辅助日历表可以很好地工作。最简单的日历表是​​单列日期。

create table calendar (
    cal_date date primary key
);

您可以使用电子表格或 SQL 来填充它。对其进行外部联接将引入数据中不存在的日期。使用 GRANT 和 REVOKE 限制权限,并使用任何必要的方法来确保您期望的日期确实在那里。我在我的服务器上运行每日报告,以确保有“n”行,并验证最早和最晚的日期。

在某些平台上,您可以即时生成一系列日期,并直接使用它或在 CTE 中使用。 PostgreSQL 有这方面的功能;我不知道MySQL是否有。不过,如果你想自己动手,它们并不难写。

【讨论】:

  • +1 :与预先创建的表格进行比较比编写代码来计算单个缺失日期要快得多。
  • @Dems:它也很不灵活。这实际上取决于日期范围的差异。
  • @pablochan :我不会说它不灵活。 100 年的日历占用的空间约为 16KB;必要时即时创建它的时间很短;可定制为DayOfWeek、BankHoliday、Open Hours等字段;它可以是天、小时、周、月等的日历;你几乎可以用它做任何你喜欢的事情。在几乎所有情况下,这都是一个巨大的性能提升。无论成本如何,灵活性都不是其中之一。
【解决方案2】:

也许你需要一个数据透视表,看看那个

数据透视表架构

名称:枢轴 列:{i:数据类型 int}

填充

创建 foo 表

模式 foo

名称:foo 列:值数据类型 varchar

insert into foo
values('0'),
values('1'),
values('2'),
values('3'),
values('4'),
values('5'),
values('6'),
values('7'),
values('8'),
values('9');

- insert 100 values
insert into pivot
select concat(a.value, a.value)
from foo a, foo b

- insert 1000 values
insert into pivot
select concat(a.value, b.value, c.value)
from foo a, foo b, foo c

您的查询

SELECT 
 Event.EventId,
 case when EventDate is null then DATE_ADD(periods.periodstart, INTERVAL auxtable.i DAY)
 else EventDate  end,
 Description

FROM
(
  select id, min(EventDate ) periodstart, max(EventDate) periodend,

  DATEDIFF(max(EventDate),min(EventDate )) as days
  FROM EVEN
  WHERE EventDate BETWEEN '2011-02-01' AND '2011-03-31'
) periods

inner join     

(
  select *
  from pivot
  where i >= 0 and i < 31 //max one month change with your needs
)auxtable
on auxtable.i < periods.days

left join Event
on Event.EventDate = DATE_ADD(periods.periodstart, INTERVAL auxtable.i DAY)

【讨论】:

  • 它有点工作但过于复杂。另外,它需要我填充序号表。
  • 嗯,这是一种来自更纯 sql 的神秘技术。 sql server 采用了“pivot”指令,我不需要阅读它,但毫无疑问,它更简单,但我更喜欢可移植的 sql,不过我会留意
【解决方案3】:

我很确定这在纯 SQL 中是不可能的,所以您的选择是:

  • 使用存储过程/函数
  • 在应用代码中执行此操作,看起来非常简单

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-25
    • 1970-01-01
    • 2021-03-26
    相关资源
    最近更新 更多