使用标准的mysql函数可以编写一个返回两个日期之间的天数列表的查询。
例如,在2009-01-01和2009-01-13中,它将返回一个包含值的列表:
2009-01-01
2009-01-02
2009-01-03
2009-01-04
2009-01-05
2009-01-06
2009-01-07
2009-01-08
2009-01-09
2009-01-10
2009-01-11
2009-01-12
2009-01-13
编辑:看来我还不清楚。我想生成这个列表。我有值存储在数据库中(按日期时间),但是希望它们在左外连接上聚合到上面的日期列表中(我希望在某些连接的右侧有一段时间为null并且会处理这个)。
答案 0 :(得分:65)
我将使用此存储过程在名为 time_intervals 的临时表中生成所需的时间间隔,然后使用temp time_intervals 表来JOIN并聚合数据表。< / p>
该过程可以生成您在其中看到的所有不同类型的间隔:
call make_intervals('2009-01-01 00:00:00','2009-01-10 00:00:00',1,'DAY')
.
select * from time_intervals
.
interval_start interval_end
------------------- -------------------
2009-01-01 00:00:00 2009-01-01 23:59:59
2009-01-02 00:00:00 2009-01-02 23:59:59
2009-01-03 00:00:00 2009-01-03 23:59:59
2009-01-04 00:00:00 2009-01-04 23:59:59
2009-01-05 00:00:00 2009-01-05 23:59:59
2009-01-06 00:00:00 2009-01-06 23:59:59
2009-01-07 00:00:00 2009-01-07 23:59:59
2009-01-08 00:00:00 2009-01-08 23:59:59
2009-01-09 00:00:00 2009-01-09 23:59:59
.
call make_intervals('2009-01-01 00:00:00','2009-01-01 02:00:00',10,'MINUTE')
.
select * from time_intervals
.
interval_start interval_end
------------------- -------------------
2009-01-01 00:00:00 2009-01-01 00:09:59
2009-01-01 00:10:00 2009-01-01 00:19:59
2009-01-01 00:20:00 2009-01-01 00:29:59
2009-01-01 00:30:00 2009-01-01 00:39:59
2009-01-01 00:40:00 2009-01-01 00:49:59
2009-01-01 00:50:00 2009-01-01 00:59:59
2009-01-01 01:00:00 2009-01-01 01:09:59
2009-01-01 01:10:00 2009-01-01 01:19:59
2009-01-01 01:20:00 2009-01-01 01:29:59
2009-01-01 01:30:00 2009-01-01 01:39:59
2009-01-01 01:40:00 2009-01-01 01:49:59
2009-01-01 01:50:00 2009-01-01 01:59:59
.
I specified an interval_start and interval_end so you can aggregate the
data timestamps with a "between interval_start and interval_end" type of JOIN.
.
Code for the proc:
.
-- drop procedure make_intervals
.
CREATE PROCEDURE make_intervals(startdate timestamp, enddate timestamp, intval integer, unitval varchar(10))
BEGIN
-- *************************************************************************
-- Procedure: make_intervals()
-- Author: Ron Savage
-- Date: 02/03/2009
--
-- Description:
-- This procedure creates a temporary table named time_intervals with the
-- interval_start and interval_end fields specifed from the startdate and
-- enddate arguments, at intervals of intval (unitval) size.
-- *************************************************************************
declare thisDate timestamp;
declare nextDate timestamp;
set thisDate = startdate;
-- *************************************************************************
-- Drop / create the temp table
-- *************************************************************************
drop temporary table if exists time_intervals;
create temporary table if not exists time_intervals
(
interval_start timestamp,
interval_end timestamp
);
-- *************************************************************************
-- Loop through the startdate adding each intval interval until enddate
-- *************************************************************************
repeat
select
case unitval
when 'MICROSECOND' then timestampadd(MICROSECOND, intval, thisDate)
when 'SECOND' then timestampadd(SECOND, intval, thisDate)
when 'MINUTE' then timestampadd(MINUTE, intval, thisDate)
when 'HOUR' then timestampadd(HOUR, intval, thisDate)
when 'DAY' then timestampadd(DAY, intval, thisDate)
when 'WEEK' then timestampadd(WEEK, intval, thisDate)
when 'MONTH' then timestampadd(MONTH, intval, thisDate)
when 'QUARTER' then timestampadd(QUARTER, intval, thisDate)
when 'YEAR' then timestampadd(YEAR, intval, thisDate)
end into nextDate;
insert into time_intervals select thisDate, timestampadd(MICROSECOND, -1, nextDate);
set thisDate = nextDate;
until thisDate >= enddate
end repeat;
END;
this post底部的类似示例数据场景,我为SQL Server构建了类似的函数。
答案 1 :(得分:28)
对于MSSQL,您可以使用它。这非常快。
您可以将它包装在表值函数或存储过程中,并在开始日期和结束日期作为变量进行解析。
DECLARE @startDate DATETIME
DECLARE @endDate DATETIME
SET @startDate = '2011-01-01'
SET @endDate = '2011-01-31';
WITH dates(Date) AS
(
SELECT @startdate as Date
UNION ALL
SELECT DATEADD(d,1,[Date])
FROM dates
WHERE DATE < @enddate
)
SELECT Date
FROM dates
OPTION (MAXRECURSION 0)
GO
答案 2 :(得分:13)
我们在BIRT报告中遇到了类似问题,因为我们想要报告那些没有数据的日子。由于这些日期没有条目,对我们来说最简单的解决方案是创建一个存储所有日期的简单表,并使用它来获取范围或连接以获得该日期的零值。
我们每个月都有一份工作,以确保该表在未来5年内填充。因此创建了表:
create table all_dates (
dt date primary key
);
毫无疑问,使用不同的DBMS有一些神奇的棘手方法,但我们总是选择最简单的解决方案。该表的存储要求很小,它使查询更加简单和便携。从性能的角度来看,这种解决方案几乎总是更好,因为它不需要对数据进行每行计算。
另一个选项(之前我们已经使用过)是为了确保表格中的每个日期都有一个条目。我们定期清扫表格,并为不存在的日期和/或时间添加零条目。这可能不是您的选择,它取决于存储的数据。
如果你真的认为保持填充all_dates
表是一件麻烦事,那么存储过程就是返回包含这些日期的数据集的方法。这几乎肯定会变慢,因为每次调用时都必须计算范围,而不是仅仅从表中提取预先计算的数据。
但是,说实话,你可以在没有任何严重的数据存储问题的情况下填充表格 - 365,000个16字节(例如)日期加上一个索引重复日期加上20%的安全开销,我是粗略估计大约14M [365,000 * 16 * 2 * 1.2 = 14,016,000字节]),这是事物计划中的一个小表。
答案 3 :(得分:12)
您可以像这样使用MySQL的user variables:
SET @num = -1;
SELECT DATE_ADD( '2009-01-01', interval @num := @num+1 day) AS date_sequence,
your_table.* FROM your_table
WHERE your_table.other_column IS NOT NULL
HAVING DATE_ADD('2009-01-01', interval @num day) <= '2009-01-13'
@num是-1,因为您第一次使用它时会添加它。此外,您不能使用“HAVING date_sequence”,因为这会使用户变量每行增加两次。
答案 4 :(得分:8)
从this回答借用一个想法,你可以设置一个0到9的表格,并使用它来生成你的日期列表。
CREATE TABLE num (i int);
INSERT INTO num (i) VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);
select adddate('2009-01-01', numlist.id) as `date` from
(SELECT n1.i + n10.i*10 + n100.i*100 AS id
FROM num n1 cross join num as n10 cross join num as n100) as numlist
where adddate('2009-01-01', numlist.id) <= '2009-01-13';
这将允许您生成最多1000个日期的列表。如果需要更大,可以在内部查询中添加另一个交叉连接。
答案 5 :(得分:3)
对于Access(或任何SQL语言)
创建一个包含2个字段的表格,我们将此表格称为tempRunDates
:
- 字段fromDate
和toDate
- 然后只插入1条记录,其中包含开始日期和结束日期。
创建另一个表:Time_Day_Ref
- 在此表中导入日期列表(将Excel中的列表很容易)。
- 我的案例中的字段名称为Greg_Dt
,格里高利日期为
- 我从2009年1月1日到2020年1月1日列出了我的名单。
运行查询:
SELECT Time_Day_Ref.GREG_DT
FROM tempRunDates, Time_Day_Ref
WHERE Time_Day_Ref.greg_dt>=tempRunDates.fromDate And greg_dt<=tempRunDates.toDate;
容易!
答案 6 :(得分:2)
通常情况下,人们会使用一个辅助数字表来为此目的而保留,并对此进行一些修改:
SELECT *
FROM (
SELECT DATEADD(d, number - 1, '2009-01-01') AS dt
FROM Numbers
WHERE number BETWEEN 1 AND DATEDIFF(d, '2009-01-01', '2009-01-13') + 1
) AS DateRange
LEFT JOIN YourStuff
ON DateRange.dt = YourStuff.DateColumn
我见过表值函数的变体等等。
您还可以保留一份永久的日期列表。我们在数据仓库中列出了这一点以及一天中的时间列表。
答案 7 :(得分:2)
如何在http://ektaraval.blogspot.com/2010/09/writing-recursive-query-to-find-out-all.html
中解释如何在SQL服务器中查找两个给定日期之间的日期答案 8 :(得分:2)
CREATE FUNCTION [dbo].[_DATES]
(
@startDate DATETIME,
@endDate DATETIME
)
RETURNS
@DATES TABLE(
DATE1 DATETIME
)
AS
BEGIN
WHILE @startDate <= @endDate
BEGIN
INSERT INTO @DATES (DATE1)
SELECT @startDate
SELECT @startDate = DATEADD(d,1,@startDate)
END
RETURN
END
答案 9 :(得分:1)
下面的示例显示了一个月的日期范围 - 从第 1 天到同月的最后一天。您可以通过使用 @e `INTERVAL` 值来更改日期范围。
SET @s := CAST('2021-12-01' AS DATE); -- start at 2021-12-01
SET @e := @s + INTERVAL 1 MONTH - INTERVAL 1 DAY; -- ends at 2021-12-31
WITH RECURSIVE d_range AS
(
SELECT @s AS gdate -- generated dates
UNION ALL
SELECT gdate + INTERVAL 1 day
FROM d_range
WHERE gdate < @e
)
SELECT *
FROM d_range;
结果:
| gdate |
+------------+
| 2021-12-01 |
| 2021-12-02 |
| 2021-12-03 |
| 2021-12-04 |
| 2021-12-05 |
| 2021-12-06 |
| 2021-12-07 |
| 2021-12-08 |
| 2021-12-09 |
| 2021-12-10 |
| 2021-12-11 |
| 2021-12-12 |
| 2021-12-13 |
| 2021-12-14 |
| 2021-12-15 |
| 2021-12-16 |
| 2021-12-17 |
| 2021-12-18 |
| 2021-12-19 |
| 2021-12-20 |
| 2021-12-21 |
| 2021-12-22 |
| 2021-12-23 |
| 2021-12-24 |
| 2021-12-25 |
| 2021-12-26 |
| 2021-12-27 |
| 2021-12-28 |
| 2021-12-29 |
| 2021-12-30 |
| 2021-12-31 |
+------------+
-> 31 rows in set (0.037 sec)
<块引用>
注意。此查询在选择较长日期范围时,此查询使用那里的递归是非常慢的31 rows in set (0.037 sec)
它会变得更慢。
答案 10 :(得分:1)
此解决方案正在使用MySQL 5.0
创建一个表 - mytable
。
架构不重要。重要的是它中的行数
因此,您只能保留一列INT类型的10列,值为1到10.
SQL:
set @tempDate=date('2011-07-01') - interval 1 day;
select
date(@tempDate := (date(@tempDate) + interval 1 day)) as theDate
from mytable x,mytable y
group by theDate
having theDate <= '2011-07-31';
限制:
上述查询返回的最大日期数为
(rows in mytable)*(rows in mytable) = 10*10 = 100.
您可以通过更改sql中的表单部分来增加此范围:
来自mytable x,mytable y,mytable z
因此,范围为10*10*10 =1000
,依此类推。
答案 11 :(得分:1)
我们在人力资源管理系统中使用了这个,你会发现它很有用
SELECT CAST(DAYNAME(daydate) as CHAR) as dayname,daydate
FROM
(select CAST((date_add('20110101', interval H.i*100 + T.i*10 + U.i day) )as DATE) as daydate
from erp_integers as H
cross
join erp_integers as T
cross
join erp_integers as U
where date_add('20110101', interval H.i*100 + T.i*10 + U.i day ) <= '20110228'
order
by daydate ASC
)Days
答案 12 :(得分:0)
创建一个存储过程,它接受两个参数a_begin和a_end。
在其中创建一个名为t的临时表,声明一个变量d,将a_begin指定给d,并在t中运行WHILE
循环INSERT
并调用ADDDATE
函数来增加值d 。最后SELECT * FROM t
。
答案 13 :(得分:0)
我已经和它斗争了很长一段时间。由于这是我搜索解决方案时谷歌的第一个热门话题,让我发帖到目前为止。
SET @d := '2011-09-01';
SELECT @d AS d, cast( @d := DATE_ADD( @d , INTERVAL 1 DAY ) AS DATE ) AS new_d
FROM [yourTable]
WHERE @d <= '2012-05-01';
将[yourTable]
替换为数据库中的表。诀窍是,您选择的表中的行数必须是> =您要返回的日期数。我尝试使用表占位符DUAL,但它只返回一行。
答案 14 :(得分:0)
select * from table_name where col_Date between '2011/02/25' AND DATEADD(s,-1,DATEADD(d,1,'2011/02/27'))
在这里,首先添加一天到当前的endDate,它将是2011-02-28 00:00:00,然后你减去一秒使得结束日期2011-02-27 23:59:59。通过这样做,您可以获得给定间隔之间的所有日期。
输出:
2011/02/25
2011/02/26
2011/02/27
答案 15 :(得分:0)
DELIMITER $$
CREATE PROCEDURE popula_calendario_controle()
BEGIN
DECLARE a INT Default 0;
DECLARE first_day_of_year DATE;
set first_day_of_year = CONCAT(DATE_FORMAT(curdate(),'%Y'),'-01-01');
one_by_one: LOOP
IF dayofweek(adddate(first_day_of_year,a)) <> 1 THEN
INSERT INTO calendario.controle VALUES(null,150,adddate(first_day_of_year,a),adddate(first_day_of_year,a),1);
END IF;
SET a=a+1;
IF a=365 THEN
LEAVE one_by_one;
END IF;
END LOOP one_by_one;
END $$
此程序将插入从年初到现在的所有日期,只需替换&#34;开始&#34;和&#34;结束&#34;,你准备好了!
答案 16 :(得分:0)
我需要一个包含2个日期之间所有月份的统计信息列表。这两个日期是订阅的开始和结束日期。 因此,该列表会显示所有月份和每月订阅量。
MYSQL
CREATE PROCEDURE `get_amount_subscription_per_month`()
BEGIN
-- Select the ultimate start and enddate from subscribers
select @startdate := min(DATE_FORMAT(a.startdate, "%Y-%m-01")),
@enddate := max(DATE_FORMAT(a.enddate, "%Y-%m-01")) + interval 1 MONTH
from subscription a;
-- Tmp table with all months (dates), you can always format them with DATE_FORMAT)
DROP TABLE IF EXISTS tmp_months;
create temporary table tmp_months (
year_month date,
PRIMARY KEY (year_month)
);
set @tempDate=@startdate; #- interval 1 MONTH;
-- Insert every month in tmp table
WHILE @tempDate <= @enddate DO
insert into tmp_months (year_month) values (@tempDate);
set @tempDate = (date(@tempDate) + interval 1 MONTH);
END WHILE;
-- All months
select year_month from tmp_months;
-- If you want the amount of subscription per month else leave it out
select mnd.year_month, sum(subscription.amount) as subscription_amount
from tmp_months mnd
LEFT JOIN subscription ON mnd.year_month >= DATE_FORMAT(subscription.startdate, "%Y-%m-01") and mnd.year_month <= DATE_FORMAT(subscription.enddate, "%Y-%m-01")
GROUP BY mnd.year_month;
END
答案 17 :(得分:0)
我正在使用 Server version: 5.7.11-log MySQL Community Server (GPL)
现在我们将以一种简单的方式解决这个问题。
我创建了一个名为“datetable”
的表格mysql> describe datetable;
+---------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+---------+------+-----+---------+-------+
| colid | int(11) | NO | PRI | NULL | |
| coldate | date | YES | | NULL | |
+---------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)
现在,我们将看到插入的记录。
mysql> select * from datetable;
+-------+------------+
| colid | coldate |
+-------+------------+
| 101 | 2015-01-01 |
| 102 | 2015-05-01 |
| 103 | 2016-01-01 |
+-------+------------+
3 rows in set (0.00 sec)
这里我们的查询是在两个日期而不是那些日期内获取记录。
mysql> select * from datetable where coldate > '2015-01-01' and coldate < '2016-01-01';
+-------+------------+
| colid | coldate |
+-------+------------+
| 102 | 2015-05-01 |
+-------+------------+
1 row in set (0.00 sec)
希望这会对很多人有所帮助。
答案 18 :(得分:0)
在MariaDB> = 10.3和MySQL> = 8.0中使用新的递归(公用表表达式)功能的优雅解决方案。
WITH RECURSIVE t as (
select '2019-01-01' as dt
UNION
SELECT DATE_ADD(t.dt, INTERVAL 1 DAY) FROM t WHERE DATE_ADD(t.dt, INTERVAL 1 DAY) <= '2019-04-30'
)
select * FROM t;
上面的代码返回了介于'2019-01-01'和'2019-04-30'之间的日期表。
答案 19 :(得分:0)
您可以使用此
SELECT CAST(cal.date_list AS DATE) day_year
FROM (
SELECT SUBDATE('2019-01-01', INTERVAL 1 YEAR) + INTERVAL xc DAY AS date_list
FROM (
SELECT @xi:=@xi+1 as xc from
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc1,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc2,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc3,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc4,
(SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) xc5,
(SELECT @xi:=-1) xc0
) xxc1
) cal
WHERE cal.date_list BETWEEN '2019-01-01' AND '2019-12-31'
ORDER BY cal.date_list DESC;
答案 20 :(得分:0)
SET @s := '2020-01-01';
SET @e := @s + INTERVAL 1 YEAR - INTERVAL 1 DAY; -- set end date to select
SELECT CAST((DATE(@s)+INTERVAL (H+T+U) DAY) AS DATE) d -- generate a list of 400 days starting from @s date so @e can not be more then @s +
FROM ( SELECT 0 H
UNION ALL SELECT 100 UNION ALL SELECT 200 UNION ALL SELECT 300
) H CROSS JOIN ( SELECT 0 T
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
) T CROSS JOIN ( SELECT 0 U
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
) U
-- generated lenght of date list can be calculated as fallow 1 * H(3 + 1) * T(9 + 1) * U(9 + 1) = 400
-- it is crucial to preserve the numbering convention as 1, 2 ... 20, 30..., 100, 200, ... to retrieve chronological date selection
-- add more UNION 400, 500, 600, ... H(6 + 1) if you want to select from more than 700 days!
WHERE
(DATE(@s+INTERVAL (H+T+U) DAY)) <= DATE((@e))
ORDER BY d;
UNIONS 定义了可供选择的可用日期的长度和范围。因此,请始终确保记录足够多。
SELECT 1 * (3 + 1) * (9 + 1) * (9 + 1); -- 400 days starting from from @s
基于此article
经测试并按预期工作
答案 21 :(得分:0)
我会使用类似的东西:
DECLARE @DATEFROM AS DATETIME
DECLARE @DATETO AS DATETIME
DECLARE @HOLDER TABLE(DATE DATETIME)
SET @DATEFROM = '2010-08-10'
SET @DATETO = '2010-09-11'
INSERT INTO
@HOLDER
(DATE)
VALUES
(@DATEFROM)
WHILE @DATEFROM < @DATETO
BEGIN
SELECT @DATEFROM = DATEADD(D, 1, @DATEFROM)
INSERT
INTO
@HOLDER
(DATE)
VALUES
(@DATEFROM)
END
SELECT
DATE
FROM
@HOLDER
然后@HOLDER
变量表保存这两个日期之间按日递增的所有日期,随时准备加入您的内容。