Skip to content

Instantly share code, notes, and snippets.

@coralieco
Last active November 23, 2018 16:50
Show Gist options
  • Save coralieco/6cd4316a560b53dd27a4f63c1dbee8ad to your computer and use it in GitHub Desktop.
Save coralieco/6cd4316a560b53dd27a4f63c1dbee8ad to your computer and use it in GitHub Desktop.
Result from query differs between Postgres and SQLite3

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:

  1. 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
  1. 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
@bluegod
Copy link

bluegod commented Nov 23, 2018

This is postgreSQL specific. https://stackoverflow.com/a/18367248/853744

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment