You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改PL/pgSQL函数实现动态列数的查询结果返回?

问题描述

现有SQL可查询v_deg='3'时的label列和对应频次freq列,需求是根据v_deg的所有不同值生成动态频次列(格式:label|freq(v_deg='1')|freq(v_deg='2')|...)。尝试编写PL/pgSQL函数GET_REM_T4时触发错误:

ERROR: relation "current_table" does not exist
LINE 1: SELECT DISTINCT v_deg FROM current_table

基础查询SQL:

SELECT
    dip
    || ' '
    || (
        SELECT lib1 || ' ' || lib2
        FROM bds
        WHERE dip_s = dip
    ) AS label,
    COUNT(dip) AS freq
FROM current_table
WHERE v_deg = '3'
GROUP BY dip;

原错误函数:

CREATE OR REPLACE FUNCTION GET_REM_T4(current_table text)
RETURNS SETOF record
LANGUAGE plpgsql
AS $$
DECLARE
    col_name text;
BEGIN
    FOR col_name IN 
        SELECT DISTINCT v_deg FROM current_table
    LOOP
        EXECUTE 'SELECT dip || '' '' || (
            SELECT lib1 || '' '' || lib2
            FROM bds
            WHERE dip_s = '||current_table.dip||'
            AND v_deg = '||col_name||
            ' )'
        INTO col_name;
        
        RETURN NEXT (col_name);
    END LOOP;
END;
$$;
错误原因
  1. 函数参数current_table是文本类型,直接在静态SQL中引用会被PostgreSQL识别为物理表名,而非传入的参数值,因此找不到名为current_table的表。
  2. 原函数逻辑完全偏离需求:仅循环查询label值,未实现按v_deg生成动态频次列的透视功能。
  3. SQL拼接存在语法错误与注入风险,且错误地在动态SQL外部引用表字段current_table.dip。
修正后的函数实现

以下函数会自动获取目标表中所有唯一的v_deg值,动态生成透视SQL,返回label列加上每个v_deg对应的频次列:

CREATE OR REPLACE FUNCTION GET_REM_T4(p_table_name text)
RETURNS TABLE (label text, freq_columns jsonb)
LANGUAGE plpgsql
AS $$
DECLARE
    v_deg_values text[];
    pivot_sql text;
BEGIN
    -- 1. 获取所有唯一的v_deg值并排序
    EXECUTE format('SELECT ARRAY_AGG(DISTINCT v_deg ORDER BY v_deg) FROM %I', p_table_name)
    INTO v_deg_values;

    -- 2. 构建透视SQL,为每个v_deg生成对应的频次统计列
    pivot_sql := format(
        'SELECT 
            dip || '' '' || (SELECT lib1 || '' '' || lib2 FROM bds WHERE dip_s = ct.dip) AS label,
            jsonb_build_object(%s) AS freq_columns
        FROM %I ct
        GROUP BY ct.dip
        ORDER BY ct.dip',
        string_agg(format('%L, COUNT(CASE WHEN v_deg = %L THEN 1 END)', 'freq_v_deg_'||val, val), ', '),
        p_table_name
    )
    FROM unnest(v_deg_values) val;

    -- 3. 执行动态SQL并返回结果
    RETURN QUERY EXECUTE pivot_sql;
END;
$$;

如果需要直接返回独立的频次列而非JSON格式,可使用以下版本(调用时需指定列定义):

CREATE OR REPLACE FUNCTION GET_REM_T4(p_table_name text, OUT result record)
LANGUAGE plpgsql
AS $$
DECLARE
    v_deg_values text[];
    pivot_sql text;
    col_defs text;
BEGIN
    -- 获取所有唯一v_deg值
    EXECUTE format('SELECT ARRAY_AGG(DISTINCT v_deg ORDER BY v_deg) FROM %I', p_table_name)
    INTO v_deg_values;

    -- 生成返回列的定义语句
    col_defs := 'label text, ' || string_agg(format('freq_v_deg_%s bigint', val), ', ') 
                FROM unnest(v_deg_values) val;

    -- 构建透视SQL
    pivot_sql := format(
        'SELECT 
            dip || '' '' || (SELECT lib1 || '' '' || lib2 FROM bds WHERE dip_s = ct.dip) AS label,
            %s
        FROM %I ct
        GROUP BY ct.dip
        ORDER BY ct.dip',
        string_agg(format('COUNT(CASE WHEN v_deg = %L THEN 1 END)', val), ', '),
        p_table_name
    )
    FROM unnest(v_deg_values) val;

    -- 执行并返回动态列结果
    EXECUTE format('SELECT * FROM (%s) AS t(%s)', pivot_sql, col_defs) INTO result;
    RETURN;
END;
$$;
使用示例
  • JSON格式版本调用:
SELECT * FROM GET_REM_T4('your_target_table');
  • 动态列版本调用(需根据实际v_deg值指定列定义):
SELECT * FROM GET_REM_T4('your_target_table') 
AS t(label text, freq_v_deg_1 bigint, freq_v_deg_2 bigint, freq_v_deg_3 bigint);

内容的提问来源于stack exchange,提问作者rochard4u

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 16:53:22