You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PL/pgSQL中实现逐行赋不同值的单条UPDATE操作方法

PL/pgSQL实现批量元素值更新方案

问题背景

我有一个存储元素与对应值映射关系的Python字典,示例如下:

db_rows_values = {
     <element_uuid_1>: 12,
     <element_uuid_2>: "abc",
     <element_uuid_3>: [123, 124, 125],
}

需要通过单条查询完成上述数据的更新。此前在Python中通过循环生成CASE语句拼接查询实现该需求,代码如下:

sql_query_elements_values_part = " ".join([f"WHEN '{element_row['element_id']}' "
                                           f"THEN '{ujson.dumps(element_row['value'])}'::JSONB "
                                           for element_row in db_row_values])

query_part_elements_values_update = f"""
    elements_value_update AS (
        UPDATE m2m_entries_n_elements 
        SET value = 
        CASE element_id 
            {sql_query_elements_values_part}
            ELSE NULL 
        END 
        WHERE element_id = ANY(%(elements_ids)s::UUID[])
        AND entry_id = ANY(%(entries_ids)s::UUID[])

        RETURNING element_id, entry_id, value

    ),
"""

现在需要将该逻辑改写为plpgsql实现,支持将db_rows_values以ROWTYPE数组或JSON格式传入,实现等价于原WHEN THEN分支的匹配赋值逻辑。


推荐实现:JSONB传参(无需自定义类型)

这是最简便的方案,不需要提前在数据库创建自定义类型,逻辑和原CASE分支完全等价,性能优于拼接长CASE语句,同时彻底规避SQL注入风险。

函数定义

CREATE OR REPLACE FUNCTION batch_update_element_values(
    p_value_map JSONB, -- 直接传入Python端序列化后的字典,格式为{"<uuid1>": 12, "<uuid2>": "abc", ...}
    p_element_ids UUID[],
    p_entry_ids UUID[]
) RETURNS TABLE (
    element_id UUID,
    entry_id UUID,
    value JSONB
) AS $$
BEGIN
    RETURN QUERY
    WITH elements_value_update AS (
        UPDATE m2m_entries_n_elements t
        -- 等价于原CASE逻辑:键存在则取对应JSONB值,不存在则返回NULL,完全对齐ELSE NULL分支
        SET value = p_value_map -> t.element_id
        WHERE t.element_id = ANY(p_element_ids)
          AND t.entry_id = ANY(p_entry_ids)
        RETURNING t.element_id, t.entry_id, t.value
    )
    SELECT * FROM elements_value_update;
END;
$$ LANGUAGE plpgsql VOLATILE;

Python端调用示例

import ujson
import psycopg2

conn = psycopg2.connect("数据库连接串")
cur = conn.cursor()

cur.execute(
    "SELECT * FROM batch_update_element_values(%s::JSONB, %s::UUID[], %s::UUID[])",
    (
        ujson.dumps(db_rows_values), # 直接序列化原字典传入
        list(db_rows_values.keys()),
        entries_ids # 原逻辑中的entries_ids参数
    )
)
updated_records = cur.fetchall()
conn.commit()

可选实现:ROWTYPE数组传参

如果需要用复合类型数组传参,需要先在数据库创建对应的复合类型,再通过关联更新实现匹配逻辑。

第一步:创建复合类型

CREATE TYPE element_value_pair AS (
    element_id UUID,
    value JSONB
);

第二步:创建更新函数

CREATE OR REPLACE FUNCTION batch_update_element_values_rowtype(
    p_value_pairs element_value_pair[],
    p_element_ids UUID[],
    p_entry_ids UUID[]
) RETURNS TABLE (
    element_id UUID,
    entry_id UUID,
    value JSONB
) AS $$
BEGIN
    RETURN QUERY
    WITH value_map AS (
        SELECT (pair).element_id AS map_eid, (pair).value AS map_val
        FROM unnest(p_value_pairs) pair
    ),
    elements_value_update AS (
        UPDATE m2m_entries_n_elements t
        SET value = vm.map_val
        FROM value_map vm
        WHERE t.element_id = vm.map_eid
          AND t.element_id = ANY(p_element_ids)
          AND t.entry_id = ANY(p_entry_ids)
        RETURNING t.element_id, t.entry_id, t.value
    )
    SELECT * FROM elements_value_update;
END;
$$ LANGUAGE plpgsql VOLATILE;

注意:该实现默认仅更新数组中存在对应element_id的行,如果需要完全对齐原逻辑中「未匹配到的element_id对应value设为NULL」的规则,需要将关联改为左连接,覆盖所有符合WHERE条件的行。


方案对比

  • JSONB方案无需提前定义数据库结构,跨语言调用适配成本低,JSONB按键查询时间复杂度为O(1),更新条目量大时性能优势明显,优先选用。
  • ROWTYPE数组方案适合数据库内部调用场景,跨语言传参时需要构造复合类型数组,适配成本更高。
  • 两种参数化方案都彻底解决了原Python拼接SQL存在的注入风险。

内容的提问来源于stack exchange,提问作者Prosto_Oleg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 19:33:23