2017-09-15 30 views
0

我的表格每年都包含相同類型的數據,但收集的數據略有不同,因爲它們可能沒有相同的字段。嘗試在PL/PgSQL中創建動態查詢字符串以在PostgreSQL中創建DRY函數9.6

d_abc_2016 
d_def_2016 
d_ghi_2016 
d_jkl_2016 

有用於每個表中的某些常數:company_idemployee_idsalary

但是,每個人可能有也可能沒有這些字段用於計算總激勵:bonus,commission,cash_incentives。還有很多,但只是以這些爲例。全部numeric

我應該注意,在這一點上,用戶只有能力運行SELECT報表。

我希望能夠做的是這樣的:

  1. 給用戶在SELECT調用,並指定自己的領域,除了通話
  2. 傳遞表名的能力正在使用進入功能在條件邏輯用於確定查詢字符串應該如何構造,爲最終total_incentives計算除了傳遞整個表,這樣一噸的參數沒有被傳遞到函數

基本上這:

SELECT employee_id, salary, total_incentives(t, 'd_abc_2016') 
FROM d_abc_2016 t; 

所以被稱爲將計算total_incentives這是numeric該功能employee_id,也表明他們salary。但用戶可能會選擇添加其他字段來查看。

對於函數,因爲total_incentives函數中使用的字段會因表而異,所以我需要創建邏輯來動態構造查詢字符串。

CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text) 
    RETURNS numeric AS 
$$ 
DECLARE 
    -- table name lower case in case user typed wrong 
    tbl   varchar(255) := lower($2; 

    -- parse out the table code to use in conditional logic 
    tbl_code  varchar(255) := split_part(survey, '_', 2); 

    -- the starting point if the query string 
    base_calc varchar(255) := 'salary + ' 

    -- query string 
    query_string varchar(255); 

    -- have to declare this to put computation INTO 
    total_incentives_calc numeric; 
BEGIN 
    IF tbl_code = 'abc' THEN 
     query_string := base_calc || 'bonus'; 
    ELSIF tbl_code = 'def' THEN 
     query_string := base_calc || 'bonus + commission'; 
    ELSIF tbl_code = 'ghi' THEN 
     -- etc... 
    END IF; 

    EXECUTE format('SELECT $1 FROM %I', tbl) 
    INTO total_incentives_calc 
    USING query_string; 

    RETURN total_incentives_calc; 
END; 
$$ 
LANGUAGE plpgsql; 

這導致:

ERROR: invalid input syntax for type numeric: "salary + bonus" 
CONTEXT: PL/pgSQL function total_incentives(anyelement,text) line 16 at EXECUTE 

因爲它應該返回一組numeric值。將其更改爲以下內容:

CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text) 
    RETURNS SETOF numeric AS 
$$ 
... 
    RETURN; 

獲取相同的錯誤。

好吧,也許它是一個表格,它試圖返回。

CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text) 
    RETURNS TABLE(tot_inc numeric) AS 
$$ 
... 

獲取相同的錯誤。

真的,任何變化都會產生這樣的結果。所以真的不知道如何讓這個工作。

RESULT QUERY,RESULT NEXTRESULT QUERY EXECUTE

https://www.postgresql.org/docs/9.6/static/plpgsql-control-structures.html

RESULT QUERY將無法​​工作,因爲它從我可以告訴一個硬編碼查詢,不會在變量取。

RESULT NEXT遍歷每個記錄,我不認爲會適合我的需求和看起來這將是很慢...它需要從我可以告訴一個硬編碼查詢。

RESULT QUERY EXECUTE聽起來很有希望。

-- EXECUTE format('SELECT $1 FROM %I', tbl) 
-- INTO total_incentives_calc 
-- USING query_string; 

RETURN QUERY 
    EXECUTE format('SELECT $1 FROM %I', tbl) 
    USING query_string; 

並獲得:

ERROR: structure of query does not match function result type 
DETAIL: Returned type character varying does not match expected type numeric in column 1. 
CONTEXT: PL/pgSQL function total_incentives(anyelement,text) line 20 at RETURN QUERY 

應該返回numeric

最後,我可以得到這個工作,但它不會幹。我寧願不使用重複代碼爲每個表創建一堆單獨的函數。大部分時間我都看到了工作的例子有一個函數整個查詢,被稱爲像這樣:

SELECT total_incentives(d_abc_2016, 'd_abc_2016'); 

因此,任何附加列必須在功能作爲指定:鑑於

EXECUTE format('SELECT employee_id...) 

用戶將只能在查詢中運行SELECT,這實在不是一種選擇。他們需要指定他們希望在查詢中看到的任何附加列。

我已經發布了一個類似的問題,但被告知它不清楚,所以希望這個更長的版本將更清楚地解釋我正在嘗試做什麼。

回答

1

列名和表名不應該用作USING子句傳遞的查詢參數。

大概行:

RETURN QUERY 
    EXECUTE format('SELECT $1 FROM %I', tbl) 
    USING query_string; 

應該是:

RETURN QUERY 
    EXECUTE format('SELECT %s FROM %I', query_string, tbl); 

這種情況是例子,爲什麼太DRY原則有時是有問題的。如果你直接編寫它,那麼你的代碼將會更簡單,更簡潔並且可能更短。

動態SQL是最後一個解決方案 - 不是第一個。只有當動態SQL比不使用動態SQL時代碼短得多時才使用動態SQL。

+0

很酷,得到了查詢運行。謝謝您的幫助!現在整理並防止它將返回的記錄數量乘以總記錄數量。 – sockpuppet