2012-10-25 37 views
1

中的一些人,我需要考慮的東西去掉極端低值和高值,如報告這個如何提取消除極值的常見行爲?

SELECT ... 
FROM 
(
    SELECT val, ntile(10) OVER(ORDER BY val) AS tile FROM table 
) AS tiled_table 
WHERE tile > 1 AND tile < 10 

它可以提取到存儲過程中,將採取表名和列名,在連接字符串和執行該查詢但有時我需要對另一個查詢的結果使用此過程。在PostgreSQL中有沒有辦法做到這一點?

回答

0

如果要使用函數來處理查詢中的結果集,最好的選擇是SELECT ... INTO TEMPORARY TABLE,然後用臨時表名稱調用該函數。

在PostgreSQL函數中使用行集的難度令人惱火,因爲給定的瑣碎行集是多少。我意識到要做到這一點的唯一方法是使用refcursor,處理臨時表,或通過實現聚合或窗口函數。後兩個選項不允許您控制返回的行數,因此它們不適合您的目的。

函數不能引用調用該函數的CTE中的公用表表達式別名,因此您不能使用CTE創建虛擬表並將表名傳遞給該函數。例如顯示它不工作:

CREATE OR REPLACE FUNCTION dynsql(tname text, colname text) RETURNS SETOF RECORD AS 
$$ 
BEGIN 
    RETURN QUERY EXECUTE format('SELECT %I FROM %I', colname, tname); 
END; 
$$ LANGUAGE plpgsql; 

WITH dummy(col) AS (VALUES (1),(2),(3)) 
SELECT * FROM dynsql('dummy','col') t(id integer); 

結果:

ERROR: relation "dummy" does not exist 

...因爲在WITH表達的別名是本地WITH表達。 (能夠從函數中引用它會很好,但是這也會產生各種令人興奮的名稱衝突問題和SECURITY DEFINER函數的安全問題。)

儘管可以編寫PL/PgSQL函數這會消耗一個refcursor,這需要你帶一個查詢的遊標並將它傳遞給該函數。你不能只使用普通的函數調用語法。它的效率也很低,需要在函數中使用LOOP。我認爲這不會有太大的幫助。

實現該功能時,請使用EXECUTE format(...) USING ...來保持動態SQL不太可怕。見this earlier answer

1

要解決此問題,您將需要動態SQL。通常,在查詢中不能有動態表名。有關動態SQL的信息,請參閱http://www.postgresql.org/docs/9.2/static/ecpg-dynamic.html

如果您構造查詢爲:

FROM 
(
    SELECT val, ntile(10) OVER(ORDER BY val) AS tile FROM <subquery> t 
) AS tiled_table 

然後,當子查詢用括號括這個WIL工作。如果它們不在原始查詢中,您可以添加它們。

+0

雖然有一些缺點 - 如果你做出這樣的功能,你將無法在查詢中使用預準備語句,而且你需要在客戶端引用你的查詢參數,這很容易搞砸。 – Tometzky

+0

@Tometzky是的。這是一個潛在的巨大SQL注入安全漏洞,要求客戶端對標識符引用等非常偏執。 –

1

如果您總是返回相同的一組列,您可以創建一個返回函數,您可以傳遞表和列的名稱。可用於就像一個表:

create or replace function get_values(tablename text, columnname text) 
    returns table (id integer, foobar text) 
as 
$$ 
BEGIN 
    RETURN QUERY EXECUTE 'select id, '||columname||' as foobar from '||tablename; 
END; 
$$ 
language plpgsql; 

然後,每當你需要的值,可以使用:

select v.*, 
     t.foobar 
from get_values('table_1', 'some_column') v 
    join table_2 t on ... 

如果查詢返回不同數量的列eacht時候你需要它,這將不行。

+0

這(字符串連接)正是我想要避免的。 – synapse

+0

@synapse:如果你想指定列和表名作爲參數我沒有看到動態SQL的任何方式(除非你有一個非常有限的**選擇,在這種情況下,你可能會逃脫一個簡單的'if'級聯和「硬編碼」語句,但考慮到你的問題的性質,爲什麼你想避免動態SQL? –

+0

@synapse如果你需要動態SQL - 如果你想要動態列名或表名,那麼你幾乎堅持使用字符串連接,而不是使用'|| quote_ident(...)',我建議使用格式''I''參見http://stackoverflow.com/a/12995424/398670爲例。 –