I am trying to run a very simple query on Postgres.
SQLite3
With Sqlite3, this query works perfectly:
>> SELECT DISTINCT "users".* FROM "users" INNER JOIN "comments" ON "comments"."user_id" = "users"."id" ORDER BY "comments"."rating" ASC
that returns
<ActiveRecord::Relation [#<User id: 1, name: "Ola">, #<User id: 2, name: "Hello">, #<User id: 3, name: "Wow">]>
Postgres
But in Postgres this same query leads to the famous error:
ActiveRecord::StatementInvalid: PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
LINE 1: ..." ON "comments"."user_id" = "users"."id" ORDER BY "comments"...
^
: SELECT DISTINCT "users".* FROM "users" INNER JOIN "comments" ON "comments"."user_id" = "users"."id" ORDER BY "comments"."rating" ASC
If I want to make it work in Postgres, I have to use the DISTINCT
in a subquery.
>> SELECT "users".* FROM (SELECT DISTINCT "users".* FROM "users" INNER JOIN "comments" ON "comments"."user_id" = "users"."id") users INNER JOIN "comments" ON "comments"."user_id" = "users"."id" ORDER BY "comments"."rating" ASC
Bug
here are the steps to reproduce:
- with sqlite3
require "active_record"
require "minitest/autorun"
require "logger"
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :comments, force: true do |t|
t.string :name
t.integer :rating
t.integer :user_id
end
create_table :users, force: true do |t|
t.string :name
end
end
class User < ActiveRecord::Base
has_many :comments
def self.order_by_rating
joins(:comments).merge(Comment.order(:rating))
end
def self.with_comments
joins(:comments).distinct
end
end
class Comment < ActiveRecord::Base
belongs_to :user
end
class BugTest < Minitest::Test
def test_association_stuff
user1 = User.create(name: 'Ola')
user2 = User.create(name: 'Hello')
user3 = User.create(name: 'Wow')
comment1 = Comment.create(user: user1, rating: 1)
comment2 = Comment.create(user: user2, rating: 2)
comment3 = Comment.create(user: user2, rating: 3)
comment3 = Comment.create(user: user3, rating: 5)
comment4 = Comment.create(user: user1, rating: 2)
assert_equal([user1, user2, user3], User.with_comments.order_by_rating)
end
end
- with postgresql
require 'bundler/inline'
gemfile(true) do
source 'https://rubygems.org'
gem 'activerecord'
gem 'pg'
end
require "active_record"
require "minitest/autorun"
require "logger"
ActiveRecord::Base.logger = Logger.new(STDOUT)
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "postgresql", port: 5432, username: 'test', password: 'test', database: 'test')
ActiveRecord::Base.establish_connection(adapter: "postgresql", database: "template1")
ActiveRecord::Schema.define do
recreate_database 'rails_issue'
end
ActiveRecord::Base.connection.disconnect!
ActiveRecord::Base.establish_connection(adapter: "postgresql", database: "rails_issue")
ActiveRecord::Schema.define do
create_table :comments, force: true do |t|
t.string :name
t.integer :rating
t.integer :user_id
end
create_table :users, force: true do |t|
t.string :name
end
end
class User < ActiveRecord::Base
has_many :comments
def self.order_by_rating
joins(:comments).merge(Comment.order(:rating))
end
def self.with_comments
joins(:comments).distinct
end
end
class Comment < ActiveRecord::Base
belongs_to :user
end
class BugTest < Minitest::Test
def test_association_stuff
user1 = User.create(name: 'Ola')
user2 = User.create(name: 'Hello')
user3 = User.create(name: 'Wow')
comment1 = Comment.create(user: user1, rating: 1)
comment2 = Comment.create(user: user2, rating: 2)
comment3 = Comment.create(user: user2, rating: 3)
comment3 = Comment.create(user: user3, rating: 5)
comment4 = Comment.create(user: user1, rating: 2)
assert_equal([user1, user2, user3], User.with_comments.order_by_rating)
end
end
This is postgreSQL specific. https://stackoverflow.com/a/18367248/853744