You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过单条PostgreSQL INSERT语句批量插入多组关联数据?

高效合并多条INSERT为单条语句的解决方案

针对你需要把多条插入操作合并为单条INSERT,同时要兼顾数十万条数据写入效率的需求,这里有两种实用方案,其中第二种尤其适合大批量场景:

方案1:用UNION ALL直接拼接(适合少量固定条目)

如果是少量几条固定的插入,直接用UNION ALL把多个SELECT的结果合并起来就行,写法简单直接:

INSERT INTO fund_data (fund_entries_id, fund_val, bbg_pulls_id)
SELECT fe.id, 1, 20
FROM fund_entries fe
WHERE fe.company_id = 399 AND fe.fiscal_prd_end_date = '2016-09-30'
UNION ALL
SELECT fe.id, 567, 20
FROM fund_entries fe
WHERE fe.company_id = 5 AND fe.fiscal_prd_end_date = '2016-09-28';

不过要是处理数十万条数据,这种写法就太繁琐了,所以更推荐下面的WITH子句方案。

方案2:用WITH子句预定义插入参数(适合大批量条目)

把所有需要插入的company_idfund_valfiscal_prd_end_date先放到WITH的临时数据集里,再关联fund_entries表获取对应的fund_entries_id,这样只需要写一次关联逻辑,代码简洁还高效:

WITH insert_params AS (
    VALUES
        (399, 1::numeric, '2016-09-30'::date),
        (5, 567::numeric, '2016-09-28'::date)
        -- 这里可以继续追加数十万条参数,每行一条即可
)
INSERT INTO fund_data (fund_entries_id, fund_val, bbg_pulls_id)
SELECT fe.id, ip.fund_val, 20
FROM insert_params ip
JOIN fund_entries fe 
  ON ip.company_id = fe.company_id 
  AND ip.fiscal_prd_end_date = fe.fiscal_prd_end_date;

为什么这个方案高效?

  • 减少数据库解析开销:数据库只需要解析一次INSERT和关联逻辑,而不是数十万条独立的INSERT语句,能节省大量的解析时间。
  • 批量写入优化:PostgreSQL会把整个批量操作作为一个事务处理(如果没手动拆分的话),减少了事务提交的次数,大幅提升写入性能。
  • 利用现有索引:你的fund_entries表在(company_id, fiscal_prd_end_date)上有唯一索引(fund_entries_company_id_fiscal_prd_end_date_key),关联查询时能快速定位到对应的fe.id,不会出现全表扫描的情况。

注意事项

  1. 数据类型要匹配:在VALUES子句里要确保数据类型和目标列一致,比如fund_valnumeric类型,所以加上::numeric显式转换;日期类型加上::date,避免隐式转换出错。
  2. 处理唯一性约束fund_data表有(fund_entries_id, bbg_pulls_id)的唯一约束,要是插入重复组合会报错。如果需要处理冲突,可以加上ON CONFLICT子句:
WITH insert_params AS (
    VALUES
        (399, 1::numeric, '2016-09-30'::date),
        (5, 567::numeric, '2016-09-28'::date)
)
INSERT INTO fund_data (fund_entries_id, fund_val, bbg_pulls_id)
SELECT fe.id, ip.fund_val, 20
FROM insert_params ip
JOIN fund_entries fe 
  ON ip.company_id = fe.company_id 
  AND ip.fiscal_prd_end_date = fe.fiscal_prd_end_date
ON CONFLICT (fund_entries_id, bbg_pulls_id) 
DO UPDATE SET fund_val = EXCLUDED.fund_val; -- 冲突时更新fund_val
  1. 分批处理大批量数据:如果是数十万条数据,建议分成若干批次(比如每1万条一批),避免单个事务过大导致内存占用过高或者锁表时间太长。

关于你提到的方案

你考虑的WITH子句方案正是最优解之一,比多个SELECT用UNION ALL拼接更适合大批量场景;而Stack Overflow上提到的单INSERT多SELECT本质就是UNION ALL的写法,结合WITH子句的参数化写法是更进阶、高效的实现方式。

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

火山引擎 最新活动