2010-08-21 138 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