2010-09-21 8 views
4

dans des rails, j'ai 2 tables:Comment sélectionner les enregistrements où un enfant n'existe pas

bans(ban_id, admin_id) 
ban_reasons(ban_reason_id, ban_id, reason_id) 

Je veux trouver tous les bans pour un certain administration où il n'y a pas d'enregistrement dans le tableau ban_reasons. Comment puis-je faire cela dans Rails sans boucler tous les enregistrements d'interdiction et filtrer tous ceux avec ban.ban_reasons.nil? Je veux le faire (heureusement) en utilisant une seule instruction SQL.

Je dois juste faire: (Mais je veux faire le « rails » chemin)

SELECT bans.* FROM bans WHERE admin_id=1234 AND 
           ban_id NOT IN (SELECT ban_id FROM ban_reasons) 

Répondre

2

Votre solution fonctionne très bien (une seule demande), mais il est presque simple SQL:

bans = Ban.where("bans.id NOT IN (SELECT ban_id from ban_reason)") 

Vous pouvez également essayer ce qui suit, et laisser les rails font partie du travail:

bans = Ban.where("bans.id NOT IN (?)", BanReason.select(:ban_id).map(&:ban_id).uniq) 
+0

Y a-t-il une différence de performance entre les deux? Le second entraîne-t-il plus de requêtes? –

+0

Oui, vous obtenez 1 requête de plus avec le second ... Je pense que le problème de performance dépend vraiment de la taille de votre DB. – Yannis

+0

Je suppose que si le but est d'utiliser Rails quel que soit le coût alors la suggestion fonctionne, mais la performance va souffrir horriblement pour les grands ensembles de données. –

2

ActiveRecord ne vous arrive à un point, après tout devrait être fait par SQL brut. La bonne chose à propos de l'AR est que cela rend la tâche facile.

Cependant, depuis Rails 3, vous pouvez presque tout faire avec l'API AREL, bien que le langage SQL brut puisse sembler plus lisible.

je partirais avec SQL premières et voici une autre question, vous pouvez essayer si le vôtre ne fonctionne pas bien:

SELECT  b.* 
FROM   bans b 
LEFT JOIN ban_reason br on b.ban_id = br.ban_id 
WHERE  br.ban_reason_id IS NULL 
+0

Cela va fonctionner mieux sur la plupart des db que l'autre ci-dessus. Beaucoup de db ont des performances IN assez faibles (où ils trouvent des sets). Cela aura un certain impact sur les performances, aussi bien que la recherche d'une valeur nulle sur la jointure, mais je pense toujours que c'est probablement mieux que ce qui précède. – Scott

+0

L'implémenter comme NOT EXISTS() serait une syntaxe plus naturelle pour cette requête je pense, mais je pense que vous avez raison que SQL est la voie à suivre. –

0

en utilisant Where Exists gem (dont je suis l'auteur):

Ban.where(admin_id: 123).where_not_exists(:ban_reasons) 
Questions connexes