2016-05-03 1 views
2

Я генерация запроса, который возвращает набор профилей, которые отвечают все условия размещены нижеMySQL заявления для извлечения записи на основе нескольких условий через реляционные таблицы

Я получаю данные, как это в 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 

Мне нужно вернуть все профили которые отвечают всем условиям - профили, которые имеют отношение к profile_attribute_values ​​, где attribute_id является х, а Допустимы у, где attribute_id является х, а имя parent_profile = у

ЧТО Я НАСТОЯЩЕЕ ИМЕЮТ

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: «Брекки ООО»

Я знаю, что это не будет работать, но мне это нужно сделать что-то вроде этого

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 динамически, поэтому мне действительно нужно, чтобы она была максимально чистой. Обычно я использую рубиновую активную запись для всего, поэтому я немного приукрашиваю, когда дело доходит до SQL-заявлений. Благодаря!

UPDATE

Хорошо, я нашел отличное решение для создания динамического запроса, что мне нужно, делает один вызов к базе данных. Это готовый класс

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 

ответ

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 или что угодно, и при запуске это будет один запрос в БД.

+0

Хороший вызов на стыки метод, хотя это по-прежнему создает несколько запросов, и его не получает именно то, что мне нужно , из-за самостоятельного присоединения к отдельному экземпляру таблицы профилей. Мне действительно нужно, чтобы это было максимально оптимизировано, так как его много называли. Я действительно понял, хотя, см. Обновление выше. –

1

Проводка, что я сделал как ответ, в случае, если кто-то пропустит это выше. Это по существу создает единый запрос на объединение, который позволяет мне извлекать точные данные.Запрос генерируется будет выглядеть следующим образом -

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 
Смежные вопросы