PostgreSQL 9.3子查询计算报错:SELECT中无法引用别名
解决PostgreSQL 9.3中SELECT子句无法引用别名的问题
嘿,这个问题我太熟了!PostgreSQL里确实不能在同一个SELECT子句里直接引用刚定义的别名,这是因为它的查询执行顺序决定的——SELECT里的别名是在查询后期阶段才生成的,同一步里根本还没这个“变量”呢。给你几个实用的解决办法,按需选就行:
方法1:用子查询先计算统计值,外层再算比率
把包含sent_count和bounce_count的查询包成一个子查询,外层就可以直接引用这两个别名了,还能顺便处理除数为0和整数除法的问题:
SELECT key, col1, col2, sent_count, bounce_count, -- 处理除数为0的情况,同时转成numeric避免整数除法 CASE WHEN sent_count = 0 THEN 0 ELSE bounce_count::numeric / sent_count END AS bounce_rate FROM ( SELECT email.key, email.col1, email.col2, (SELECT count(sent.subscriber_key) FROM sent WHERE sent.email_id = email.key) AS sent_count, (SELECT count(bounce.subscriber_key) FROM bounce WHERE bounce.email_id = email.key) AS bounce_count FROM email ) AS email_stats;
方法2:用JOIN代替关联子查询(性能更优)
如果你的email表数据量比较大,上面的关联子查询会每行执行一次统计,性能会很差。换成预统计后JOIN的方式,只需要统计一次sent和bounce表,效率高很多:
SELECT email.key, email.col1, email.col2, -- 用COALESCE把NULL转成0,避免没有记录时出现空值 COALESCE(sent_stats.sent_count, 0) AS sent_count, COALESCE(bounce_stats.bounce_count, 0) AS bounce_count, CASE WHEN COALESCE(sent_stats.sent_count, 0) = 0 THEN 0 ELSE COALESCE(bounce_stats.bounce_count, 0)::numeric / sent_stats.sent_count END AS bounce_rate FROM email LEFT JOIN ( SELECT email_id, count(subscriber_key) AS sent_count FROM sent GROUP BY email_id ) AS sent_stats ON sent_stats.email_id = email.key LEFT JOIN ( SELECT email_id, count(subscriber_key) AS bounce_count FROM bounce GROUP BY email_id ) AS bounce_stats ON bounce_stats.email_id = email.key;
方法3:用CTE(公共表表达式)让代码更清晰
PostgreSQL 9.3已经支持CTE了,把统计逻辑拆成独立的部分,代码可读性更好,和方法2的性能差不多:
WITH sent_stats AS ( SELECT email_id, count(subscriber_key) AS sent_count FROM sent GROUP BY email_id ), bounce_stats AS ( SELECT email_id, count(subscriber_key) AS bounce_count FROM bounce GROUP BY email_id ) SELECT email.key, email.col1, col2, COALESCE(sent_stats.sent_count, 0) AS sent_count, COALESCE(bounce_stats.bounce_count, 0) AS bounce_count, CASE WHEN COALESCE(sent_stats.sent_count, 0) = 0 THEN 0 ELSE COALESCE(bounce_stats.bounce_count, 0)::numeric / sent_stats.sent_count END AS bounce_rate FROM email LEFT JOIN sent_stats ON sent_stats.email_id = email.key LEFT JOIN bounce_stats ON bounce_stats.email_id = email.key;
几个关键提醒:
- 一定要处理除数为0的情况,不然当
sent_count是0时会直接报错; - 把整数转成
numeric类型再做除法,不然PostgreSQL会做整数除法(比如3/5会得到0而不是0.6); - 数据量大的时候优先选方法2或3,比关联子查询高效得多。
内容的提问来源于stack exchange,提问作者ComputerTinker




