获取两个date之间的date列表
使用标准的mysql函数是有办法写一个查询,将返回两个date之间的天数列表。
例如给定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
编辑:看来我还没有清楚。 我想生成这个列表。 我有存储在数据库中的值(按date时间),但希望他们被聚合在一个左外部连接的date列表(我期待有些日子这个连接的一些右边的null,并会处理这个)。
我将使用此存储过程生成所需的时间间隔到名为time_intervals的临时表中,然后使用临时time_intervals表将JOIN和聚合您的数据表。
该过程可以生成您在其中指定的所有不同types的间隔:
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;
在这篇文章的底部,我为SQL Server构build了一个类似的函数。
对于MSSQL你可以使用这个。 这非常快。
你可以将它包装在一个表值函数或存储过程中,并在开始date和结束date中作为variables进行分析。
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
我们和BIRT报告有类似的问题,因为我们想报告那些没有数据的日子。 由于这些date没有条目,所以我们最简单的解决scheme是创build一个简单的表格,存储所有date,并使用它来获取范围或连接以获得该date的零值。
我们每个月都有一份工作,以确保表格在未来5年内能够满足。 表格是这样创build的:
create table all_dates ( dt date primary key );
毫无疑问,用不同的数据库pipe理系统来实现这一点很奇妙,但我们总是select最简单的解决scheme。 该表的存储需求是最小的,它使查询如此简单和便携。 从性能angular度来看,这种解决scheme几乎总是更好,因为它不需要对数据进行每行计算。
另一个选项(我们之前使用过)确保每个date都有一个表格条目。 我们定期扫描表格,并为不存在的date和/或时间添加零条目。 这可能不是你的select,它取决于存储的数据。
如果你真的认为保持all_dates
表是一件很麻烦的all_dates
,那么存储过程就是要返回包含这些date的数据集的方法。 这几乎肯定会比较慢,因为每次调用时都必须计算范围,而不是从表中抽取预先计算的数据。
但是,说实话,你可以填充表1000年,没有任何严重的数据存储问题 – 365,000 16个字节(例如)date加上一个索引重复date加上20%的安全开销,我大致估计在约为14M [365,000 * 16 * 2 * 1.2 = 14,016,000字节]),这是一个小东西。
你可以像这样使用MySQL的用户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”,因为这使得用户可变增量每行两次。
从这个答案借用一个想法,你可以设置一个0到9的表格,并用它来生成你的date列表。
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个date的列表。 如果您需要更大,可以添加另一个交叉连接到内部查询。
对于Access(或任何SQL语言)
-
创build一个有两个字段的表,我们将这个表
tempRunDates
:
– 从fromDate
到date的fromDate
– 然后插入1条logging,其中包含开始date和结束date。 -
创build另一个表:
Time_Day_Ref
– 导入一个date列表(使excel中的列表很容易)到这个表中。
– 在我的情况下,字段名称是Greg_Dt
dateGreg_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;
简单!
通常情况下,你会使用一个辅助数字表,你通常只是为了这个目的而保留一些变化:
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
我已经看到了表值函数等的变体
你也可以保留一个永久的date列表。 我们在我们的数据仓库中有这样的一个时间表。
那么如何findSQL Server中两个给定date之间的date是解释http://ektaraval.blogspot.com/2010/09/writing-recursive-query-to-find-out-all.html
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
我们在我们的HRMS系统中使用这个function,您会发现它很有用
SELECT CAST(DAYNAME(daydate) as CHAR) as dayname,daydate FROM (select CAST((date_add('20110101', interval Hi*100 + Ti*10 + Ui 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 Hi*100 + Ti*10 + Ui day ) <= '20110228' order by daydate ASC )Days
这个解决scheme使用MySQL 5.0
创build一个表 – mytable
。
架构不重要。 重要的是它的行数。
所以,你可以只保留一列INTtypes10行,值为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';
限制:以上查询返回的date的最大数量为
(rows in mytable)*(rows in mytable) = 10*10 = 100.
您可以通过更改sql中的表单部分来增加这个范围:
从mytable x,mytable y,mytable z
所以范围是10*10*10 =1000
等等。
创build一个带有两个参数a_begin和a_end的存储过程。 在其中创build一个名为t的临时表,声明一个variablesd,将a_begin赋值给d,然后运行一个WHILE
循环,将其插入到d中,并调用ADDDATE
函数来增加值d。 最后SELECT * FROM t
。
我会用类似的东西:
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
variables表保存这两个date之间所有date递增的date,随时准备join您的内容。
我一直在为此奋斗了一段时间。 由于这是我在search解决scheme时在Google上的第一个search结果,所以让我发布到目前为止的位置。
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';
用数据库中的表replace[yourTable]
。 诀窍是您select的表中的行数必须> =您要返回的date数。 我试过使用表占位符DUAL,但它只会返回一个单一的行。
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 $$
这个程序会插入从今年开始到现在的所有date,只是取代“开始”和“结束”的日子,你准备好了!
我需要一个两个date之间的所有月份列表统计。 这两个date是订阅的开始和结束date。 所以列表显示了所有月份和每个月的订阅量。
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
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,然后你减去一秒钟,使结束date2011-02-27 23:59:59。 通过这样做,您可以获得给定间隔之间的所有date。
输出:
2011/02/25
2011/02/26
2011/02/27