sql - 我可以让 postgres plpgsql 函数返回可变列记录吗?

标签 sql postgresql stored-procedures plpgsql

我想创建一个 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

当然,诀窍在于标记为“Label 1”和“Label”的列 2' 取决于内容中定义的事件集 事件表,我在调用函数时无法预测。如何 我可以创建一个函数来访问这些信息吗?

最佳答案

如果您确实想动态创建这样的表,也许只需在函数中创建一个临时表,这样它就可以包含您想要的任何列。让函数将所有行插入表中而不是返回它们。该函数可以仅返回表的名称,或者您可以只返回一个您知道的确切表名称。运行该函数后,您只需从表中选择数据即可。该函数还应该检查临时表是否存在,以便删除或截断它。

关于sql - 我可以让 postgres plpgsql 函数返回可变列记录吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1613921/

相关文章:

SQL:卡片的良好数据库设计?

mysql - 网站的两个用户之间的对话类型收件箱消息

sql - PostgreSQL:是否有可能使用触发器删除可以作为更新

sql - 在 SQL (T-SQL) 中做高级逻辑 - 匹配算法

oracle - 将存储过程的权限授予另一个 Oracle 用户

php - MySQL 查询当前周的 yearweek 星期四开始,星期三结束

php - 如何在收件箱中获取最后一条消息?

sql - 删除 Postgresql 中的冗余时间戳范围

postgresql - 是否可以从 RETURNING 子句中获取 DISTINCT 行?

MySQL服务器存储过程语法错误