【问题标题】:PL/pgSQL - Find out which items of an array exist in the tablePL/pgSQL - 找出表中存在数组的哪些项目
【发布时间】:2020-09-26 21:09:15
【问题描述】:

我正在尝试找到一种方法,通过它们在表中的存在将文本数组拆分为两个数组。

假设users 表有这三个ID:1, 2, 3,我有一个input_array_ids,其值为1, 2, 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;

我该怎么做?

【问题讨论】:

  • 您展示了一个带有 integer 数组的示例,但该函数适用于 varchar 输入、text 数组和混淆类型。 id 的实际数据类型很重要。 (就像你的 Postgres 版本一样,总是这样。)告诉我们更多关于输入的信息:一个文本字符串?具体是什么规格?

标签: sql postgresql sql-update plpgsql sql-function


【解决方案1】:

我认为你不需要 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 的状态字符串 - 这是函数的结果集。

Demo on DB Fiddle

-- 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, 2, 4', '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  |            |

【讨论】:

    【解决方案2】:

    事先将 ID 分为两组现有的和不存在的(v_valid_idsv_invalid_ids 在您的代码中),这会使您面临竞争条件。如果 ID 被并发事务删除/更新/插入怎么办?

    只需在原始 ID 集(列表/数组)上运行 UPDATE 并使用 RETURNING 子句返回实际上受影响的 ID。见:

    不仅通常更便宜(只需通过目标表一次),而且在竞争条件下也很安全

    假设integer ID 允许更简单和更快的代码,但是,使用附加模块 intarray - 需要为每个数据库安装一次

    CREATE EXTENSION intarray;
    

    见:

    (但我们可以为 any 数据类型进行这项工作。)

    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 函数:

    【讨论】:

      猜你喜欢
      • 2012-03-27
      • 2014-08-05
      • 2017-12-10
      • 2016-12-26
      • 2015-02-26
      • 1970-01-01
      • 2012-07-28
      • 2012-12-17
      • 1970-01-01
      相关资源
      最近更新 更多