什么是最简单的方法来填补空的date在SQL结果(在MySQL或Perl结束)?

我正在build立一个快速的csv从一个查询如下的MySQL表:

select DATE(date),count(date) from table group by DATE(date) order by date asc; 

然后把它们倾倒在perl文件中:

 while(my($date,$sum) = $sth->fetchrow) { print CSV "$date,$sum\n" } 

数据中有数据缺口,但是:

 | 2008-08-05 | 4 | | 2008-08-07 | 23 | 

我想填充数据填写零计数条目的最后几天结束:

 | 2008-08-05 | 4 | | 2008-08-06 | 0 | | 2008-08-07 | 23 | 

我打了一个非常尴尬(几乎肯定是越野车)的解决方法,每个月的数组和一些math,但是必须有更直接的东西在MySQL或Perl的一面。

任何天才的想法/一巴掌,为什么我这么笨?


我结束了一个存储过程,其中产生了一个临时表的date范围有几个原因:

  • 我知道我每次都在寻找的date范围
  • 有问题的服务器不幸的是我可以在atm上安装perl模块,并且它的状态已经够老了,它没有任何东西Date :: – y已安装

perldate/date时间 – 迭代答案也非常好,我希望我可以select多个答案!

当你在服务器端需要类似的东西时,你通常会创build一个包含两个时间点之间的所有可能date的表,然后将这个表与查询结果连接起来。 像这样的东西:

 create procedure sp1(d1 date, d2 date) declare d datetime; create temporary table foo (d date not null); set d = d1 while d <= d2 do insert into foo (d) values (d) set d = date_add(d, interval 1 day) end while select foo.d, count(date) from foo left join table on foo.d = table.date group by foo.d order by foo.d asc; drop temporary table foo; end procedure 

在这种情况下,最好在客户端进行一些检查,如果当前date不是previos + 1,则需要添加一些附加string。

当我不得不处理这个问题时,为了填写缺失的date,我实际上创build了一个只包含我感兴趣的所有date的参考表,并在date字段中join了数据表。 这是粗糙的,但它的作品。

 SELECT DATE(r.date),count(d.date) FROM dates AS r LEFT JOIN table AS d ON d.date = r.date GROUP BY DATE(r.date) ORDER BY r.date ASC; 

至于输出,我只是使用SELECT INTO OUTFILE而不是手动生成CSV。 让我们摆脱了担心逃离特殊字符的担忧。

