2016-05-25 93 views
0

我有以下設置:優化SQL查詢反覆case語句

CREATE TABLE table_name 
(
perf_id INT, 
build_id INT, 
series_id INT, 
client_id INT, 
measurement INT 
); 

INSERT INTO table_name VALUES (1, 1, 1, 1, 10); 
INSERT INTO table_name VALUES (2, 1, 2, 1, 15); 
INSERT INTO table_name VALUES (3, 2, 1, 1, 10); 
INSERT INTO table_name VALUES (4, 2, 2, 1, 15); 
INSERT INTO table_name VALUES (5, 2, 2, 1, 20); 
INSERT INTO table_name VALUES (6, 3, 1, 1, 10); 
INSERT INTO table_name VALUES (7, 3, 2, 1, 20); 

然後我用下面的查詢這樣的:

SELECT 
build_id, 
CONCAT(
MIN(CASE WHEN series_id IN (1) THEN measurement ELSE NULL END), 
';', 
AVG(CASE WHEN series_id IN (1) THEN measurement ELSE NULL END), 
';', 
MAX(CASE WHEN series_id IN (1) THEN measurement ELSE NULL END) 
), 
CONCAT(
MIN(CASE WHEN series_id IN (2) THEN measurement ELSE NULL END), 
';', 
AVG(CASE WHEN series_id IN (2) THEN measurement ELSE NULL END), 
';', 
MAX(CASE WHEN series_id IN (2) THEN measurement ELSE NULL END) 
) 
FROM table_name GROUP BY build_id; 

您可以在這裏找到一個sqlFiddle http://sqlfiddle.com/#!9/efef8/17

我想優化我的查詢,主要是爲了減少重複的代碼。我將從PHP調用此函數,併爲IN運算符綁定值。

我想通功能可取代的重複代碼批量:

CREATE FUNCTION data(s INT) 
RETURNS VARCHAR(50) 
RETURN CONCAT(
MIN(CASE WHEN series_id IN (s) THEN measurement ELSE NULL END), 
';', 
AVG(CASE WHEN series_id IN (s) THEN measurement ELSE NULL END), 
';', 
MAX(CASE WHEN series_id IN (s) THEN measurement ELSE NULL END) 
) 

但這種抱怨不恰當地使用聚合函數的。我也不確定如何傳入IN運算符的數組。

NB:

  1. 的毗連運算似乎並沒有對SQLFiddle工作,但確實在我的機器上。我不知道我是否弄亂了一些語法?

  2. 列數將是可變的。

  3. 我也將在GROUP BY之前控制WHERE語句,但是我有這個工作,我只是不想重複我的concat一百萬次。

+0

SQL沒有數組。你可以創建一個臨時表,然後使用'WHEN series_id IN(SELECT id FROM temptable)' – Barmar

+0

但是你不能在函數中使用像'MIN'和'AVG'這樣的聚合函數,因爲它們在一個查詢,而不是個別值。 – Barmar

+0

我想這個術語數組來自於我將綁定到它的PHP變量。我認爲我不能將一個像「(1)」或「(1,2,3,4,5)」這樣的可變長度的「數組」傳遞給一個函數?我可以將表格傳遞給函數嗎?並創建這個臨時表,然後傳入? – Deoxyribonucleic

回答

2

你需要從你的函數中選擇一個。試試這個:

DELIMITER $$ 

create FUNCTION data(s INT, id int) 
RETURNS VARCHAR(50) 
BEGIN 
    DECLARE res VARCHAR(50); 
    SET res = (SELECT CONCAT(
MIN(CASE WHEN series_id IN (s) THEN measurement ELSE NULL END), 
';', 
AVG(CASE WHEN series_id IN (s) THEN measurement ELSE NULL END), 
';', 
MAX(CASE WHEN series_id IN (s) THEN measurement ELSE NULL END) 
) 
from table_name 
where build_id = id); 
    RETURN res; 
END$$ 


select 
t.build_id 
, data(1, t.build_id) 
, data(2, t.build_id) 
from table_name t 
group by t.build_id 
+0

這看起來很完美!我猜唯一的缺點是我想放在主查詢中的任何過濾器也會傳遞給函數。但是,這似乎不是太多的問題。 (即,如果我想通過客戶端ID來限制所有結果,我必須在該函數上添加該限制並將其傳入。實際上,我認爲WHERE client_id = X現在僅在內部查詢中需要) – Deoxyribonucleic

+0

@脫氧核糖核酸如果我正確地理解了你,那麼如果你將函數改成'FUNCTION data(s INT,id int,clientid int)'並且改變函數內部的WHERE以添加'client_id = clientid',那麼外部查詢只需要改變爲',data(2,t.build_id,t。client_id) from table_name t group by t.build_id' – artm

+0

是的,正是我在想什麼。所以它仍然沒有重複,因爲它只在函數內部,我必須定義我的約束條件 – Deoxyribonucleic

1

如果你不關心行,其中的值是0

SELECT build_id, 
     CONCAT_WS(';', MIN(measurement), AVG(measurement), MAX(measurement)), 
FROM table_name 
WHERE series_id = 2 
GROUP BY build_id; 

要獲取其他行:

SELECT build_id, 
     CONCAT_WS(';', MIN(measurement), AVG(measurement), MAX(measurement)), 
FROM table_name 
WHERE series_id = 2 
GROUP BY build_id 
UNION ALL 
SELECT build_id, '' 
FROM table_name 
GROUP BY build_id 
HAVING MAX(series_id = 2) = 0; 
+0

CONCAT_WS看起來很完美!但是我不確定我瞭解代碼的後半部分是如何工作的?我會試驗一下,看看它是否更清晰。 – Deoxyribonucleic

+0

第一個查詢獲取具有'series_id = 2'的build_id。第二個查詢有第二個子查詢來獲取其餘的行(注意:我修正了'having'中的邏輯,所以它是正確的)。 –