子查询无返回值时插入操作是否失败?附PostgreSQL临时表代码
这得分两种具体场景来判断:
场景1:
INSERT ... VALUES搭配单行子查询
如果你用的是类似这样的写法:INSERT INTO your_table (col1) VALUES ((SELECT x FROM some_table WHERE condition));当子查询
SELECT x FROM some_table WHERE condition没有返回任何行时,PostgreSQL会直接抛出错误:ERROR: no rows returned by a subquery used as an expression,此时插入操作完全失败,不会插入任何数据。场景2:
INSERT ... SELECT批量插入语法
要是你采用批量插入的写法:INSERT INTO your_table (col1) SELECT x FROM some_table WHERE condition;这种情况下,子查询没结果只会导致插入0行数据,不会触发错误,插入操作本身是成功执行的(只是没有数据被写入表中)。
我帮你梳理下代码里的关键问题和优化方向:
1. 未定义的变量错误
你在DECLARE块里定义了START_TIME_STR和END_TIME_STR两个字符串变量,但后续查询中却用了start_time和end_time——这两个变量根本没声明!而且timestamp_gnrl应该是TIMESTAMP类型,直接用字符串比较可能存在隐式转换风险,建议直接定义时间类型变量:
DECLARE START_TIME TIMESTAMP := '2018-01-28 00:00:00'; END_TIME TIMESTAMP := '2018-02-27 23:59:59'; v_category INTEGER := 2; v_intrim_loop tranx1%ROWTYPE; -- 别忘了声明循环变量! BEGIN -- 后续查询直接使用START_TIME和END_TIME即可
2. 循环变量未声明
你用了FOR v_intrim_loop IN (select distinct(t.*) from tranx1 t),但v_intrim_loop没有在DECLARE块里声明,必须加上v_intrim_loop tranx1%ROWTYPE;才能正常使用这个行类型变量。
3. 单行子查询无结果的风险
你在INSERT的VALUES里用了多个(SELECT x from something LIMIT 1)这类子查询——如果something表没有匹配的行,这些子查询会抛出和问题1场景1一样的错误,导致当前循环的插入失败,甚至中断整个DO块的执行。
解决办法是用COALESCE处理无结果的情况,让它返回NULL或者你指定的默认值:
COALESCE((SELECT x from something LIMIT 1), NULL) -- 无结果时存NULL -- 也可以指定自定义默认文本 COALESCE((SELECT x from something LIMIT 1), 'N/A')
4. 效率优化:避免循环插入
PostgreSQL里循环处理单条记录插入的效率极低,尤其是当tranx1数据量较大时。建议把整个逻辑改成批量插入,一次性完成所有数据写入:
INSERT INTO cntr_track_detail (cntr_no, cntr_trxn_id, surajbari, mokha, gate_in, port_in, terminal_in, terminal_name) SELECT t.cntr_no, t.cntr_trxn_id, COALESCE((SELECT x FROM something WHERE some_condition = t.cntr_trxn_id LIMIT 1), NULL), COALESCE((SELECT x FROM something_else WHERE some_condition = t.cntr_trxn_id LIMIT 1), NULL), ... -- 其他字段同理,根据实际关联条件调整子查询 FROM (SELECT DISTINCT * FROM tranx1) t;
5. 临时表的小细节
你创建tranx1临时表的写法没问题,但要注意ON COMMIT DROP会在事务结束时自动删除临时表,符合你的需求。
最后给你修正后的完整代码框架参考:
DO $$ DECLARE START_TIME TIMESTAMP := '2018-01-28 00:00:00'; END_TIME TIMESTAMP := '2018-02-27 23:59:59'; v_category INTEGER := 2; BEGIN CREATE TEMPORARY TABLE cntr_track_detail ( cntr_no VARCHAR(11) NOT NULL, cntr_trxn_id BIGSERIAL NOT NULL, surajbari TEXT default null, mokha TEXT default null, gate_in TEXT default null, port_in TEXT default null, terminal_in TEXT default null, terminal_name VARCHAR(100) default null ) ON COMMIT DROP; CREATE TEMPORARY TABLE tranx1 ON COMMIT DROP AS SELECT ctm.cntr_no, ctm.cntr_trxn_id FROM cntr_trxn_mapping ctm INNER JOIN cntr_track_time_log cttl ON cttl.cntr_trxn_id = ctm.cntr_trxn_id AND ctm.cntr_cycle_id = v_category AND cttl.timestamp_gnrl >= START_TIME AND cttl.timestamp_gnrl <= END_TIME AND cttl.event_id IN (13,21,1,19); RAISE NOTICE 'size of tranx1 is, %', (SELECT COUNT(*) FROM tranx1); -- 用批量插入替代循环,提升效率 INSERT INTO cntr_track_detail (cntr_no, cntr_trxn_id, surajbari, mokha, gate_in, port_in, terminal_in, terminal_name) SELECT t.cntr_no, t.cntr_trxn_id, COALESCE((SELECT x FROM something WHERE ... LIMIT 1), NULL), COALESCE((SELECT x FROM something WHERE ... LIMIT 1), NULL), COALESCE((SELECT x FROM something WHERE ... LIMIT 1), NULL), COALESCE((SELECT x FROM something WHERE ... LIMIT 1), NULL), COALESCE((SELECT x FROM something WHERE ... LIMIT 1), NULL), COALESCE((SELECT x FROM something WHERE ... LIMIT 1), NULL) FROM (SELECT DISTINCT * FROM tranx1) t; END $$;
内容的提问来源于stack exchange,提问作者आनंद

