我想创建一个postgres函数来构建它的列集 即时返回;简而言之,它应该包含一个键列表,构建 每个键一列,并返回包含任何设置的记录 列是。简而言之,这是代码:
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 :(得分:2)
如果你真的想动态创建这样的表,可能只是在函数中创建一个临时表,这样它就可以有你想要的任何列。让函数将所有行插入表中而不是返回它们。该函数只能返回表的名称,或者您只能拥有一个您知道的确切表名。运行该功能后,您只需从表中选择数据即可。该函数还应检查临时表是否存在,以便删除或截断它。
答案 1 :(得分:2)
西蒙的答案最终可能会更好,我只是告诉你如何在不改变你的情况的情况下做到这一点。
来自the docs:
from_item可以是以下之一:
...
function_name ( [ argument [, ...] ] ) [ AS ] alias [ ( column_alias [, ...] | column_definition [, ...] ) ]
function_name ( [ argument [, ...] ] ) AS ( column_definition [, ...] )
换句话说,后来它说:
如果该功能已被定义为 然后返回记录数据类型 别名或关键字AS必须是 目前,后面是一个专栏 表格中的定义清单( column_name data_type [,...])。该 列定义列表必须匹配 列的实际数量和类型 由函数返回。
我认为别名只是一个选项,如果你已经在某个地方预定了一个类型(比如你是在模仿预定义表的输出,或者实际上已经使用了CREATE TYPE ......请不要引用我的话但是。)
所以,我认为你需要这样的东西:
SELECT *
FROM reports.get_agent_activity_breakdown('percentage', NULL, NULL)
AS (agent_id integer, portal_user_id integer, total something, ...)
问题在于 ... 。在执行查询之前,您需要知道所有列的名称和类型 - 因此您最终将选择public.activity两次。
答案 2 :(得分:0)
Simon和Kev的答案都很好,但我最终做的是将对数据库的调用分成两个查询:
这在我的情况下是安全的,因为动态列列表不会经常更改,因此我不需要担心查询的目标数据在这些调用之间发生变化。否则,我的方法可能不起作用。
答案 3 :(得分:0)
你不能改变输出列的数量,但你可以使用refcursor,你可以返回打开的游标。
更多关于http://okbob.blogspot.com/2008/08/using-cursors-for-generating-cross.html