PostgreSQL EXISTS判断异常:已删表仍触发THEN分支报错求助
问题原因及解决方案
为什么原代码会报错
PostgreSQL在执行查询前,会先对整个SQL语句做语法解析和语义校验,这个阶段会检查所有引用的表、列是否存在,不管CASE语句里的条件是否会触发对应分支。所以哪怕你通过information_schema.tables判断表不存在,解析阶段还是会扫描到THEN分支里的hist.game_hist,发现表不存在就直接报错,根本到不了执行CASE条件判断的步骤。
解决方法:用PL/pgSQL动态处理
静态SQL无法跳过不存在表的校验,必须用动态SQL或者PL/pgSQL函数来实现“表存在才执行对应查询”的逻辑。
方法1:创建函数获取阈值
先写一个函数,内部判断表是否存在并返回对应的时间阈值:
CREATE OR REPLACE FUNCTION get_sync_threshold() RETURNS timestamp AS $$ BEGIN IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'hist' AND table_name = 'game_hist' ) THEN RETURN COALESCE( (SELECT max(dbt_updated_at) - INTERVAL '120 minutes' FROM hist.game_hist), '1900-01-01'::timestamp ); ELSE RETURN '1900-01-01'::timestamp; END IF; END; $$ LANGUAGE plpgsql;
然后主查询调用这个函数即可:
SELECT pi.* FROM stg.stg_game pi WHERE pi.status != '9' AND pi.hvr_capture_timestamp::timestamp > get_sync_threshold();
PL/pgSQL是在执行阶段才会解析分支里的SQL,所以只有当表存在时,才会去解析hist.game_hist的查询,避免了不存在时的报错。
方法2:用动态SQL直接执行查询
如果不想创建函数,也可以用DO块结合动态SQL实现:
DO $$ DECLARE threshold timestamp; BEGIN -- 先计算阈值 IF EXISTS ( SELECT 1 FROM information_schema.tables WHERE table_schema = 'hist' AND table_name = 'game_hist' ) THEN SELECT COALESCE(max(dbt_updated_at) - INTERVAL '120 minutes', '1900-01-01'::timestamp) INTO threshold FROM hist.game_hist; ELSE threshold := '1900-01-01'::timestamp; END IF; -- 动态执行查询 EXECUTE format( 'SELECT pi.* FROM stg.stg_game pi WHERE pi.status != ''9'' AND pi.hvr_capture_timestamp::timestamp > %L', threshold ); END $$;
注意:DO块不会直接返回查询结果,如果需要获取结果,可以把查询结果插入临时表,再从临时表查询。
内容的提问来源于stack exchange,提问作者abc_23
相关产品推荐
相关产品推荐

