Apenas para atualizar isso, pois parece que muitas pessoas chegam a isso, se você estiver usando o Rails 4, observe as respostas de Trung Lê` e VinniVidiVicci.
Topic.where.not(forum_id:@forums.map(&:id))
Topic.where(published:true).where.not(forum_id:@forums.map(&:id))
Espero que exista uma solução fácil que não envolva find_by_sql
, se não, acho que terá que funcionar.
Encontrei este artigo que faz referência a isso:
Topic.find(:all, :conditions => { :forum_id => @forums.map(&:id) })
que é o mesmo que
SELECT * FROM topics WHERE forum_id IN (<@forum ids>)
Gostaria de saber se existe uma maneira de fazer NOT IN
isso, como:
SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)
ruby-on-rails
rails-activerecord
Toby Joiner
fonte
fonte
Person.all(:name.not => ['bob','rick','steve'])
Respostas:
Trilhos 4+:
Trilhos 3:
Onde
actions
está uma matriz com:[1,2,3,4,5]
fonte
Topic.where('id NOT IN (?)', (actions.empty? ? '', actions)
. Ele ainda quebraria em zero, mas acho que a matriz que você passa geralmente é gerada por um filtro que retornará[]
no mínimo e nunca nulo. Eu recomendo verificar o Squeel, um DSL no topo do Active Record. Então você pode fazerTopic.where{id.not_in actions}
:, nil / empty / ou de outra forma..empty?
de.blank?
e você é nil-provaFYI, no Rails 4, você pode usar a
not
sintaxe:fonte
Você pode tentar algo como:
Você pode precisar fazer
@forums.map(&:id).join(',')
. Não me lembro se o Rails incluirá o argumento em uma lista CSV, se for enumerável.Você também pode fazer isso:
fonte
Usando o Arel:
ou, se preferir:
e desde os trilhos 4 em:
Observe que, eventualmente, você não deseja que os forum_ids sejam a lista de IDs, mas uma subconsulta; nesse caso, você deve fazer algo assim antes de obter os tópicos:
dessa maneira, você obtém tudo em uma única consulta: algo como:
Observe também que, eventualmente, você não deseja fazer isso, mas uma junção - o que pode ser mais eficiente.
fonte
EXPLAIN
!Para expandir a resposta @Trung Lê, no Rails 4 você pode fazer o seguinte:
E você poderia dar um passo adiante. Se você precisar primeiro filtrar apenas os Tópicos publicados e depois filtrar os IDs que não deseja, faça o seguinte:
O Rails 4 facilita muito!
fonte
A solução aceita falhará se
@forums
estiver vazia. Para contornar isso, eu tive que fazerOu, se estiver usando o Rails 3+:
fonte
A maioria das respostas acima deve bastar para você, mas se você estiver fazendo muito mais combinações de predicado e complexo, consulte Squeel . Você será capaz de fazer algo como:
fonte
Você pode dar uma olhada no plugin meta_where de Ernie Miller. Sua instrução SQL:
... poderia ser expresso assim:
Ryan Bates, do Railscasts, criou um belo screencast explicando o MetaWhere .
Não tenho certeza se é isso que você está procurando, mas aos meus olhos certamente parece melhor do que uma consulta SQL incorporada.
fonte
O post original menciona especificamente o uso de IDs numéricos, mas eu vim aqui procurando a sintaxe para fazer um NOT IN com uma matriz de strings.
O ActiveRecord também cuidará disso muito bem:
fonte
Esses IDs de fórum podem ser elaborados de maneira pragmática? por exemplo, você pode encontrar esses fóruns de alguma forma - se for esse o caso, faça algo como
O que seria mais eficiente do que fazer um SQL
not in
fonte
Dessa maneira, otimiza a legibilidade, mas não é tão eficiente em termos de consultas ao banco de dados:
fonte
Você pode usar o sql nas suas condições:
fonte
Pegando carona de Jonnii:
usando arrancar em vez de mapear sobre os elementos
encontrado via railsconf 2012 10 coisas que você não sabia que os trilhos poderiam fazer
fonte
Quando você consulta uma matriz em branco, adicione "<< 0" à matriz no bloco where para que não retorne "NULL" e quebre a consulta.
Se as ações pudessem ser uma matriz vazia ou em branco.
fonte
Topic.where("id NOT IN (?)", actions.presence || [0])
Aqui está uma consulta "não in" mais complexa, usando uma subconsulta no Rails 4 usando squeel. Claro que muito lento comparado ao sql equivalente, mas ei, funciona.
Os 2 primeiros métodos no escopo são outros escopos que declaram os aliases cavtl1 e tl1. << é o operador não no squeel.
Espero que isso ajude alguém.
fonte