![](/img/trans.png)
[英]How to write an insert SQL statement that loop through each record in an array of objects and insert into a record's specific columns accordingly?
[英]Loop through columns of RECORD
我需要通过键/索引循环遍历RECORD
类型的项目,就像我可以使用其他编程语言中的数组结构来执行此操作一样。
例如:
DECLARE
data1 record;
data2 text;
...
BEGIN
...
FOR data1 IN
SELECT
*
FROM
sometable
LOOP
FOR data2 IN
SELECT
unnest( data1 ) -- THIS IS DOESN'T WORK!
LOOP
RETURN NEXT data1[data2]; -- SMTH LIKE THIS
END LOOP;
END LOOP;
正如@Pavel所解释的那样,不可能简单地遍历记录,就像你可以遍历一个数组一样。 但有几种方法可以解决它 - 具体取决于您的具体要求。 最终,由于您想要返回同一列中的所有值,您需要将它们转换为相同的类型 - text
是明显的共同点,因为每种类型都有一个文本表示。
比如,你有一个包含integer
, text
和date
列的表。
CREATE TEMP TABLE tbl(a int, b text, c date);
INSERT INTO tbl VALUES
(1, '1text', '2012-10-01')
,(2, '2text', '2012-10-02')
,(3, ',3,ex,', '2012-10-03') -- text with commas
,(4, '",4,"ex,"', '2012-10-04') -- text with commas and double quotes
那么解决方案可以很简单:
SELECT unnest(string_to_array(trim(t::text, '()'), ','))
FROM tbl t;
适用于前两行,但对于第3行和第4行的特殊情况无效。
您可以使用文本表示中的逗号轻松解决问题:
SELECT unnest(('{' || trim(t::text, '()') || '}')::text[])
FROM tbl t
WHERE a < 4;
这样可以正常工作 - 除了在文本表示中有双引号的第4行。 通过加倍他们逃脱了。 但是数组构造函数需要将它们转义为\\
。 不知道为什么这种不兼容性存在......
SELECT ('{' || trim(t::text, '()') || '}') FROM tbl t WHERE a = 4
产量:
{4,""",4,""ex,""",2012-10-04}
但你需要:
SELECT '{4,"\",4,\"ex,\"",2012-10-04}'::text[]; -- works
如果您事先知道列名称,那么干净的解决方案很简单:
SELECT unnest(ARRAY[a::text,b::text,c::text])
FROM tbl
由于您对熟知类型的记录进行操作,因此您只需查询系统目录:
SELECT string_agg(a.attname || '::text', ',' ORDER BY a.attnum)
FROM pg_catalog.pg_attribute a
WHERE a.attrelid = 'tbl'::regclass
AND a.attnum > 0
AND a.attisdropped = FALSE
把它放在一个带动态SQL的函数中:
CREATE OR REPLACE FUNCTION unnest_table(_tbl text)
RETURNS SETOF text LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE '
SELECT unnest(ARRAY[' || (
SELECT string_agg(a.attname || '::text', ',' ORDER BY a.attnum)
FROM pg_catalog.pg_attribute a
WHERE a.attrelid = _tbl::regclass
AND a.attnum > 0
AND a.attisdropped = false
) || '])
FROM ' || _tbl::regclass;
END
$func$;
呼叫:
SELECT unnest_table('tbl') AS val
返回:
val
-----
1
1text
2012-10-01
2
2text
2012-10-02
3
,3,ex,
2012-10-03
4
",4,"ex,"
2012-10-04
无需安装其他模块即可使用。 另一个选择是安装hstore扩展并像@Craig演示一样使用它。
PL / pgSQL并不是为你想要的而设计的。 它不认为记录是可迭代的,它是可能不同且不兼容的数据类型的元组。
PL / pgSQL对动态SQL有EXECUTE
,但EXECUTE
查询不能直接引用PL / pgSQL变量,如NEW
或其他记录。
您可以做的是将记录转换为hstore
键/值结构,然后遍历hstore
。 使用each(hstore(the_record))
,它生成一个key,value
元组的行集。 所有值都转换为其text
表示形式。
这个玩具函数通过创建一个匿名的ROW(..)
来实现对记录的迭代 - 它将具有列名f1
, f2
, f3
- 然后将其转换为hstore
,迭代其列/值对,并返回每对。
CREATE EXTENSION hstore;
CREATE OR REPLACE FUNCTION hs_demo()
RETURNS TABLE ("key" text, "value" text)
LANGUAGE plpgsql AS
$$
DECLARE
data1 record;
hs_row record;
BEGIN
data1 = ROW(1, 2, 'test');
FOR hs_row IN SELECT kv."key", kv."value" FROM each(hstore(data1)) kv
LOOP
"key" = hs_row."key";
"value" = hs_row."value";
RETURN NEXT;
END LOOP;
END;
$$;
实际上你永远不会这样写,因为整个循环可以用一个简单的RETURN QUERY
语句替换它并且each(hstore)
都做同样的事情 - 所以这只是为了显示each(hstore(record))
工作,并且实际上不应该使用上述功能。
plpgsql不支持此功能 - 像其他脚本语言一样记录IS NOT哈希数组 - 它类似于C或ADA,其中此功能是不可能的。 您可以使用其他PL语言,如PLPerl或PLPython或一些技巧 - 您可以使用HSTORE数据类型(扩展名)或通过动态SQL进行迭代
但是对此功能的请求通常意味着,所以你做错了。 当您使用PL / pgSQL时,您认为与使用Javascript或Python不同
FOR data2 IN
SELECT d
from unnest( data1 ) s(d)
LOOP
RETURN NEXT data2;
END LOOP;
如果您在循环之前订购结果,您将完成您想要的任务。
for rc in select * from t1 order by t1.key asc loop
return next rc;
end loop;
将完全满足您的需求。 它也是执行此类任务的最快方式。
我找不到循环记录的正确方法,所以我所做的是先将记录转换为 json,然后循环 json
declare
_src_schema varchar := 'db_utility';
_targetjson json;
_key text;
_value text;
BEGIN
select row_to_json(c.*) from information_schema.columns c where c.table_name = prm_table and c.column_name = prm_column
and c.table_schema = _src_schema into _targetjson;
raise notice '_targetjson %', _targetjson;
FOR _key, _value IN
SELECT * FROM jsonb_each_text(_targetjson)
LOOP
-- do some math operation on its corresponding value
RAISE NOTICE '%: %', _key, _value;
END LOOP;
return true;
end;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.