【问题标题】:MySQL statement to fetch a record based on multiple conditions through relational tablesMySQL 语句通过关系表根据多个条件获取一条记录
【发布时间】:2016-05-03 16:20:14
【问题描述】:

我正在生成一个查询,该查询返回一组满足下面发布的所有条件的配置文件

我在 Ruby 中像这样接收数据,然后根据内容进行动态 MySQL 查询 -

[{ attribute_id: 58, parent_profile_name: 'Douglas-Connelly' },
{ attribute_id: 26, parent_profile_name: 'Brekke LLC' },
{ attribute_id: 35, val: 'Asa' },
{ attribute_id: 38, val: 'Stanton' }]

这些是数据库的当前内容

profile_attribute_values 
profile_id  attribute_id    parent_profile_id   val
6           58              2
6           26              5
6           35              nil                 'Asa'
6           38              nil                 'Stanton'

profile
id     name
2      Douglas-Connelly 
5      Brekke LLC
6      nil

我需要返回所有满足所有条件的配置文件 - profilesprofile_attribute_values 有关系,其中 attribute_id 是 x,val 是 y,并且其中attribute_id 为x,parent_profile 名称= y

我目前拥有的东西

SELECT * FROM

(SELECT P2. * 
FROM profile_attribute_values PAV
INNER JOIN profiles P1 ON P1.id = PAV.parent_profile_id
INNER JOIN profiles P2 ON P2.id = PAV.profile_id
WHERE (PAV.ne_attribute_id = '58' AND P1.`name` = 'Douglas-Connelly')
) A,

(SELECT P1. * 
FROM profiles P1
INNER JOIN profile_attribute_values PAV ON P1.id = PAV.profile_id
INNER JOIN profile_attribute_values PAV2 ON P1.id = PAV2.profile_id
WHERE (PAV.ne_attribute_id = '35' AND PAV.val = 'ASA')
AND (PAV2.ne_attribute_id = '38' AND PAV2.val = 'Stanton')
) B

WHERE A.id = B.id

这将返回

profile
id    name
6     nil

这正是我想要的,虽然棘手的部分是第二个 parent_profile 条件,我需要 attribute_id 26 和 parent_profile_name: 'Brekke LLC'

我知道这行不通,但我需要这个来做这样的事情

SELECT * FROM

(SELECT P2. * 
FROM profile_attribute_values PAV
INNER JOIN profiles P1 ON P1.id = PAV.parent_profile_id
INNER JOIN profiles P2 ON P2.id = PAV.profile_id
WHERE (PAV.ne_attribute_id = '58' AND P1.`name` = 'Douglas-Connelly')
AND (PAV.ne_attribute_id = '26' AND P1.`name` = 'Brekke LLC')
) A,
.....

我正在动态生成 SQL 语句,所以我真的需要它尽可能干净。我通常对所有事情都使用 ruby​​ 活动记录,所以在谈到 SQL 语句时我有点小气。谢谢!

更新

好的,我找到了一个很好的解决方案来生成我需要的动态查询,只需一次调用数据库。这是完成的课程

class ProfileSearch
  def initialize(params, args = {})
    @attr_vals = params
    @options = args
    filter_attrs
  end

  attr_accessor :parent_attrs, :string_attrs

  def search
    Profile.joins(generate_query)
  end

  def generate_query
    q = ''
    q << parents_query
    q << attr_vals_query
  end

  def parents_query
    str = ''
    parent_attrs.each_with_index do |pa, i|
      str <<  "INNER JOIN profile_attribute_values PAVP#{i} ON profiles.id = PAVP#{i}.profile_id "\
              "AND PAVP#{i}.ne_attribute_id = #{pa} "\
              "INNER JOIN profiles PP#{i} ON PP#{i}.id = PAVP#{i}.parent_profile_id "\
              "AND PP#{i}.`name` = '#{@attr_vals[pa.to_s]}' "
    end
    str
  end

  def attr_vals_query
    str = ''
    string_attrs.each_with_index do |a, i|
      str << "INNER JOIN profile_attribute_values PAVS#{i} ON profiles.id = PAVS#{i}.profile_id "\
             "AND PAVS#{i}.ne_attribute_id = #{a} AND PAVS#{i}.val = '#{@attr_vals[a.to_s]}' "
    end
    str
  end

  def filter_attrs
    ne = NeAttribute.find(@attr_vals.keys)
    self.parent_attrs = ne.select{ |x| x.parent_profile)_attr? }.map(&:id)
    self.string_attrs = ne.select{ |x| x.string_attr? }.map(&:id)
  end
