У меня есть разные результаты из запроса для COUNT ('e.id') или COUNT (e.id) - PullRequest
0 голосов
/ 29 апреля 2018

У меня есть следующий код:

def self.department_members(department)
  where(organization_id: department.organization_id)
    .joins("LEFT JOIN core_employments As e ON
      e.organization_id = #{department.organization_id} AND
      core_members.user_id = e.user_id")
    .group('core_members.id')
end

def self.can_automerged(department)
  department_members(department).having("COUNT('e.id') = 1")
  # department_members(department).having("COUNT(e.id) = 1")
end

def self.can_not_automerged(department)
  department_members(department).having("Count('e.id') > 1")
end

Когда я использую

department_members(department).having("COUNT('e.id') = 1")

мой тест завершается без ошибок. Когда я использую

department_members(department).having("COUNT(e.id) = 1")

мой тест не пройден. Я не могу понять почему. Можешь объяснить почему? Я использую Rails-4 и PostgreSQL.

схема:

  create_table "core_members", force: :cascade do |t|
    t.integer  "user_id",                                    null: false
    t.integer  "project_id",                                 null: false
    t.boolean  "owner",                      default: false
    t.string   "login"
    t.string   "project_access_state"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "organization_id"
    t.integer  "organization_department_id"
  end

  create_table "core_employments", force: :cascade do |t|
    t.integer  "user_id"
    t.integer  "organization_id"
    t.boolean  "primary"
    t.string   "state"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "organization_department_id"
  end

тест:

module Core
  require "initial_create_helper"
  describe Member do
    describe "automerge" do
      before(:each) do
        @organization = create(:organization)
        @department1 = create(:organization_department,organization: @organization)
        @department2 = create(:organization_department,organization: @organization)

        @user = create(:user)
        @user_with_many_employments = create(:user)

        @department1.employments.create!(user: @user)
        @department1.employments.create!(organization: @organization, user: @user_with_many_employments)
        @department2.employments.create!(organization: @organization, user: @user_with_many_employments)
        @project = create_project
        @project.members.create!(user: @user,
                                organization: @organization)
        @project.members.create!(user: @user_with_many_employments,
                                organization: @organization)

      end

      it "::can_not_automerged" do
        expect(Member.can_not_automerged(@department1).to_a.map(&:user)).to match_array [@user_with_many_employments]
      end
      it "::can_automerged" do
        expect(Member.can_automerged(@department1).to_a.map(&:user)).to match_array [@user]
      end
    end
  end
end

1 Ответ

0 голосов
/ 29 апреля 2018

У меня разные результаты из запроса для COUNT ('e.id') или COUNT (e.id)

'e.id' - строковая константа, поэтому COUNT('e.id') - это просто неловкий, вводящий в заблуждение способ сказать COUNT(*).

COUNT(e.id), с другой стороны, считает все строки в результате, где e.id IS NOT NULL - так как count() не считает значения NULL.

Мануал о count():

count(*) ... количество строк ввода

count(expression) ... количество входных строк, для которых значение выражения не равно нулю

Как видите, внутри есть даже две отдельные функции. А также Следует отметить, что count(*) немного быстрее. Так что используйте это, если вам не нужен второй вариант. Связанный:

Вы можете противостоять:
"Но e.id - это PRIMARY KEY из core_employments, поэтому оно определено NOT NULL!"

Но это не учитывает условное LEFT JOIN в вашем запросе, которое все еще вводит значения NULL в столбце NOT NULL, где условия соединения не выполняются. Связанный:

Тем не менее, LEFT [OUTER] JOIN также вводит в заблуждение. Позднее состояние

having("COUNT(e.id) = 1")

заставляет его действовать как равнина [INNER] JOIN. Как только вы исправите это, вы можете упростить до:

having("COUNT(*) = 1")

И если все, что вас волнует, это то, что по крайней мере одна связанная строка существует в core_employments, переводя в having("COUNT(*) >= 1"), превосходную (более четкую, быструю) технику в простых случаях будет EXISTS полусоединение :

WHERE EXISTS (SELECT FROM core_employments WHERE <conditions>)
...