2010-08-21 133 views
49

我有一张2列,日期和分数表。它最多有30个参赛作品,最近30天每场参赛作品一个。MySQL如何填写范围内的缺失日期?

date  score 
----------------- 
1.8.2010 19 
2.8.2010 21 
4.8.2010 14 
7.8.2010 10 
10.8.2010 14 

我的问题是,一些日期缺失 - 我希望看到:

date  score 
----------------- 
1.8.2010 19 
2.8.2010 21 
3.8.2010 0 
4.8.2010 14 
5.8.2010 0 
6.8.2010 0 
7.8.2010 10 
... 

我从单个查询需要的是让:19,21,9,14,0,0 ,10,0,0,14 ...这意味着缺少的日期是填充0.

我知道如何获取所有的值和服务器端语言遍历日期和缺少空白。但这是可能的在MySQL中做的,所以我按日期排序结果,并得到缺失的部分。

编辑:在这个表中有另一个名为UserID的列,所以我有30.000个用户,其中一些人在这个表中有得分。我每天删除日期,如果日期< 30天前,因为我需要为每个用户的最后30天得分。原因是我制作了过去30天内用户活动的图表,并绘制了我需要用逗号分隔的30个值的图表。所以我可以说在查询让我的USERID = 10203活动和查询会得到我30分,每过去30天的一个。我希望我现在更清楚。

+0

是的,这是可能的,但*为什么*你会这样做? – NullUserException 2010-08-21 20:32:23

+0

我还是不明白。不要从数据库中获取不必要的数据,如果你可以用绘制图表的任何内容填补这些空白,你就可以节省一些开销。 – NullUserException 2010-08-21 20:42:11

+2

但我必须为USERID选择数据,例如获得20行日期并得分,然后我必须在我的服务器端语言(ASP)中循环以检查30天前是否有日期,如果它是不要让0其他数据库值...这是不是更消耗从数据库30取值,只是构造字符串? – Jerry2 2010-08-21 20:45:32

回答

49

MySQL没有递归功能,让你留下了使用数字表绝招 -

  1. 创建一个只持有递增的数字表 - 易使用AUTO_INCREMENT做:

    DROP TABLE IF EXISTS `example`.`numbers`; 
    CREATE TABLE `example`.`numbers` (
        `id` int(10) unsigned NOT NULL auto_increment, 
        PRIMARY KEY (`id`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 
    
  2. 使用填充表:

    INSERT INTO `example`.`numbers` 
        (`id`) 
    VALUES 
        (NULL) 
    

    ...的尽可能多的价值,你需要。

  3. 使用DATE_ADD来构建日期列表,根据NUMBERS.id值增加天数。替换 “2010-06-06” 和 “2010-06-14” 与您相应的开始和结束日期(但使用相同的格式,YYYY-MM-DD) -

    SELECT `x`.* 
        FROM (SELECT DATE_ADD('2010-06-06', INTERVAL `n`.`id` - 1 DAY) 
          FROM `numbers` `n` 
         WHERE DATE_ADD('2010-06-06', INTERVAL `n`.`id` -1 DAY) <= '2010-06-14') x 
    
  4. LEFT JOIN到你的餐桌基于时间部分的数据:

    SELECT `x`.`ts` AS `timestamp`, 
          COALESCE(`y`.`score`, 0) AS `cnt` 
        FROM (SELECT DATE_FORMAT(DATE_ADD('2010-06-06', INTERVAL `n`.`id` - 1 DAY), '%m/%d/%Y') AS `ts` 
          FROM `numbers` `n` 
          WHERE DATE_ADD('2010-06-06', INTERVAL `n`.`id` - 1 DAY) <= '2010-06-14') x 
    LEFT JOIN TABLE `y` ON STR_TO_DATE(`y`.`date`, '%d.%m.%Y') = `x`.`ts` 
    

如果你想保持的日期格式,使用DATE_FORMAT function

DATE_FORMAT(`x`.`ts`, '%d.%m.%Y') AS `timestamp` 
+1

谢谢。这是一个快速的操作,你会建议不要使用这种方法和服务器端计算? – Jerry2 2010-08-21 20:54:14

+4

@ Jerry2:我的首选是在数据库中进行尽可能多的数据处理,而不是真正涉及到的演示内容。我不羡慕在应用程序代码中这样做,只要它是*一次到数据库* ... – 2010-08-21 20:56:51

+0

为了使用索引,条件(WHERE和ON子句)可以重写为WHERE n.id < DATEDIFF('2010-06-14','2010-06-06')'和'LEFT JOIN TABLE y ON y.date = DATE_FORMAT(x.ts,'%d。%m。%Y')' – 2017-10-15 16:53:06

13

你可以通过使用日历表来完成此操作。这是您创建一次并填充日期范围的表格(例如,每个日期的数据集2000-2050;这取决于您的数据)。然后,您可以在桌面上对日历表进行外连接。如果表格中缺少日期,则返回0作为分数。

+0

True ,但数字表格更加灵活 - 请参阅我的答案。 IE:如果你现在需要连续数字呢?你想每个数据类型的表? – 2010-08-21 21:02:25

+1

需要连续数字将是另一个用例;-)如果您必须针对不同的DBMS(例如Oracle,MySQL,SQL-Server),则您的方法需要稍微修改的语句,并且我怀疑DATE_ADD方法比日历慢表(但我认为这是不相关的) – Soundlink 2010-08-21 21:32:49

+0

关于[SQL Server 2005,递归CTE *几乎*比NUMBERS表技巧更快](http://stackoverflow.com/questions/1478951/tsql-generate-a-递增结果日期/ 1479028#1479028) – 2010-08-21 22:02:55