Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
830 views
in Technique[技术] by (71.8m points)

sql - How to use dynamic column names in an UPDATE or SELECT statement in a function?

In PostgreSQL 9.1, PL/pgSQL, given a query:

select fk_list.relname from ...

where relname is of type name (e.g., "table_name").

How do you get the appropriate value for "relname" that can be used directly in an UPDATE statement as:

Update <relname> set ...

within the PL/pgSQL script?

Using quote_ident(r.relname) as:

Update quote_ident(r.relname) Set ...

fails with:

syntax error at or near "(" LINE 55: UPDATE quote_ident(r.relname) ....

The complete code I am working with:

CREATE FUNCTION merge_children_of_icd9 (ocicd9 text, 
                      ocdesc text, ncicd9 text, ncdesc text)
RETURNS void AS $BODY$
DECLARE
  r RECORD;
BEGIN
  FOR r IN
    WITH fk_actions ( code, action ) AS (
      VALUES ('a', 'error'), 
             ('r', 'restrict'), 
             ('c', 'cascade'),
             ('n', 'set null'),
             ('d', 'set default')
    ), fk_list AS (
      SELECT pg_constraint.oid AS fkoid, conrelid, confrelid::regclass AS parentid,
             conname, relname, nspname,
             fk_actions_update.action AS update_action,
             fk_actions_delete.action AS delete_action,
             conkey AS key_cols
      FROM pg_constraint
      JOIN pg_class ON conrelid = pg_class.oid
      JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
      JOIN fk_actions AS fk_actions_update ON confupdtype = fk_actions_update.code
      JOIN fk_actions AS fk_actions_delete ON confdeltype = fk_actions_delete.code
      WHERE contype = 'f'
    ), fk_attributes AS (
      SELECT fkoid, conrelid, attname, attnum
      FROM fk_list
      JOIN pg_attribute ON conrelid = attrelid AND attnum = ANY(key_cols)
      ORDER BY fkoid, attnum
    ), fk_cols_list AS (
      SELECT fkoid, array_agg(attname) AS cols_list
      FROM fk_attributes
      GROUP BY fkoid
    )
    SELECT fk_list.fkoid, fk_list.conrelid, fk_list.parentid, fk_list.conname, 
           fk_list.relname, fk_cols_list.cols_list
    FROM fk_list
    JOIN fk_cols_list USING (fkoid)
    WHERE parentid = 'icd9'::regclass
  LOOP
    RAISE NOTICE 'now in loop. relname is %', quote_ident(r.relname);
    RAISE NOTICE 'cols_list[1] is %', quote_ident(r.cols_list[1]);
    RAISE NOTICE 'cols_list[2] is %', quote_ident(r.cols_list[2]);
    RAISE NOTICE 'now doing update';

    UPDATE quote_ident(r.relname) SET r.cols_list[1] = ncicd9, r.cols_list[2] = ncdesc
    WHERE r.cols_list[1] = ocicd9 AND r.cols_list[2] = ocdesc;

    RAISE NOTICE 'finished update'; 
  END LOOP;     
  RETURN;
END $BODY$ LANGUAGE plpgsql VOLATILE;   

-- select merge_children_of_icd9('', 'aodm type 2', '', 'aodm, type 2'); 

I'm sure this kind of thing is done often, but I can't seem to find anything like it using PostgreSQL. Is there a better way?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In an UPDATE statement in PL/pgSQL, the table name has to be given as a literal. If you want to dynamically set the table name and the columns, you should use the EXECUTE command and paste the query string together:

EXECUTE 'UPDATE ' || quote_ident(r.relname) ||
       ' SET ' || quote_ident(r.cols_list[1]) || ' = $1, ' || 
                  quote_ident(r.cols_list[2]) || ' = $2' ||
       ' WHERE ' || quote_ident(r.cols_list[1]) || ' = $3 AND ' ||
                    quote_ident(r.cols_list[2]) || ' = $4'
USING ncicd9, ncdesc, ocicd9, ocdesc;

The USING clause can only be used for substituting data values, as shown above.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...