2013-07-16 57 views
0

我很努力地從以下方式從MySQL獲得結果。我有10個記錄在MySQL數據庫表中有日期和單位字段。我需要在每個日期都獲得使用單位。取消單元消費日期明智

如下,在每個記錄添加現今單元與過去以往單位表結構:

 
Date  Units 
---------- --------- 
10/10/2012 101 
11/10/2012 111 
12/10/2012 121 
13/10/2012 140 
14/10/2012 150 
15/10/2012 155 
16/10/2012 170 
17/10/2012 180 
18/10/2012 185 
19/10/2012 200 

所需的輸出將是:

 
Date  Units 
---------- --------- 
10/10/2012 101 
11/10/2012 10 
12/10/2012 10 
13/10/2012 19 
14/10/2012 10 
15/10/2012 5 
16/10/2012 15 
17/10/2012 10 
18/10/2012 5 
19/10/2012 15 

任何幫助將不勝感激。謝謝

+0

單位和units_used之間的關係是什麼? – 7alhashmi

+0

@ 7alhashmi:units_used只是sql結果中的單位的別名。 –

+0

我的意思是你將如何從列單元獲得unit_used? – 7alhashmi

回答

2

有幾種方法可以得到結果集。如果您可以在結果集中使用額外的列以及列的順序,那麼類似這樣的操作是可行的。


使用用戶變量

SELECT d.Date 
    , IF(@prev_units IS NULL 
     ,@diff := 0 
     ,@diff := d.units - @prev_units 
     ) AS `Units_used` 
    , @prev_units := d.units AS `Units` 
    FROM (SELECT @prev_units := NULL) i 
    JOIN ( 
     SELECT t.Date, t.Units 
      FROM mytable t 
      ORDER BY t.Date, t.Units 
     ) d 

這將返回結果集指定,但它包含單位列,以及。可能會將該列過濾掉,但由於MySQL處理內聯視圖的方式(MySQL稱之爲「派生表」),因此該列更加昂貴。

要刪除多餘的列,可以將其包裝在另一個查詢中...

SELECT f.Date 
    , f.Units_used 
    FROM (
     query from above goes here 
     ) f 
ORDER BY f.Date 

但同樣,刪除該列會帶來第二次實現結果集的額外成本。使用


半聯接

如果要保證有一個單獨的行,每個Date值,或者存儲爲DATE,或作爲與設置爲恆定的timecomponent一個DATETIME,如果有一個丟失的日期值

SELECT t.Date 
    , t.Units - s.Units AS Units_Used 
    FROM mytable t 
    LEFT 
    JOIN mytable s 
    ON s.Date = t.Date + INTERVAL -1 DAY 
    ORDER BY t.Date 

:如午夜,在日期值沒有間隙,並且日期被定義爲日期或日期時間數據類型,然後又查詢將返回代表特定的結果集(間隙),使得沒有匹配的前一行,那麼Units_used將具有NULL值。使用


相關子查詢

如果沒有沒有「丟失日期」的保證,但你必須保證,沒有多行特定日期,那麼另一種方法(通常在性能方面更貴)是使用相關子查詢:

SELECT t.Date 
    , (t.Units - (SELECT s.Units 
         FROM mytable s 
        WHERE s.Date < t.Date 
        ORDER BY s.Date DESC 
        LIMIT 1) 
     ) AS Units_used 
    FROM mytable t 
ORDER BY t.Date, t.Units 
+0

感謝您的解決方案幫助我實現了我期望的結果! –

1

spencer7593的解決方案會更快,但你也可以做這樣的事情...

SELECT * FROM rolling; 
+----+-------+ 
| id | units | 
+----+-------+ 
| 1 | 101 | 
| 2 | 111 | 
| 3 | 121 | 
| 4 | 140 | 
| 5 | 150 | 
| 6 | 155 | 
| 7 | 170 | 
| 8 | 180 | 
| 9 | 185 | 
| 10 | 200 | 
+----+-------+ 

SELECT a.id,COALESCE(a.units - b.units,a.units) units 
    FROM 
(SELECT x.* 
     , COUNT(*) rank 
    FROM rolling x 
JOIN rolling y 
     ON y.id <= x.id 
GROUP 
    BY x.id 
) a  
LEFT 
JOIN 
(SELECT x.* 
     , COUNT(*) rank 
    FROM rolling x 
JOIN rolling y 
     ON y.id <= x.id 
GROUP 
    BY x.id 
) b 
ON b.rank= a.rank -1; 

    +----+-------+ 
    | id | units | 
    +----+-------+ 
    | 1 | 101 | 
    | 2 | 10 | 
    | 3 | 10 | 
    | 4 | 19 | 
    | 5 | 10 | 
    | 6 |  5 | 
    | 7 | 15 | 
    | 8 | 10 | 
    | 9 |  5 | 
    | 10 | 15 | 
    +----+-------+ 
+0

+1。非常好。這也是一種可行的方法;基本上使用一個semijoin來獲得每行的前一行的計數(如果我們沒有保證'id'值是我們可以使用'Date'列和'id'列(如果存在)在'Date'序列中升序)。通過這個計數,我們可以基本上爲每一行分配一個「行序列」,我們可以使用它來加入前一行。非常好。 (這種方法的性能下降是大集合,半連接集合的大小可能很大,並且相同的結果集必須多次實現。 – spencer7593

+0

@ spencer7593謝謝 - 您正確地識別了實際上我的輕微錯誤查詢,這使得它不是通用的!澄清:只要值是唯一的,你可以使用日期列而不是id列,重要的是這些值是順序的,它們不需要是連續的。 – Strawberry

0

這應該會給出想要的結果。我不知道你的表是如何被調用的,所以我將它命名爲「tbltest」。 命名錶日期通常是一個壞主意,因爲它也指其他事物(函數,數據類型...),所以我將其重命名爲「fdate」。在字段名稱或表名中使用大寫字母也是一個壞主意,因爲它使您的語句不依賴於數據庫(有些數據庫區分大小寫,有些則不區分大小寫)。

SELECT 
    A.fdate, 
    A.units - coalesce(B.units, 0) AS units 
FROM 
    tbltest A left join tbltest B ON A.fdate = B.fdate + INTERVAL 1 DAY