Jak wyrazić zapytanie Nie w ActiveRecord/Rails?

Aby to zaktualizować, ponieważ wydaje się, że wiele osób do tego dochodzi, jeśli używasz Rails 4, spójrz na odpowiedzi Trung Lê ' i VinniVidiVicci.

Topic.where.not(forum_id:@forums.map(&:id))

Topic.where(published:true).where.not(forum_id:@forums.map(&:id))

Mam nadzieję, że istnieje łatwe rozwiązanie, które nie obejmuje find_by_sql, Jeśli nie, to chyba będzie musiało zadziałać.

Znalazłem Ten artykuł który odwołuje się do tego:

Topic.find(:all, :conditions => { :forum_id => @forums.map(&:id) })

Czyli to samo co

SELECT * FROM topics WHERE forum_id IN (<@forum ids>)

Zastanawiam się, czy jest jakiś sposób, aby zrobić NOT IN z tym, jak:

SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)
Author: user2262149, 2010-11-29

15 answers

Używam tego:

Topic.where('id NOT IN (?)', Array.wrap(actions))

Gdzie actions jest tablicą z: [1,2,3,4,5]

Edit:

Dla notacji Rails 4:

Article.where.not(title: ['Rails 3', 'Rails 5']) 
 269
Author: José Castro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-28 22:43:24

FYI, w Rails 4 możesz użyć składni not:

Article.where.not(title: ['Rails 3', 'Rails 5'])
 145
Author: Trung Lê,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-16 00:21:38

Możesz spróbować czegoś takiego:

Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.map(&:id)])

Być może będziesz musiał to zrobić @forums.map(&:id).join(','). Nie pamiętam, czy Rails zamieni argument na listę CSV, jeśli można go wyliczyć.

Możesz też to zrobić:
# 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)
 50
Author: jonnii,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-10 01:04:52

Używając Arel:

topics=Topic.arel_table
Topic.where(topics[:forum_id].not_in(@forum_ids))

Lub, jeśli preferowane:

topics=Topic.arel_table
Topic.where(topics[:forum_id].in(@forum_ids).not)

I od rails 4 na:

topics=Topic.arel_table
Topic.where.not(topics[:forum_id].in(@forum_ids))

Zauważ, że w końcu nie chcesz, aby forum_ids było listą ids, ale raczej zapytaniem podrzędnym, jeśli tak, to powinieneś zrobić coś takiego przed uzyskaniem tematów:

@forum_ids = Forum.where(/*whatever conditions are desirable*/).select(:id)

W ten sposób otrzymujesz wszystko w jednym zapytaniu: coś w stylu:

select * from topic 
where forum_id in (select id 
                   from forum 
                   where /*whatever conditions are desirable*/)

Zauważ również, że w końcu nie chcesz tego robić, ale raczej dołączyć - co może być więcej wydajny.

 50
Author: Pedro Morte Rolo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-27 16:05:23

Aby rozwinąć @Trung Lê answer, w Rails 4 możesz wykonać następujące czynności:

Topic.where.not(forum_id:@forums.map(&:id))
I mógłbyś pójść o krok dalej. Jeśli najpierw musisz filtrować tylko opublikowane tematy i , a następnie odfiltrować identyfikatory, których nie chcesz, możesz to zrobić:
Topic.where(published:true).where.not(forum_id:@forums.map(&:id))

Rails 4 sprawia, że jest to o wiele łatwiejsze!

 17
Author: VinniVidiVicci,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-22 16:08:29

Przyjęte rozwiązanie nie powiedzie się, jeśli @forums jest puste. Aby obejść to, musiałem to zrobić

Topic.find(:all, :conditions => ['forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id))])

Lub, jeśli używasz Rails 3+:

Topic.where( 'forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id)) ).all
 12
Author: Filipe Giusti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-10 01:01:30

Większość powyższych odpowiedzi powinna Ci wystarczyć, ale jeśli robisz dużo więcej takich predykatów i złożonych kombinacji sprawdź Squeel . Będziesz mógł zrobić coś takiego:

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)}
 4
Author: jake,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-10 00:59:48

Możesz rzucić okiem na wtyczkę meta_where autorstwa Erniego Millera. Twoje polecenie SQL:

SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)

...można wyrazić tak:

Topic.where(:forum_id.nin => @forum_ids)
Ryan Bates z Railscasts stworzyłładny screencast wyjaśniający MetaWhere .

Nie jestem pewien, czy to jest to, czego szukasz, ale moim oczom na pewno wygląda lepiej niż osadzone zapytanie SQL.

 2
Author: Marcin Wyszynski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-10 01:02:11

Czy te identyfikatory forum można wypracować w sposób pragmatyczny? np. czy można jakoś znaleźć te fora-jeśli tak to należy zrobić coś w stylu

Topic.all(:joins => "left join forums on (forums.id = topics.forum_id and some_condition)", :conditions => "forums.id is null")

Który byłby bardziej efektywny niż robienie SQL not in

 1
Author: Omar Qureshi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-29 20:24:18

Ten sposób optymalizuje czytelność, ale nie jest tak skuteczny pod względem zapytań do bazy danych:

# Retrieve all topics, then use array subtraction to
# find the ones not in our list
Topic.all - @forums.map(&:id)
 1
Author: evanrmurphy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-28 07:31:20

Oryginalny post wyraźnie wspomina o używaniu identyfikatorów numerycznych, ale przyszedłem tutaj szukając składni do robienia NOT in z tablicą łańcuchów.

ActiveRecord też sobie z tym poradzi:

Thing.where(['state NOT IN (?)', %w{state1 state2}])
 1
Author: Andy Triggs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-20 11:42:41

Możesz używać sql w swoich warunkach:

Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", @forums.map(&:id)])
 0
Author: tjeden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-29 19:58:26

Piggybacking off of jonnii:

Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.pluck(:id)])

Używanie plucka zamiast mapowania elementów

Znaleziono przez railsconf 2012 10 rzeczy, o których nie wiedziałeś, że rails może zrobić

 0
Author: Thomas Wolfe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-13 17:15:15

Gdy odpytywasz pustą tablicę, dodaj "

Topic.where('id not in (?)',actions << 0)

Jeśli akcje mogą być pustą lub pustą tablicą.

 0
Author: itsEconomics,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-18 18:33:31

Tutaj jest bardziej złożone zapytanie "Nie w", używając subquery w rails 4 używając squeela. Oczywiście bardzo wolno w porównaniu do równoważnego sql, ale hej, to działa.

    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)}
    }

Pierwsze 2 metody w zakresie to inne zakresy deklarujące aliasy cavtl1 i tl1. Mam nadzieję, że to komuś pomoże.

 0
Author: dukha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-10 04:09:42