为什么PostgreSQL不识别我查询中自定义的vote_count计数列?
问题根因
- SQL语法兼容性差异:SQLite对非标准SQL语法的容错度远高于PostgreSQL。你给统计字段设置别名时使用
AS people.vote_count的写法不符合SQL标准,PostgreSQL会将其识别为「查找people表下的vote_count字段」,而这个字段本身不存在,所以抛出未定义列的错误。SQLite则将带点的别名识别为普通字段名,因此可以正常运行。 - 如果你修改别名为
vote_count后仍然报错,大概率是order子句中还保留了people.vote_count的写法,PostgreSQL依然会匹配表字段而非你定义的别名。 - 额外说明:
uniq!(:group)的写法是错误的,uniq!是Ruby数组的修改方法,作用于未加载的ActiveRecord Relation时不会产生预期的去重效果。按people.id分组本身就已经保证返回的Person记录唯一,这段代码可以直接删除。
适配PostgreSQL的修改方案
直接将统计字段别名设为vote_count即可,只要select子句中包含该字段,Rails会自动将其映射为Person实例的属性,你依然可以在视图中直接通过@person.vote_count访问,不需要加people.前缀。同时建议避免直接拼接show.id到SQL字符串,改用参数绑定规避SQL注入风险。
修改后的order_by_votes方法代码如下:
def self.order_by_votes(show = nil) people = left_joins(:votes).group(:id) if show count_expr = "CASE WHEN votes.show_id = ? AND NOT votes.fulfilled THEN 1 ELSE NULL END" people = people.select("people.*, COUNT(#{count_expr}) AS vote_count", show.id) else count_expr = "NULLIF(votes.fulfilled, true)" people = people.select("people.*, COUNT(#{count_expr}) AS vote_count") end people.order(vote_count: :desc) end
如果想使用更规范、兼容性更强的Arel写法避免硬编码SQL片段,也可以改成:
def self.order_by_votes(show = nil) votes_table = Vote.arel_table count_expr = if show Arel::Nodes::Case.new .when(votes_table[:show_id].eq(show.id).and(votes_table[:fulfilled].eq(false))) .then(1) .else(nil) else Arel::Nodes::NamedFunction.new('NULLIF', [votes_table[:fulfilled], Arel::Nodes::True.new]) end left_joins(:votes) .group(:id) .select(arel_table[Arel.star], Arel::Nodes::Count.new(count_expr).as('vote_count')) .order(vote_count: :desc) end
内容的提问来源于stack exchange,提问作者Christoffer Reijer
相关产品推荐
相关产品推荐

