динамическая вставка plpgsql с именем таблицы в качестве переменной и переменной массива в предложении where - PullRequest
0 голосов
/ 19 февраля 2019

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

CREATE OR REPLACE FUNCTION f_dynamic_sql()
  RETURNS void
  LANGUAGE plpgsql
AS
$body$

DECLARE 
rec record;
iterator float4 := 1;
tbl_name text;

BEGIN
DROP TABLE IF EXISTS t_ar; CREATE TEMP TABLE t_ar (reg text, zones text[]);
INSERT INTO t_ar VALUES 
('NA', '{"US","UG","UC","UR"}'),
('NE', '{"UK", "SP"}'),
('LA', '{"CA","EC","WC","EC","WC"}');

FOR rec IN SELECT zones from t_ar
    LOOP 
  tbl_name := 'schema.table_' || iterator; 
  EXECUTE format('INSERT INTO %s 
          SELECT
            DATE_PART(''MONTH'', month)::INT AS month,
            SUM(COALESCE(prev_year,0))::INT AS py,
            SUM(COALESCE(last_year,0))::INT AS ly
        FROM org_table
        WHERE load_area IN (SELECT UNNEST(rec.zones))
        GROUP BY 1
        ORDER BY 1', tbl_name); 

    iterator := iterator + 1;
END LOOP;
END;
$body$
  VOLATILE
  COST 100;

Это нормально выполняется из формата выполнения, но затем яне может поместить имя таблицы как переменную, но внутри формата выполнения это показывает синтаксическую ошибку в запросе SQL.SQL-запрос работает нормально снаружи.

1 Ответ

0 голосов
/ 19 февраля 2019

Вам необходимо использовать заполнители и аргументы в функции format().

Практический совет при использовании EXECUTE format() - используйте RAISE NOTICE '%', вместо EXECUTE и запустите функцию в psql, чтобы увидеть, какие запросы она фактически генерирует.Если вы уверены, что сгенерированные запросы верны, замените RAISE NOTICE '%', на EXECUTE.Пример:

...
FOR rec IN SELECT reg, zones FROM t_ar
LOOP 
RAISE NOTICE '%', format('
    INSERT INTO my_schema.table_%s 
    SELECT
        DATE_PART(''MONTH'', month)::INT AS month,
        SUM(COALESCE(prev_year,0))::INT AS py,
        SUM(COALESCE(last_year,0))::INT AS ly
    FROM org_table
    WHERE load_area = ANY (%L)
    GROUP BY 1
    ORDER BY 1', rec.reg, rec.zones); 
END LOOP;
...
...