使用存储过程插入列列表
Insert list of column with stored procedure
我有一个 Matlab 脚本,我想在其中使用存储过程而不是将所有指令发送到服务器。问题是我必须处理包含不同列的字符串变量,例如:
listColumns = '(column1, column2, column3, ... columnN)';
我想知道的是如何使用动态 SQL 创建一个存储过程,以将列列表作为字符串输入参数进行插入?是否可能或者我是否需要更改我的脚本以使其更容易?
这对我来说很新,我尝试了更简单的存储过程,我只需要一列并且效果很好,但我暂时坚持使用它:
CREATE OR REPLACE FUNCTION myfunction(
tablename regclass,
listColumns regclass,
listColumnsTarget regclass)
RETURNS void
LANGUAGE = 'plpgsql'
AS $$
BEGIN
EXECUTE format('INSERT INTO ' %s %s '(SELECT ' %s ' FROM anotherTable)',
tablename, listeColumns, listColumnsTarget);
END
$$;
我希望让存储过程正常工作,但它失败了,我收到语法错误...
regclass
类型只能包含某个数据库对象的一个标识(由 pg_class
系统 table 的一行描述)。您不能使用 regclass
类型来传递列名,因为只有列不是 table。 regclass
类型是特殊的并根据系统检查值 tables.
postgres=# select 'xxx'::regclass;
ERROR: relation "xxx" does not exist
LINE 1: select 'xxx'::regclass;
^
Postgres 有特殊类型的 SQL 标识符 - name
。如果你想传递更多的值,你应该使用一个名称数组 - name[]
。但是对于来自外部的输入,最好使用通常的 text
类型并在函数内部处理必要的操作。在这种情况下,使用 name
类型对我们没有帮助。
需要什么?转义——这是针对语法错误或 SQL 注入的清理。例如 - 标识符:fubu, boo-boo, AA
应该被翻译成安全的标识符列表:fubi, "boo-boo", "AA"
。这一步很重要
Postgres 没有这个 fork for list 的任何函数,但我们可以编写自己的函数:
CREATE OR REPLACE FUNCTION sanitize_identifiers(text)
RETURNS text AS $$
SELECT string_agg(quote_ident(v), ',')
FROM unnest(string_to_array(, ',')) g(v)
$$ LANGUAGE sql;
现在,我们可以这样写函数:
CREATE OR REPLACE FUNCTION foo(tablename regclass,
columns text)
RETURNS void AS $$
BEGIN
-- should to print valid SQL
RAISE NOTICE '%',
FORMAT('INSERT INTO %s SELECT %s FROM tab',
tablename,
sanitize_identifiers(columns));
END;
$$ LANGUAGE plpgsql;
结果:
postgres=# select foo('Test','a,b,c,C');
NOTICE: INSERT INTO test SELECT a,b,c,"C" FROM tab
我有一个 Matlab 脚本,我想在其中使用存储过程而不是将所有指令发送到服务器。问题是我必须处理包含不同列的字符串变量,例如:
listColumns = '(column1, column2, column3, ... columnN)';
我想知道的是如何使用动态 SQL 创建一个存储过程,以将列列表作为字符串输入参数进行插入?是否可能或者我是否需要更改我的脚本以使其更容易?
这对我来说很新,我尝试了更简单的存储过程,我只需要一列并且效果很好,但我暂时坚持使用它:
CREATE OR REPLACE FUNCTION myfunction(
tablename regclass,
listColumns regclass,
listColumnsTarget regclass)
RETURNS void
LANGUAGE = 'plpgsql'
AS $$
BEGIN
EXECUTE format('INSERT INTO ' %s %s '(SELECT ' %s ' FROM anotherTable)',
tablename, listeColumns, listColumnsTarget);
END
$$;
我希望让存储过程正常工作,但它失败了,我收到语法错误...
regclass
类型只能包含某个数据库对象的一个标识(由 pg_class
系统 table 的一行描述)。您不能使用 regclass
类型来传递列名,因为只有列不是 table。 regclass
类型是特殊的并根据系统检查值 tables.
postgres=# select 'xxx'::regclass;
ERROR: relation "xxx" does not exist
LINE 1: select 'xxx'::regclass;
^
Postgres 有特殊类型的 SQL 标识符 - name
。如果你想传递更多的值,你应该使用一个名称数组 - name[]
。但是对于来自外部的输入,最好使用通常的 text
类型并在函数内部处理必要的操作。在这种情况下,使用 name
类型对我们没有帮助。
需要什么?转义——这是针对语法错误或 SQL 注入的清理。例如 - 标识符:fubu, boo-boo, AA
应该被翻译成安全的标识符列表:fubi, "boo-boo", "AA"
。这一步很重要
Postgres 没有这个 fork for list 的任何函数,但我们可以编写自己的函数:
CREATE OR REPLACE FUNCTION sanitize_identifiers(text)
RETURNS text AS $$
SELECT string_agg(quote_ident(v), ',')
FROM unnest(string_to_array(, ',')) g(v)
$$ LANGUAGE sql;
现在,我们可以这样写函数:
CREATE OR REPLACE FUNCTION foo(tablename regclass,
columns text)
RETURNS void AS $$
BEGIN
-- should to print valid SQL
RAISE NOTICE '%',
FORMAT('INSERT INTO %s SELECT %s FROM tab',
tablename,
sanitize_identifiers(columns));
END;
$$ LANGUAGE plpgsql;
结果:
postgres=# select foo('Test','a,b,c,C');
NOTICE: INSERT INTO test SELECT a,b,c,"C" FROM tab