2009-10-23 68 views
1

我想創建一個postgres函數來構建它的一組列 即時返回;簡而言之,它應該包含一個密鑰列表,每個密鑰構建一個列,並且返回一個記錄,其中包含列的所有內容,即 。簡單地說,這裏是代碼:我可以使用postgres plpgsql函數返回變量列記錄嗎?

CREATE OR REPLACE FUNCTION reports.get_activities_for_report() RETURNS int[] AS $F$ 
BEGIN 
    RETURN ARRAY(SELECT activity_id FROM public.activity WHERE activity_id NOT IN (1, 2)); 
END; 
$F$ 
LANGUAGE plpgsql 
STABLE; 

CREATE OR REPLACE FUNCTION reports.get_amount_of_time_query(format TEXT, _activity_id INTEGER) RETURNS TEXT AS $F$ 
DECLARE 
    _label TEXT; 
BEGIN 
    SELECT label INTO _label FROM public.activity WHERE activity_id = _activity_id; 
    IF _label IS NOT NULL THEN 
     IF lower(format) = 'percentage' THEN 
      RETURN $$TO_CHAR(100.0 *$$ || 
      $$ (SUM(CASE WHEN activity_id = $$ || _activity_id || $$ THEN EXTRACT(EPOCH FROM ended - started) END) /$$ || 
      $$ SUM(EXTRACT(EPOCH FROM ended - started))),$$ || 
      $$ '990.99 %') AS $$ || quote_ident(_label); 
     ELSE 
      RETURN $$SUM(CASE WHEN activity_id = $$ || _activity_id || $$ THEN ended - started END)$$ || 
      $$ AS $$ || quote_ident(_label); 
     END IF; 
    END IF; 
END; 
$F$ 
LANGUAGE plpgsql 
STABLE; 

CREATE OR REPLACE FUNCTION reports.build_activity_query(format TEXT, activities int[]) RETURNS TEXT AS $F$ 
DECLARE 
    _activity_id INT; 
    query TEXT; 
    _activity_count INT; 
BEGIN 
    _activity_count := array_upper(activities, 1); 
    query := $$SELECT agent_id, portal_user_id, SUM(ended - started) AS total$$; 
    FOR i IN 1.._activity_count LOOP 
     _activity_id := activities[i]; 

     query := query || ', ' || reports.get_amount_of_time_query(format, _activity_id); 
    END LOOP; 
    query := query || $$ FROM public.activity_log_final$$ || 
    $$ LEFT JOIN agent USING (agent_id)$$ || 
    $$ WHERE started::DATE BETWEEN actual_start_date AND actual_end_date$$ || 
    $$ GROUP BY agent_id, portal_user_id$$ || 
    $$ ORDER BY agent_id$$; 
    RETURN query; 
END; 
$F$ 
LANGUAGE plpgsql 
STABLE; 

CREATE OR REPLACE FUNCTION reports.get_agent_activity_breakdown(format TEXT, start_date DATE, end_date DATE) RETURNS SETOF RECORD AS $F$ 
DECLARE 
    actual_end_date DATE; 
    actual_start_date DATE; 
    query TEXT; 
    _rec RECORD; 
BEGIN 
    actual_start_date := COALESCE(start_date, '1970-01-01'::DATE); 
    actual_end_date := COALESCE(end_date, now()::DATE); 
    query := reports.build_activity_query(format, reports.get_activities_for_report()); 

    FOR _rec IN EXECUTE query LOOP 
     RETURN NEXT _rec; 
    END LOOP; 
END 
$F$ 
LANGUAGE plpgsql; 

這將構建這樣看起來(大約)查詢:

SELECT agent_id, 
    portal_user_id, 
    SUM(ended - started) AS total, 
    SUM(CASE WHEN activity_id = 3 THEN ended - started END) AS "Label 1" 
    SUM(CASE WHEN activity_id = 4 THEN ended - started END) AS "Label 2" 
FROM public.activity_log_final 
    LEFT JOIN agent USING (agent_id) 
WHERE started::DATE BETWEEN actual_start_date AND actual_end_date 
GROUP BY agent_id, portal_user_id 
ORDER BY agent_id 

當我嘗試調用get_agent_activity_breakdown()功能,我得到這個錯誤:

psql:2009-10-22_agent_activity_report_test.sql:179: ERROR: a column definition list is required for functions returning "record" 
CONTEXT: SQL statement "SELECT * FROM reports.get_agent_activity_breakdown('percentage', NULL, NULL)" 
PL/pgSQL function "test_agent_activity" line 92 at SQL statement 

當然,訣竅在於,標籤爲'標籤1'和'標籤 2'的列依賴於內容中定義的一組活動活動表,我無法預測何時調用該函數。 如何創建一個函數來訪問這些信息?

回答

0

兩個西蒙和千電子伏的答案是好的,但我落得這樣做拆分到數據庫的調用到兩個查詢:

  1. 構建使用查詢構造方法我包含在查詢有問題,將其返回給應用程序。
  2. 直接調用查詢,並返回該數據。

這對我來說很安全,因爲動態列表不會經常變化,所以我不需要擔心這些調用之間查詢的目標數據發生變化。否則,雖然,我的方法可能無法正常工作。

2

如果你真的想動態創建這樣的表,也許只是在函數中創建一個臨時表,以便它可以有任何你想要的列。讓函數將所有行插入到表中而不是返回它們。該函數只能返回表的名稱,或者只能有一個確切的表名。運行該功能後,您可以從表中選擇數據。該函數還應該檢查臨時表是否存在,以便刪除或截斷它。

2

Simon的答案最終總體上可能會更好,我只是告訴你如何做到這一點,而不會改變你有什麼。

the docs

from_item can be one of:

... 
function_name ([ argument [, ...] ]) [ AS ] alias [ (column_alias [, ...] | column_definition [, ...]) ] 
function_name ([ argument [, ...] ]) AS (column_definition [, ...]) 

換句話說,後來它說:

If the function has been defined as returning the record data type, then an alias or the key word AS must be present, followed by a column definition list in the form ( column_name data_type [, ... ]). The column definition list must match the actual number and types of columns returned by the function.

我覺得別名東西只有一個選擇,如果你在什麼地方預定義的類型(比如,如果你模仿預定義表格的輸出,或者實際上使用了CREATE TYPE ...請不要在此引用我)

所以,我想你會需要這樣的東西:你在於...

SELECT * 
    FROM reports.get_agent_activity_breakdown('percentage', NULL, NULL) 
    AS (agent_id integer, portal_user_id integer, total something, ...) 

的問題。在執行查詢之前,您需要知道所有列的名稱和類型 - 因此您最終會選擇public.activity兩次。

相關問題