PostgreSQL中生成列乘以6返回NULL值且无报错的问题求助
嘿,我来帮你排查这个问题!你遇到的情况其实是PostgreSQL查询解析顺序导致的典型问题,咱们一步步理清楚:
问题根源
PostgreSQL的查询执行是有固定顺序的:它会先处理FROM和WINDOW子句,接着是WHERE、GROUP BY、HAVING,最后才会处理SELECT子句里的表达式。这意味着你在SELECT里刚定义的别名hour_rate,在同一个SELECT的其他表达式(也就是你的Last_Six_Hours列)里是无法被识别的——因为这个别名还没被系统解析生效呢。
那为什么没报错反而返回NULL?大概率是你的public.production表没有名为hour_rate的列,PostgreSQL找不到这个列时,在某些会话设置下会把它当作NULL处理,而不是直接抛出错误。如果你的表真有这个列,那结果会用表中的原始数据,而不是你计算的生成列,这同样会出问题。
解决方案
这里给你几个靠谱的解决办法,按需选择:
1. 重复计算逻辑(简单直接)
把hour_rate的计算式直接代入Last_Six_Hours里,虽然有点冗余,但适合逻辑不复杂的场景:
SELECT well_id, reported_date, (EXTRACT(EPOCH FROM age(reported_date, LAG(reported_date) OVER w))/3600)::int AS hour_rate, ((EXTRACT(EPOCH FROM age(reported_date, LAG(reported_date) OVER w))/3600)::int * 6)::int AS Last_Six_Hours FROM public.production WINDOW w AS (PARTITION BY well_id ORDER BY well_id, reported_date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW);
2. 使用CTE(清晰易维护)
用公共表表达式(CTE)先算出包含hour_rate的中间结果,再在外部查询里引用这个别名,可读性更好,适合复杂逻辑:
WITH production_with_hour_rate AS ( SELECT well_id, reported_date, (EXTRACT(EPOCH FROM age(reported_date, LAG(reported_date) OVER w))/3600)::int AS hour_rate FROM public.production WINDOW w AS (PARTITION BY well_id ORDER BY well_id, reported_date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ) SELECT well_id, reported_date, hour_rate, (hour_rate * 6)::int AS Last_Six_Hours FROM production_with_hour_rate;
3. 使用子查询(兼容旧版本)
如果你的PostgreSQL版本比较旧(不过现在大多都支持CTE了),可以用子查询代替:
SELECT well_id, reported_date, hour_rate, (hour_rate * 6)::int AS Last_Six_Hours FROM ( SELECT well_id, reported_date, (EXTRACT(EPOCH FROM age(reported_date, LAG(reported_date) OVER w))/3600)::int AS hour_rate FROM public.production WINDOW w AS (PARTITION BY well_id ORDER BY well_id, reported_date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) ) AS sub_query;
验证建议
你可以先检查下public.production表是否真的有hour_rate列,用这个语句:
SELECT column_name FROM information_schema.columns WHERE table_name = 'production' AND table_schema = 'public';
如果有这个列,那之前的查询会优先用表中的列,这也会导致结果不符合预期,这时候上面的解决方案同样适用。
内容的提问来源于stack exchange,提问作者Justin Massaro

