【问题标题】:Chaining multiple ActiveRecord `or` queries链接多个 ActiveRecord `or` 查询
【发布时间】:2019-12-13 11:41:58
【问题描述】:

我有一个列数组,我想循环并选择将or 查询链接到 ActiveRecord 查询链。我可以让它工作,但是生成的查询将or 附加到查询链上,因此使我的初始查询中的列是可选的。这是我的课:

class Claim
  class MatchingAttributeFinder
    ATTRIBUTE_GROUPS_TO_MATCH = [
      ["teacher_reference_number"],
      ["email_address"],
      ["national_insurance_number"],
      ["bank_account_number", "bank_sort_code", "building_society_roll_number"],
    ].freeze

    def initialize(source_claim, claims_to_compare = Claim.submitted)
      @source_claim = source_claim
      @claims_to_compare = claims_to_compare
    end

    def matching_claims
      claims = @claims_to_compare.where.not(id: @source_claim.id)

      ATTRIBUTE_GROUPS_TO_MATCH.each do |attributes|
        vals = values_for_attributes(attributes)

        next if vals.blank?

        concatenated_columns = "CONCAT(#{attributes.join(",")})"

        claims = claims.or(
          Claim.where("LOWER(#{concatenated_columns}) = LOWER(?)", vals.join)
        )
      end

      claims
    end

    private

    def values_for_attributes(attributes)
      attributes.map { |attribute|
        @source_claim.read_attribute(attribute)
      }.reject(&:blank?)
    end
  end
end

生成的 SQL 如下所示:

SELECT "claims".* FROM "claims" WHERE (((("claims"."submitted_at" IS NOT NULL AND "claims"."id" != 'a7b25b99-4477-42b1-96ab-8262582c5541' OR (LOWER(CONCAT(teacher_reference_number)) = LOWER('0902344'))) OR (LOWER(CONCAT(email_address)) = LOWER('genghis.khan@mongol-empire.com'))) OR (LOWER(CONCAT(national_insurance_number)) = LOWER('QQ891011C'))) OR (LOWER(CONCAT(bank_account_number,bank_sort_code,building_society_roll_number)) = LOWER('34682151972654123456789/ABCD')))

但我真正想要的更像是这样的:

SELECT "claims".* FROM "claims" WHERE "claims"."submitted_at" IS NOT NULL AND "claims"."id" != 'd6a53b4d-c569-49e6-a2ea-ac44b69b0451' AND (LOWER(concat(teacher_reference_number)) = LOWER('0902344') OR LOWER(concat(email_address)) = LOWER('genghis.khan@mongol-empire.com') OR LOWER(concat(national_insurance_number)) = LOWER('QQ891011C') OR LOWER(concat(bank_account_number,bank_sort_code,building_society_roll_number)) = LOWER('34682151972654123456789/ABCD'))

有没有什么方法可以设置一个空范围之类的东西,我可以将我的OR 查询链接到?

【问题讨论】:

    标签: ruby-on-rails postgresql rails-activerecord ruby-on-rails-6


    【解决方案1】:

    尝试先将所有“或”连在一起,然后链接原始查询

    def matching_claims
      claims = @claims_to_compare.where.not(id: @source_claim.id)
    
      ors = nil
    
      ATTRIBUTE_GROUPS_TO_MATCH.each do |attributes|
        vals = values_for_attributes(attributes)
    
        next if vals.blank?
    
        concatenated_columns = "CONCAT(#{attributes.join(",")})"
    
        aux = Claim.where("LOWER(#{concatenated_columns}) = LOWER(?)", vals.join)
        if ors.nil?
          ors = aux
        else
          ors = ors.or(aux)
        end
      end
    
      claims.merge(ors)
    end
    

    【讨论】:

    • 啊,我试过这样的方法。唯一的问题是 to_sql 生成完整的 SQL 查询(包括 SELECT *.. 部分),所以我收到错误 ERROR: subquery must return only one column
    猜你喜欢
    • 2011-04-08
    • 1970-01-01
    • 1970-01-01
    • 2015-12-21
    • 2017-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    相关资源
    最近更新 更多