2012-07-21 33 views
0

據MySQL文檔從最後一行中減去值:使用變量賦值MySQL中

作爲一般規則,你不應該賦值給一個用戶變量和相同的語句中讀出的值。您可能會得到您期望的結果 ,但這並不能保證。

http://dev.mysql.com/doc/refman/5.6/en/user-variables.html

然而,在這本書性能比較高的MySQL有一對夫婦使用這種戰術反正來提高查詢性能的例子。

下面是反模式,如果有的話,是否有更好的方式來編寫查詢,同時保持良好的性能?

set @last = null; 
select tick, [email protected] as delta, @last:=count from measurement; 

爲了澄清,我的目標是找到這一行和最後一行之間的區別。我的表格上有一個主鍵,它是一個日期時間列。

更新:

試圖施洛米的建議後,我恢復到我原來的查詢。事實證明,使用具有集合函數的case語句會產生意想不到的行爲。參見例如:

case when (@delta := (max(measurement.count) - @lastCount)) AND 0 then null 
when (@lastCount := measurement.count) AND 0 then null 
else @delta end 

似乎MySQL的評估不通過的結果包含在第一次通過聚合函數的表達式,然後評估在第二(分組)道次的聚合表達式。它似乎評估第二遍期間或之後的案例表達式,並使用該評估中第一遍的預先計算的值。結果是第三行@delta始終是@delta的初始值(因爲直到分組過程才發生分配)。我試圖將一個組函數與@delta合併到一行中,但無法使其按照預期行事。所以我最終回到我的原始查詢時沒有這個問題。

我仍然希望聽到更多關於如何更好地處理這樣的查詢的建議。

更新2:

對不起,我沒有在這個問題上的反應,我沒有機會進一步調查直到現在。

使用Shlomi的解決方案,它看起來像我有一個問題,因爲當我閱讀我的@last變量時,我正在使用按功能分組,但不是當我設置它時。我的代碼看起來是這樣的:

CASE 
    WHEN (@delta := count - @last) IS NULL THEN NULL 
    WHEN (@last:= count) IS NULL THEN NULL 
    ELSE (CASE WHEN cumulative THEN @delta ELSE avg(count) END) 
END AS delta 

的MySQL似乎處理不包含在第一遍和那些在第二遍做聚合函數表達式。上面代碼中的奇怪之處在於,即使cumulative的計算結果爲true,MySQL必須在ELSE子句中看到AVG聚合函數,並決定在第二遍中評估整個內部CASE表達式。由於@delta被設置爲一個沒有聚合函數的表達式,因此它似乎在第一次傳遞時被設置,並且在第二次傳遞發生時MySQL正在對設置了@delta@last的行進行求值。

最終,我似乎通過在第一個表達式中包含聚合函數找到了一個修復程序。事情是這樣的:

CASE 
    WHEN (@delta := max(count) - @last) IS NULL THEN NULL 
    WHEN (@last:= max(count)) IS NULL THEN NULL 
    ELSE (CASE WHEN cumulative THEN @delta ELSE avg(count) END) 
END AS delta 

我的純粹是基於測試和猜想,因爲我沒看過的源代碼,但希望這將幫助其他人誰可能會遇到類似的問題是什麼MySQL是幹什麼的理解。

我會接受Shlomi的回答,因爲它確實是一個很好的解決方案。只要小心你如何使用聚合函數。

回答

3

我已經深入研究了這個問題,並在上​​面寫了一些改進。

我在this post中提供了一個解決方案,該解決方案使用了預期順序的函數。去年還考慮my talk

構造如CASECOALESCE等函數已知底層行爲(至少在此情況發生變化之後,對吧?)。

例如,CASE子句通過定義順序依次檢查WHEN條件。

考慮原始查詢的改寫:

select 
    tick, 
    CASE 
    WHEN (@delta := [email protected]) IS NULL THEN NULL 
    WHEN (@last:=count) IS NULL THEN NULL 
    ELSE @delta 
    END AS delta 
from 
    measurement, 
    (select @last := 0) s_init 
; 

CASE條款有三個WHEN條件。它按順序執行它們,直到遇到成功的第一個。我已經寫了他們,前兩個將總是失敗。因此它執行第一個,然後轉向執行第二個,然後最後返回第三個。 總是

我這樣克服期待評價的順序,當你開始增加更多的複雜條款,如GROUP BYDISTINCTORDER BY和這樣這是一個真正的和真實的問題,主要是明顯的問題。作爲最後一個提示,我的解決方案與結果集上第一行的解決方案不同 - 與您的'返回NULL,我的解決方案返回0count之間的增量。如果我使用了NULL,我需要以其他方式更改WHEN條件 - 確保他們將失敗上的NULL值。

+0

這是一個很好的答案。感謝您的建議。 我已經提前實施了一個類似於你的解決方案,但爲了保持我的初始值爲空,我使用瞭如下子句: 'WHEN(@delta:= count- @ last)AND 0 THEN NULL' – 2012-07-23 23:32:26

+0

'AND 0' - 太簡單了!大! – 2012-07-24 04:42:46

+0

嘗試您的解決方案後,我無法使其與聚合函數一起工作。你提到這個解決方案適用於複雜的子句,比如'GROUP BY',但是我沒有任何運氣。我在上面添加了更多細節。我正在使用MySQL 5.5.20。 – 2012-08-03 17:56:22