我想创建一个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()函数时,出现以下错误:
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”的列取决于活动表内容中定义的活动集,我在调用该函数时无法预测这些活动。如何创建访问此信息的功能?
Simon和Kev的答案都是很好的答案,但是我最后要做的是将对数据库的调用分为两个查询:
就我而言,这是安全的,因为动态列列表不会频繁更改,因此我不必担心查询的目标数据在这些调用之间会发生变化。但是,否则,我的方法可能不起作用。