end

【问题讨论】:

    标签: mysql sql ruby activerecord


    【解决方案1】:

    您需要了解的第一件事是joins

    像这样使用它:

    relation_1 =  ProfileAttributeValue.joins("
      INNER JOIN profiles P1 ON P1.id = PAV.parent_profile_id
      INNER JOIN profiles P2 ON P2.id = PAV.profile_id
    ").where(..).pluck(:id)
    
    relation_2 = ProfileAttributeValue.joins...where...pluck(:id)
    

    relation_1 和relation_2 尚未运行。它们是 ActiveRecord 关系。

    我喜欢以下技巧:

    ProfileAttributeValue.where(["profile_attribute_values.id in (?) or profile_attribute_values.id in (?)]", relation_1, relation_2)
    

    同样,这只是一个 activerecord 关系。如果好奇,请致电to_sql。 添加更多 .where 或其他任何内容,运行时,它将是数据库上的单个查询。

    【讨论】:

    • 对 joins 方法的调用很好,尽管这仍然会创建多个查询,并且由于配置文件表的单独实例上的自连接,它并不能完全满足我的需求。我真的需要尽可能优化它,因为它被称为很多。我实际上确实想通了,请参阅上面的更新。
    【解决方案2】:

    发布我所做的作为答案,以防有人在上面错过它。这实质上创建了一个单一的连接查询,允许我提取所需的确切数据。生成的查询将如下所示 -

    SELECT `profiles`.* 
    FROM `profiles` 
    INNER JOIN profile_attribute_values PAVP0 ON profiles.id = PAVP0.profile_id 
        AND PAVP0.ne_attribute_id = 26 
    INNER JOIN profiles PP0 ON PP0.id = PAVP0.parent_profile_id 
        AND PP0.`name` = 'Brekke LLC' 
    INNER JOIN profile_attribute_values PAVP1 ON profiles.id = PAVP1.profile_id 
        AND PAVP1.ne_attribute_id = 58 
    INNER JOIN profiles PP1 ON PP1.id = PAVP1.parent_profile_id 
        AND PP1.`name` = 'Douglas-Connelly' 
    INNER JOIN profile_attribute_values PAVS0 ON profiles.id = PAVS0.profile_id 
        AND PAVS0.ne_attribute_id = 35 AND PAVS0.val = 'Asa' 
    INNER JOIN profile_attribute_values PAVS1 ON profiles.id = PAVS1.profile_id 
        AND PAVS1.ne_attribute_id = 38 AND PAVS1.val = 'Stanton'
    

    这是生成它的类

    class ProfileSearch
      def initialize(params, args = {})
        @attr_vals = params
        @options = args
        filter_attrs
      end
    
      attr_accessor :parent_attrs, :string_attrs
    
      def search
        Profile.joins(generate_query)
      end
    
      def generate_query
        q = ''
        q << parents_query
        q << attr_vals_query
      end
    
      def parents_query
        str = ''
        parent_attrs.each_with_index do |pa, i|
          str <<  "INNER JOIN profile_attribute_values PAVP#{i} ON profiles.id = PAVP#{i}.profile_id "\
                  "AND PAVP#{i}.ne_attribute_id = #{pa} "\
                  "INNER JOIN profiles PP#{i} ON PP#{i}.id = PAVP#{i}.parent_profile_id "\
                  "AND PP#{i}.`name` = '#{@attr_vals[pa.to_s]}' "
        end
        str
      end
    
      def attr_vals_query
        str = ''
        string_attrs.each_with_index do |a, i|
          str << "INNER JOIN profile_attribute_values PAVS#{i} ON profiles.id = PAVS#{i}.profile_id "\
                 "AND PAVS#{i}.ne_attribute_id = #{a} AND PAVS#{i}.val = '#{@attr_vals[a.to_s]}' "
        end
        str
      end
    
      def filter_attrs
        ne = NeAttribute.find(@attr_vals.keys)
        self.parent_attrs = ne.select{ |x| x.parent_profile)_attr? }.map(&:id)
        self.string_attrs = ne.select{ |x| x.string_attr? }.map(&:id)
      end
    end
    

    【讨论】:

      猜你喜欢
      • 2020-06-19
      • 2010-09-11
      • 1970-01-01
      • 1970-01-01
      • 2021-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多