不是愚蠢的,这不是MySQL所做的,插入空的date值。 我在perl中这样做了两步。 首先,将查询中的所有数据加载到按date组织的散列中。 然后,我创build一个Date :: EzDate对象,并增加它的一天,所以…

 my $current_date = Date::EzDate->new(); $current_date->{'default'} = '{YEAR}-{MONTH NUMBER BASE 1}-{DAY OF MONTH}'; while ($current_date <= $final_date) { print "$current_date\t|\t%hash_o_data{$current_date}"; # EzDate provides for automatic stringification in the format specfied in 'default' $current_date++; } 

最终date是另一个EzDate对象或包含date范围结束的string。

EzDate现在不在CPAN上,但是你可能会发现另外一个perl mod将会做date比较并提供一个date增加器。

你可以使用一个DateTime对象:

 use DateTime; my $dt; while ( my ($date, $sum) = $sth->fetchrow ) { if (defined $dt) { print CSV $dt->ymd . ",0\n" while $dt->add(days => 1)->ymd lt $date; } else { my ($y, $m, $d) = split /-/, $date; $dt = DateTime->new(year => $y, month => $m, day => $d); } print CSV, "$date,$sum\n"; } 

上面的代码所做的是保持DateTime对象$dt存储的最后一个打印date,并且当前date将来超过一天时,它会将$dt增加一天(并将其打印为一行)直到它与当前date相同。

这样你不需要额外的表格,也不需要预先提取所有的行。

既然你不知道差距在哪里,但是你需要从列表中的第一个date到最后一个date的所有值(大概是这样),请执行如下操作:

 use DateTime; use DateTime::Format::Strptime; my @row = $sth->fetchrow; my $countdate = strptime("%Y-%m-%d", $firstrow[0]); my $thisdate = strptime("%Y-%m-%d", $firstrow[0]); while ($countdate) { # keep looping countdate until it hits the next db row date if(DateTime->compare($countdate, $thisdate) == -1) { # counter not reached next date yet print CSV $countdate->ymd . ",0\n"; $countdate = $countdate->add( days => 1 ); $next; } # countdate is equal to next row's date, so print that instead print CSV $thisdate->ymd . ",$row[1]\n"; # increase both @row = $sth->fetchrow; $thisdate = strptime("%Y-%m-%d", $firstrow[0]); $countdate = $countdate->add( days => 1 ); } 

嗯,结果比我想象的要复杂得多..我希望这是有道理的!

我认为这个问题最简单的通用解决scheme是创build一个你需要的行数最多的Ordinal表(在你的情况下,31 * 3 = 93)。

 CREATE TABLE IF NOT EXISTS `Ordinal` ( `n` int(10) unsigned NOT NULL AUTO_INCREMENT, PRIMARY KEY (`n`) ); INSERT INTO `Ordinal` (`n`) VALUES (NULL), (NULL), (NULL); #etc 

接下来,从Ordinal执行一个LEFT JOIN连接到你的数据。 这是一个简单的例子,在上周每天都在进行:

 SELECT CURDATE() - INTERVAL `n` DAY AS `day` FROM `Ordinal` WHERE `n` <= 7 ORDER BY `n` ASC 

你需要改变的两件事是起点和间隔。 为了清晰起见,我使用了SET @var = 'value'语法。

 SET @end = CURDATE() - INTERVAL DAY(CURDATE()) DAY; SET @begin = @end - INTERVAL 3 MONTH; SET @period = DATEDIFF(@end, @begin); SELECT @begin + INTERVAL (`n` + 1) DAY AS `date` FROM `Ordinal` WHERE `n` < @period ORDER BY `n` ASC; 

所以最后的代码看起来是这样的,如果你在过去的三个月里join了每天的消息数量:

 SELECT COUNT(`msg`.`id`) AS `message_count`, `ord`.`date` FROM ( SELECT ((CURDATE() - INTERVAL DAY(CURDATE()) DAY) - INTERVAL 3 MONTH) + INTERVAL (`n` + 1) DAY AS `date` FROM `Ordinal` WHERE `n` < (DATEDIFF((CURDATE() - INTERVAL DAY(CURDATE()) DAY), ((CURDATE() - INTERVAL DAY(CURDATE()) DAY) - INTERVAL 3 MONTH))) ORDER BY `n` ASC ) AS `ord` LEFT JOIN `Message` AS `msg` ON `ord`.`date` = `msg`.`date` GROUP BY `ord`.`date` 

提示和意见:

  • 查询中最困难的部分可能是确定限制Ordinal时使用的天Ordinal 。 相比之下,将整数序列转换为date很容易。
  • 你可以使用Ordinal来满足你所有的不间断的需求。 只要确保它包含比您最长的序列更多的行。
  • 您可以在Ordinal上使用多个查询来查看多个序列,例如,在过去七(1-7)周的每个工作日(1-5)中列出。
  • 您可以通过在Ordinal表中存储date来加快速度,但是它会变得不那么灵活。 这样你只需要一个Ordinal表,不pipe你使用了多less次。 不过,如果速度值得,请尝试INSERT INTO ... SELECT语法。

我希望你能弄清楚其余的。

 select * from ( select date_add('2003-01-01 00:00:00.000', INTERVAL n5.num*10000+n4.num*1000+n3.num*100+n2.num*10+n1.num DAY ) as date from (select 0 as num 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) n1, (select 0 as num 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) n2, (select 0 as num 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) n3, (select 0 as num 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) n4, (select 0 as num 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) n5 ) a where date >'2011-01-02 00:00:00.000' and date < NOW() order by date 

 select n3.num*100+n2.num*10+n1.num as date 

你会得到一个从0到最大(n3)* 100 +最大(n2)* 10 +最大(n1)

由于这里我们有最大n3为3,SELECT将返回399,再加上0 – > 400条logging(日历中的date)。

您可以通过限制它来调整dynamic日历,例如,从min(date)到现在()。

使用一些Perl模块来进行date计算,如推荐的DateTime或Time :: Piece(核心从5.10)。 只是递增date和打印date,直到date为0才符合当前。

我不知道这是否可行,但如果你创build了一个包含所有可能date的新表(这可能是这个想法的问题,如果date范围将不可预知地变化),那么怎么办?然后做两个表上的左连接? 我想这是一个疯狂的解决scheme,如果有大量的可能的date,或者没有办法预测第一个和最后一个date,但是如果date的范围是固定的或容易解决,那么这可能工作。