Обновление таблицы на основе JSON внутри функции PostgreSQL

#sql #postgresql #function

Вопрос:

Я пишу функцию plpgsql, которая должна обновлять таблицу на основе предоставленного объекта JSON. JSON содержит представление таблицы со всеми теми же столбцами, что и в самой таблице.

В настоящее время функция выглядит следующим образом:

 CREATE OR REPLACE FUNCTION update (updated json)  BEGIN  /* transfrom json to table */ WITH updated_vals AS (  SELECT  *  FROM  json_populate_recordset(NULL::my_table, updated) ),  /* Retrieve all columns from mytable and also with reference to updated_vals table */ cols AS (  SELECT  string_agg(quote_ident(columns), ',') AS table_cols,  string_agg('updated_vals.' || quote_ident($1), ',') AS updated_cols  FROM  information_schema  WHERE  table_name = 'my_table' -- table name, case sensitive  AND table_schema = 'public' -- schema name, case sensitive  AND column_name lt;gt; 'id' -- all columns except id and user_id  AND column_name lt;gt; 'user_id' ),  /* Define the table columns separately */ table_cols AS (  SELECT  table_cols  FROM  cols ),  /* Define the updated columns separately */ updated_cols AS (  SELECT  updated_cols  FROM  cols)  /* Execute the update statement */  EXECUTE 'UPDATE my_table'  || ' SET (' || table_cols::text || ') = (' || updated_cols::text || ') '  || ' FROM updated_vals '  || ' WHERE my_table.id = updated_vals.id '  || ' AND my_table.user_id = updated_vals.user_id';  COMMIT; END;  

Я заметил , что комбинация WITH предложения в сочетании с the EXECUTE всегда будет вызывать ошибку syntax error at or near EXECUTE , даже если они очень просты и понятны. Действительно ли это так, и если да, то каков был бы альтернативный подход для предоставления требуемых переменных ( updated_vals , table_cols и updated_cols ) EXECUTE ?

Если у вас есть какие-либо другие улучшения в этом коде, я был бы рад их увидеть, потому что я очень новичок в sql/plpgsql.

Ответ №1:

Если вы написали имя таблицы ( my_table ) в своей функции, это означает, что вы всегда будете обновлять только одну указанную таблицу из данных JSON. Из-за этого вы можете записывать имена таблиц и столбцов в свою функцию вручную, не используя information_schema . Это простой и легкий путь.

Например:

 CREATE OR REPLACE FUNCTION rbac.update_users_json(updated json) RETURNS boolean  LANGUAGE plpgsql AS $function$ begin   update rbac.users usr   set   username = jsn.username,   first_name = jsn.first_name,   last_name = jsn.last_name  from (  select * from json_populate_recordset(NULL::rbac.users, updated)  ) jsn   where jsn.id = usr.id;    return true;    END; $function$ ;  

Комментарии:

1. Вы действительно правы, заметив, что я написал my_table . Однако код, который я представил, является стилизованным примером реального кода. my_table будет заменена динамической таблицей.

Ответ №2:

Для динамических таблиц:

 CREATE OR REPLACE FUNCTION rbac.update_users_json_dynamic(updated json) RETURNS boolean  LANGUAGE plpgsql AS $function$ declare  f record;  exec_sql text;  sep text; begin   exec_sql = 'update rbac.users usr set ' || E'n';  sep = '';   for f in   select clm.column_name   from   information_schema."tables" tbl  inner join   information_schema."columns" clm on   clm.table_name = tbl.table_name   and clm.table_schema = tbl.table_schema   where   tbl.table_schema = 'test'   and tbl.table_name = 'users'  and clm.column_name lt;gt; 'id'  loop   exec_sql = exec_sql || sep || f.column_name || ' = ' || 'jsn.' || f.column_name;  sep = ', ' || E'n';  end loop;    exec_sql = exec_sql || E'n' || 'from (select * from json_populate_recordset(NULL::rbac.users, ''' ||   updated::text || ''')) jsn ' || E'n' || 'where jsn.id = usr.id';    execute exec_sql;   return true;    END; $function$ ;