Ответ 1
Я использую это:
Topic.where('id NOT IN (?)', Array.wrap(actions))
Где actions
представляют собой массив с: [1,2,3,4,5]
Редактировать:
Для обозначения Rails 4:
Article.where.not(title: ['Rails 3', 'Rails 5'])
Просто обновите это, так как кажется, что многие люди приходят к этому, если вы используете Rails 4, смотрите ответы от Trung Lê` и VinniVidiVicci.
Topic.where.not(forum_id:@forums.map(&:id))
Topic.where(published:true).where.not(forum_id:@forums.map(&:id))
Я надеюсь, что есть простое решение, которое не включает find_by_sql
, если не тогда, я думаю, что это сработает.
Я нашел эту статью, которая ссылается на это:
Topic.find(:all, :conditions => { :forum_id => @forums.map(&:id) })
что совпадает с
SELECT * FROM topics WHERE forum_id IN (<@forum ids>)
Мне интересно, есть ли способ сделать NOT IN
с этим, например:
SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)
Я использую это:
Topic.where('id NOT IN (?)', Array.wrap(actions))
Где actions
представляют собой массив с: [1,2,3,4,5]
Редактировать:
Для обозначения Rails 4:
Article.where.not(title: ['Rails 3', 'Rails 5'])
FYI, В Rails 4 вы можете использовать синтаксис not
:
Article.where.not(title: ['Rails 3', 'Rails 5'])
Вы можете попробовать что-то вроде:
Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.map(&:id)])
Возможно, вам понадобится @forums.map(&:id).join(',')
. Я не могу вспомнить, будет ли Rails аргументом в список CSV, если он перечислим.
Вы также можете сделать это:
# in topic.rb
named_scope :not_in_forums, lambda { |forums| { :conditions => ['forum_id not in (?)', forums.select(&:id).join(',')] }
# in your controller
Topic.not_in_forums(@forums)
Использование Arel:
topics=Topic.arel_table
Topic.where(topics[:forum_id].not_in(@forum_ids))
или, если это необходимо:
topics=Topic.arel_table
Topic.where(topics[:forum_id].in(@forum_ids).not)
а так как рельсы 4 на:
topics=Topic.arel_table
Topic.where.not(topics[:forum_id].in(@forum_ids))
Обратите внимание, что в конце концов вы не хотите, чтобы forum_ids являлся списком идентификаторов, а скорее подзапросом, если это так, вы должны сделать что-то вроде этого, прежде чем получать темы:
@forum_ids = Forum.where(/*whatever conditions are desirable*/).select(:id)
таким образом вы получаете все в одном запросе: что-то вроде:
select * from topic
where forum_id in (select id
from forum
where /*whatever conditions are desirable*/)
Также обратите внимание, что в конечном итоге вы не хотите этого делать, а скорее соединение - что может быть более эффективным.
Чтобы развернуть ответ @Trung Lê, в Rails 4 вы можете сделать следующее:
Topic.where.not(forum_id:@forums.map(&:id))
И вы могли бы сделать это еще дальше. Если вам нужно сначала фильтровать только опубликованные темы и , а затем отфильтровать идентификаторы, которые вы не хотите, вы можете сделать это:
Topic.where(published:true).where.not(forum_id:@forums.map(&:id))
Rails 4 делает это намного проще!
Принятое решение не выполняется, если @forums
пусто. Чтобы обойти это, я должен был сделать
Topic.find(:all, :conditions => ['forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id))])
Или, если вы используете Rails 3 +:
Topic.where( 'forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id)) ).all
Большинство ответов выше должно быть достаточно, но если вы делаете намного больше таких предикатов и сложных комбинаций, просмотрите Squeel. Вы сможете сделать что-то вроде:
Topic.where{{forum_id.not_in => @forums.map(&:id)}}
Topic.where{forum_id.not_in @forums.map(&:id)}
Topic.where{forum_id << @forums.map(&:id)}
Возможно, вам стоит взглянуть на плагин meta_where от Эрни Миллера. Ваш оператор SQL:
SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)
... может быть выражено следующим образом:
Topic.where(:forum_id.nin => @forum_ids)
Райан Бейтс из Railscasts создал отличный скринкаст, объясняющий MetaWhere.
Не уверен, что это то, что вы ищете, но для моих глаз это, безусловно, выглядит лучше, чем встроенный SQL-запрос.
Могут ли эти форумы форума разрабатываться прагматично? например вы можете найти эти форумы как-то - если это так, вы должны сделать что-то вроде
Topic.all(:joins => "left join forums on (forums.id = topics.forum_id and some_condition)", :conditions => "forums.id is null")
Что было бы более эффективным, чем выполнение SQL not in
Этот способ оптимизирован для удобства чтения, но он не так эффективен с точки зрения запросов к базе данных:
# Retrieve all topics, then use array subtraction to
# find the ones not in our list
Topic.all - @forums.map(&:id)
В оригинальном сообщении конкретно упоминается использование числовых идентификаторов, но я пришел сюда, ища синтаксис для выполнения NOT IN с массивом строк.
ActiveRecord прекрасно справится с этим:
Thing.where(['state NOT IN (?)', %w{state1 state2}])
Вы можете использовать sql в своих условиях:
Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", @forums.map(&:id)])
Съемка с jonnii:
Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.pluck(:id)])
используя вырезку, а не отображение над элементами
найдено через railsconf 2012 10 вещей, которые вы не знали, рельсы могли делать
Когда вы запрашиваете пустой массив, добавьте "< < 0" к массиву в блоке where, чтобы он не возвращал "NULL" и не разбивал запрос.
Topic.where('id not in (?)',actions << 0)
Если действия могут быть пустым или пустым массивом.
Вот более сложный запрос "не в", используя подзапрос в rails 4 с использованием squeel. Конечно, очень медленный по сравнению с эквивалентным sql, но эй, он работает.
scope :translations_not_in_english, ->(calmapp_version_id, language_iso_code){
join_to_cavs_tls_arr(calmapp_version_id).
joins_to_tl_arr.
where{ tl1.iso_code == 'en' }.
where{ cavtl1.calmapp_version_id == my{calmapp_version_id}}.
where{ dot_key_code << (Translation.
join_to_cavs_tls_arr(calmapp_version_id).
joins_to_tl_arr.
where{ tl1.iso_code == my{language_iso_code} }.
select{ "dot_key_code" }.all)}
}
Первые 2 метода в области являются другими областями, которые объявляют псевдонимы cavtl1 и tl1. & Л; < это не оператор в squeel.
Надеюсь, это поможет кому-то.