【问题标题】:Passing column names dynamically for a record variable in PostgreSQL为 PostgreSQL 中的记录变量动态传递列名
【发布时间】:2013-08-12 03:47:05
【问题描述】:

使用 PostgreSQL,表中第一条记录的列值存储在记录变量中。例如:设变量为:recordvar

recordvar.columnname

给出指定列名的值。我将在变量中定义columname

var := columnname

如果我用变量(即recordvar.var)替换columnname,则它不起作用。

请告诉我在这种情况下如何处理。以下是示例代码:

CREATE OR REPLACE FUNCTION getrowdata(id numeric, table_name character varying)
RETURNS SETOF void AS
$BODY$ 
DECLARE

srowdata record;
reqfield character varying;
value numeric;


BEGIN

RAISE NOTICE 'id: %',id; 
reqfield:= 'columnname';

EXECUTE 'select * from datas.'||table_name||' WHERE id = '||id into srowdata;

RAISE NOTICE 'srowdata: %',srowdata; 

RAISE NOTICE 'srowdatadata.columnname: %',srowdata.columnname;

value:= srowdata.reqfield;

RAISE NOTICE 'value: %',value;


END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;

【问题讨论】:

    标签: postgresql types plpgsql dynamic-sql hstore


    【解决方案1】:

    使用这个虚拟表

    CREATE TEMP TABLE foo (id int, my_num numeric);
    INSERT INTO foo VALUES (1, 12.34)
    

    首先,我简化并清理了您的示例:

    • 删除了一些与问题无关的噪音。

    • RETURNS SETOF void 几乎没有意义。我改用RETURNS void

    • 为了简单起见,我使用text 而不是character varying

    • 在使用动态 SQL 时,您必须防范 SQL 注入,在这种情况下,我使用 format()%IThere are other ways

    基本问题是 SQL 对类型和标识符非常严格。您正在使用动态表名称以及记录的动态字段名称 - 匿名记录你原来的例子。 Pl/pgSQL 不能很好地处理这个问题。 Postgres 不知道匿名记录里面是什么。只有将记录分配给众所周知的类型后,您才能引用各个字段。
    这是一个密切相关的问题,试图设置具有动态名称的记录字段:
    How to set value of composite variable field using dynamic SQL

    基本功能

    CREATE OR REPLACE FUNCTION getrowdata1(table_name text, id int)
      RETURNS void AS
    $func$ 
    DECLARE
       srowdata record;
       reqfield text := 'my_num';   -- assigning at declaration time for convenience
       value    numeric;
    BEGIN
    
    RAISE NOTICE 'id: %', id; 
    
    EXECUTE format('SELECT * FROM %I WHERE id = $1', table_name)
    USING  id
    INTO   srowdata;
    
    RAISE NOTICE 'srowdata: %', srowdata;
    
    RAISE NOTICE 'srowdatadata.my_num: %', srowdata.my_num;
    
    /* This does not work, even with dynamic SQL
    EXECUTE format('SELECT ($1).%I', reqfield)
    USING srowdata
    INTO value;
    
    RAISE NOTICE 'value: %', value;
    */
    
    END
    $func$ LANGUAGE plpgsql;
    

    呼叫:

    SELECT * from getrowdata1('foo', 1);
    

    注释部分会引发异常:

    无法识别记录数据类型中的列“my_num”:SELECT * from getrowdata(1,'foo')

    hstore

    您需要为此安装附加模块hstore。每个数据库一次:

    CREATE EXTENSION hstore;
    

    那么一切都可以这样工作:

    CREATE OR REPLACE FUNCTION getrowdata2(table_name text, id int)
      RETURNS void AS
    $func$ 
    DECLARE
       hstoredata hstore;
       reqfield   text := 'my_num';
       value      numeric;
    BEGIN
    
    RAISE NOTICE 'id: %', id; 
    
    EXECUTE format('SELECT hstore(t) FROM %I t WHERE id = $1', table_name)
    USING  id
    INTO   hstoredata;
    
    RAISE NOTICE 'hstoredata: %', hstoredata;
    
    RAISE NOTICE 'hstoredata.my_num: %', hstoredata -> 'my_num';
    
    value := hstoredata -> reqfield;
    
    RAISE NOTICE 'value: %', value;
    
    END
    $func$ LANGUAGE plpgsql;
    

    呼叫:

    SELECT * from getrowdata2('foo', 1);
    

    多态类型

    无需安装额外模块的替代方案。

    由于您在记录变量中选择了一整行,因此每个定义都有一个定义明确的类型。用它。关键字是polymorphic types

    CREATE OR REPLACE FUNCTION getrowdata3(_tbl anyelement, id int)
      RETURNS void AS
    $func$ 
    DECLARE
       reqfield text := 'my_num';
       value    numeric;
    BEGIN
    
    RAISE NOTICE 'id: %', id; 
    
    EXECUTE format('SELECT * FROM %s WHERE id = $1', pg_typeof(_tbl))
    USING  id
    INTO   _tbl;
    
    RAISE NOTICE '_tbl: %', _tbl;
    
    RAISE NOTICE '_tbl.my_num: %', _tbl.my_num;
    
    EXECUTE 'SELECT ($1).' || reqfield   -- requfield must be SQLi-safe or escape
    USING _tbl
    INTO  value;
    
    RAISE NOTICE 'value: %', value;
    
    END
    $func$ LANGUAGE plpgsql;
    

    呼叫:

    SELECT * from getrowdata3(NULL::foo, 1);
    

    -> SQLfiddle

    【讨论】:

    • 您好,我刚开始错误:记录“_tbl”没有字段“my_num”,请您帮忙
    • @ismail:请说得更具体一些,然后开始一个新的问题。评论不是地方。您可以随时链接到此以供参考。
    猜你喜欢
    • 1970-01-01
    • 2017-09-24
    • 2012-04-01
    • 2012-07-21
    • 2020-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多