简体   繁体   中英

“NOT IN” for Active Record

I have a MySQL query that I am trying to chain a "NOT IN" at the end of it.

Here is what it looks like in ruby using Active Record:

not_in = find_by_sql("SELECT parent_dimension_id FROM relations WHERE relation_type_id  = 6;").map(&:parent_dimension_id)

      joins('INNER JOIN dimensions ON child_dimension_id = dimensions.id')
          .where(relation_type_id: model_relation_id,
                 parent_dimension_id: sub_type_ids,
                 child_dimension_id: model_type)
          .where.not(parent_dimension_id: not_in)

So the SQL query I'm trying to do looks like this:

INNER JOIN dimensions ON child_dimension_id = dimensions.id
WHERE relations.relation_type_id = 5 
AND relations.parent_dimension_id 
NOT IN(SELECT parent_dimension_id FROM relations WHERE relation_type_id  = 6);

Can someone confirm to me what I should use for that query? do I chain on where.not ?

If you really do want

SELECT parent_dimension_id
FROM relations
WHERE relation_type_id = 6

as a subquery, you just need to convert that SQL to an ActiveRecord relation:

Relation.select(:parent_dimension_id).where(:relation_type_id => 6)

then use that as a value in a where call the same way you'd use an array:

not_parents = Relation.select(:parent_dimension_id).where(:relation_type_id => 6)

Relation.joins('...')
        .where(relation_type_id: model_relation_id, ...)
        .where.not(parent_dimension_id: not_parents)

When you use an ActiveRecord relation as a value in a where and that relation selects a single column:

r = M1.select(:one_column).where(...)
M2.where(:column => r)

ActiveRecord is smart enough to inline r 's SQL as an in (select one_column ...) rather than doing two queries.

You could probably replace your:

joins('INNER JOIN dimensions ON child_dimension_id = dimensions.id')

with a simpler joins(:some_relation) if your relations are set up too.

You can feed where clauses with values or arrays of values, in which case they will be translated into in (?) clauses.

Thus, the last part of your query could contain a mapping:

.where.not(parent_dimension_id:Relation.where(relation_type_id:6).map(&:parent_dimension_id))

Or you can prepare a statement

.where('parent_dimension_id not in (?)', Relation.where(relation_type_id:6).map(&:parent_dimension_id) )

which is essentially exactly the same thing

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM