问题描述
我正在尝试找到一种方法,通过将它们在表中的存在将文本数组分为两个数组。
让我们说users
表具有以下三个ID:1,2,3
,而我有一个input_array_ids
,其值为1,5
。表中存在ID 1,2
,因此它进入另一个称为ids_valid
的数组,ID 5
进入ids_invalid
。最后,我想对有效ID进行更新,并通知用户哪些ID已更新以及在表中找不到哪些ID。
CREATE FUNCTION update_status(p_id character varying DEFAULT NULL::character varying,p_status character varying DEFAULT NULL::character varying) RETURNS character varying
LANGUAGE plpgsql
AS
$$
DECLARE
v_valid_ids users.id%type;
v_invalid_ids users.id%type;
v_result varchar(255);
v_ids text[];
BEGIN
v_ids = string_to_array(regexp_replace(p_id,'[\s+]','','g'),',');
--Do some magic to split v_ids into v_valid_ids and v_invalid_ids
UPDATE users
SET row_status = p_status,updated_by = 'hxBiSP'
WHERE id = ANY (v_valid_ids);
v_result = 'Updated: ' || string_agg(v_valid_ids::character varying,') || 'Could not update: ' || string_agg(v_invalid_ids::character varying,');
RETURN v_result;
END ;
$$;
ALTER FUNCTION update_status(varchar,varchar) OWNER TO postgres;
我该怎么办?
解决方法
我认为您不需要plgpsql即可完成所需的工作:CTE足够好。考虑以下sql函数:
create function update_status(p_id text,p_status text)
returns text
as $$
with
t_ids(id) as (select regexp_split_to_table('1,2,4',',\s*')::int),upd as (
update users u
set row_status = p_status,updated_by = 'hxbisp'
from t_ids i
where i.id = u.id
)
select 'updated: ' || string_agg(u.id::text,')
|| ' - could not update: ' || string_agg(i.id::text,') filter(where u.id is null)
from t_ids i
left join users u on u.id = i.id
$$
language sql;
第一个CTE t_ids
将输入字符串嵌套到行中。然后,upd
执行联接。最后,外部查询使用left join
和聚合(即函数的结果集)来生成表示匹配的ID和未匹配的ID的状态字符串。
-- initial table content
select * from users order by id;
id | name | row_status | updated_by
-: | :--- | :--------- | :---------
1 | foo | |
2 | bar | |
3 | baz | |
-- call the function
select update_status('1,'zoo');
| update_status |
| :---------------------------------- |
| updated: 1,2 - could not update: 4 |
-- check the content of the table
select * from users order by id;
id | name | row_status | updated_by
-: | :--- | :--------- | :---------
1 | foo | zoo | hxbisp
2 | bar | zoo | hxbisp
3 | baz | |
,
预先将ID分为两组现有的和不存在的(在代码中为v_valid_ids
和v_invalid_ids
),这使您可以进入竞争状态。如果并发事务删除/更新/插入ID,该怎么办?
只需在原始ID组(列表/数组)上运行UPDATE
,然后使用RETURNING
子句返回实际上受 影响的ID。参见:
不仅通常便宜得多(仅对目标牌桌进行一次通行),而且针对比赛条件也很安全。
假设使用integer
的ID,该ID可以实现更简单,更快速的代码,但是,使用附加模块intarray
时-每个数据库需要使用一次安装
CREATE EXTENSION intarray;
请参阅:
- Postgresql intarray error: undefined symbol: pfree
- Compare arrays for equality,ignoring order of elements
(但是我们可以对任何数据类型使用此功能。)
CREATE OR REPLACE FUNCTION f_update_status(_ids_as_string text = NULL,_status text = NULL)
RETURNS text
LANGUAGE plpgsql AS
$func$
DECLARE
_ids int[] := string_to_array(_ids_as_string,')::int[]; -- ①
_updated int[];
BEGIN
WITH upd AS (
UPDATE users
SET row_status = _status,updated_by = 'hxBiSP'
WHERE id = ANY (_ids) -- just use input array
RETURNING id -- returns actually affected IDs
)
SELECT ARRAY(TABLE upd)
INTO _updated; -- ③ aggregate into array with ARRAY constructor
RETURN format('Updated: %s. Not found: %s.',_updated::text,(_ids - _updated)::text); -- ②
END
$func$;
db 提琴here
或者使用VARIADIC
函数将实际整数值或实际整数数组作为单个参数传递,例如您上一个问题下建议的a_horse:
①可以在声明时间分配变量。并自动从整数中修剪前导或尾随空白。
②使用intarray提供的integer[] - integer[] → integer[]
operator。
③关于ARRAY构造函数:
如何将单个数组而不是值字典传递给VARIADIC
函数: