如何使用 plpgsql 函数插入数据?
Posted
技术标签:
【中文标题】如何使用 plpgsql 函数插入数据?【英文标题】:How can I insert data using a plpgsql function? 【发布时间】:2012-05-02 20:19:56 【问题描述】:我正在尝试使用 plpgsql 函数或存储过程将数据插入表中。但是,我想一次插入多条记录。我现在这些都将是 VARCHAR,所以我想我可以使用像 function(tablename VARCHAR, records VARCHAR[][])
这样形成的函数。但后来我发现 plpgsql 中的多维数组支持并不是那么棒。
这就是我目前的功能。这不会产生我正在寻找的结果。 当我问
SELECT insert_data('tennis', ARRAY[ARRAY['1','2'], ARRAY['3','4']])
我收到以下错误
ERROR: syntax error at or near ""
LINE 1: INSERT INTO tennis VALUES (null, 1), (null, 3);
^
QUERY: INSERT INTO tennis VALUES (null, 1), (null, 3);
CONTEXT: PL/pgSQL function "insert_data" line 26 at EXECUTE statement
但是我期待这样的查询
INSERT INTO tennis VALUES (null, '1', '2'), (null, '3', '4');
这会起作用,因为乒乓球有这种结构。
CREATE OR REPLACE FUNCTION insert_data (dsetname_in VARCHAR, records VARCHAR[][])
RETURNS BOOLEAN AS $PROC$
DECLARE
insertquery TEXT;
val VARCHAR;
i INT;
j INT;
BEGIN
insertquery = $$INSERT INTO $$ || dsetname_in || $$ VALUES $$;
FOR i IN array_lower(records, 1)..array_upper(records, 1)
LOOP
insertquery = insertquery || $$(null, $$;
FOR j IN array_lower(records[i:i], 1)..array_upper(records[i:i], 1)
LOOP
val = records[i:i][j:j];
insertquery = insertquery || val;
IF j <> array_upper(records[i:i], 1) THEN
insertquery = insertquery || $$, $$;
END IF;
END LOOP;
insertquery = insertquery || $$)$$;
IF i <> array_upper(records, 1) THEN
insertquery = insertquery || $$, $$;
END IF;
END LOOP;
insertquery = insertquery || $$;$$;
EXECUTE insertquery;
RETURN TRUE;
END;$PROC$ LANGUAGE 'plpgsql';
【问题讨论】:
我不会从数据库中执行它,而是从外部代码中执行。 这并不能解释为什么外部代码更容易生成这个字符串:SELECT insert_data('tennis', ARRAY[ARRAY['1','2'], ARRAY['3','4']])
,而不是这个:INSERT INTO tennis VALUES (null, '1', '2'), (null, '3', '4')
。不明白为什么第一个是您想要的,因此很难对建议进行推理。
我想在我的外部代码中使查询尽可能抽象。因此,与其调用 INSERT 语句,不如调用我自己的函数。
【参考方案1】:
我对整个方法的价值持怀疑态度,因为我认为它没有增加任何有用的抽象级别;但是如果你必须这样做,并且你所有的值都是字符串,我认为最干净的方法是:
CREATE OR REPLACE FUNCTION insert_data(dsetname_in text, records text[])
RETURNS VOID LANGUAGE plpgsql AS $PROC$
DECLARE
insertquery text;
i int;
BEGIN
insertquery := 'INSERT INTO ' || dsetname_in || ' VALUES ';
FOR i IN array_lower(records, 1)..array_upper(records, 1)
LOOP
insertquery := insertquery || '(null, ' || records[i] || '),';
END LOOP;
insertquery := left(insertquery, char_length(insertquery) - 1);
EXECUTE insertquery;
END;
$PROC$;
然后你可以这样调用它,这看起来比你为嵌套数组显示的更干净:
SELECT insert_data('tennis',
ARRAY[$$'1','2'$$,
$$'3','4'$$]);
【讨论】:
是的,这看起来更好。谢谢你和我一起思考,也许这不是正确的方法,但你今天确实让我很开心。我只是想将“大”查询留给数据库,希望代码更清晰。我会看看它在最后阶段会带来什么。 小心处理数据中的引号,尤其是 SQL 注入。通过这样做,许多用于处理此类事情的常用基础设施并不容易获得。我还没有准确分析这里的风险,但它确实“感觉”你需要在这方面小心行事。 我正在使用 java.sql.CallableStatement。降低 SQL 注入的风险?或者不,使用记录数组中的文本? 是的,在数组的每个字符串中嵌套字符串可能会使正常保护无法触及。当心小鲍比桌。 xkcd.com/327以上是关于如何使用 plpgsql 函数插入数据?的主要内容,如果未能解决你的问题,请参考以下文章
无法从 plpgsql 函数中的动态命名临时表运行“选择进入”