2010-09-29 3 views
0

J'ai plusieurs modèles d'enregistrement actif Rails: ContactEmails, ContactCalls, ContactPostalcards.ce SQL semble donner des résultats bizarres avec plusieurs JOINS, comment puis-je le corriger?

Chacun représente une activité terminée: un courriel, un appel ou une carte postale dûment rempli à un contact spécifique, et chaque contact appartient à une entreprise.

Je veux un résumé de haut niveau par la Compagnie de tous les e-mails, appels et Postalcards dans une plage de dates:

def self.get_list(report_start_date, report_end_date) 

    self.find :all, 
     :select => "companies.name AS co_name, 
       companies.id AS comp_id, 
       COUNT(contact_emails.id) AS email_count, 
       COUNT(contact_calls.id) AS call_count, 
       COUNT(contact_letters.id) AS letter_count, 
       COUNT(contact_postalcards.id) AS postalcard_count", 

     :conditions => ['contact_emails.date_sent < ? and contact_emails.date_sent > ? or 
         contact_calls.date_sent < ? and contact_calls.date_sent > ?', 
        report_end_date, report_start_date, report_end_date, report_start_date], 

     :joins => [ 
     "LEFT JOIN companies ON companies.id = contacts.company_id", 
     "LEFT JOIN contact_emails ON contact_emails.contact_id = contacts.id", 
     "LEFT JOIN contact_letters ON contact_letters.contact_id = contacts.id", 
     "LEFT JOIN contact_postalcards ON contact_postalcards.contact_id = contacts.id", 
     "LEFT JOIN contact_calls ON contact_calls.contact_id = contacts.id" 
     ], 
    #:group => "companies.id" 
     :group => "companies.name" 
    end 

Voilà comment je sortie la matrice dans mon point de vue:

<% @matrix_summary.each do |item| %>  
<tr> 
    <td><%= link_to item.co_name, company_path(item.comp_id) %></td> 
    <td><%= item.email_count %> </td> 
    <td><%= item.postalcard_count %></td> 
    <td><%= item.call_count %></td> 
<% end %> 
</tr> 

Mon attente est que chaque ligne doit représenter une société, avec le nombre respectif d'e-mails, de cartes postales et d'appels pour cette société (tous les contacts).

Ce que j'obtiens est la même valeur (et une valeur erronée) pour toutes les activités.

Que fais-je qui ne va pas?

Répondre

2

Si vous avez peu d'entreprises, la suggestion de @ klew est probablement la voie à suivre. Si vous avez beaucoup et que vous ne voulez exécuter qu'une seule requête, voici un aperçu qui illustre les sous-requêtes: http://gist.github.com/602944

Il est divisé en deux exemples, le premier est vraiment une introduction au fonctionnement des sous-requêtes, le seconde supprime toute la redondance, il est donc plus facile de modifier dans le futur.

+0

Je vais probablement avoir de nombreuses entreprises - beaucoup de signification pour un utilisateur spécifique 10-20 entreprises, et la requête pourrait être exécutée par plusieurs utilisateurs sur différents ensembles de données .... Je n'ai pas besoin d'utiliser une requête, mais je pensais qu'il pourrait être plus efficace (mais alors, je vais échanger la simplicité pour l'efficacité à ce stade) .... – Angela

+0

ah intéressant - merci pour l'amorce sur les sous-requêtes! – Angela

+0

I +1 parce que cela ressemble à la solution complète - cependant, je pense qu'à ce stade, je penche pour la lisibilité parce que je me rends compte que je suis encore en train de développer tôt, je dois le comprendre plus pour examiner et mettre en œuvre votre solution comme je reçois un meilleur handle .... – Angela

0

Exécutez le journal de votre serveur (tail -f log/development.log), puis exécutez votre action dans le navigateur. Voir la requête générée dans les journaux. Copiez cette requête et exécutez-la dans une console SQL. Est-ce que ça donne les bonnes réponses?

En second lieu, si vous souhaitez effectuer la personnalisation si lourd de trouver Rails, je vous recommande d'utiliser plutôt a) find_by_sql ou b) ActiveRecord :: Base.connection.execute(), etc.

1

Qu'est-ce que votre requête semble faire (je sais SQL mais pas Rails) retourne une ligne pour chaque carte postale, email etc qui sont ensuite groupés. Compte compte simplement le nombre de lignes dans le groupe de sorte que le nombre que vous obtenez sera le nombre total des 4 types.

Je suppose que vous devrez utiliser une boucle SQL gérée pour faire une boucle sur les clients, puis pour compter les enregistrements dans chaque catégorie pour chaque client, puis résumer les informations.

0

Je suggère de changer chacun de vos COUNT (nomchamp) pour être COUNT (DISTINCT nomchamp) - ainsi, par exemple, COUNT(contact_emails.id) AS email_count deviendrait COUNT(DISTINCT contact_emails.id) AS email_count.

0

Je suppose que vos modèles ressemblent à ceci:

# Company model 
has_many :contacts 
has_many :contact_emails, :through => :contacts 
has_many :contact_calls, :through => :contacts 
has_many :contact_letters, :through => :contacts 
has_many :contact_postalcard, :through => :contacts 

# Contact model 
has_many :contact_emails 
has_many :contact_calls 
has_many :contact_letters 
has_many :contact_postalcards 

# ContactEmail model 
scope :from, lambda {|start| where("date_sent > ?", start)} 
scope :to, lambda {|stop| where("date_sent < ?", stop)} 

# ContactCall model 
scope :from, lambda {|start| where("date_sent > ?", start)} 
scope :to, lambda {|stop| where("date_sent < ?", stop)} 

Ensuite, vous pouvez charger ce que vous voulez pour chaque entreprise comme ceci:

emails_count = @company.contact_emails.from(some_date).to(some_date2).size 
letters_count = @company.contact_letters.size 
etc. 

Cette solution fonctionnera de nombreuses requêtes SQL, mais bon et propre pour le début.

Vous pouvez également écrire des requêtes qui chargent tous les e-mails dans la plage de dates et les comptent avec le regroupement par société. Dans cette solution vous auriez seulement 5 requêtes (pour charger la compagnie et chaque "compte").

EDIT:

Je pense que 50 à 100 requêtes est « pas si mal » si elle est exécutée pas trop souvent.Vous pouvez toujours commencer avec ceci et quand votre application est en production, regardez simplement où sont les parties les plus lentes et ensuite optimisez-les. Cependant, cette requête semble poser des problèmes, donc je vous suggère de suivre la méthode proposée par @Tim. J'ai juste jeté un coup d'oeil là-dessus et il semble que c'est ce que vous cherchez. Mais vous devriez aussi avoir du code dans ma réponse, car vous en aurez probablement besoin dans votre application et vous pourrez l'utiliser pour voir si l'approche de @ Tim donne des résultats corrects.

Selon votre question, quelles entreprises vous devriez montrer. Donc, vous avez une @user qui est l'utilisateur actuel et User modèle que vous avez has_many :companies, donc:

# in controller 
# this should load all companies that belongs to @user and has at least one contact 
@companies = @user.companies.joins(:contacts).where("contacts.id is not null") 

# in view 
<% @companies.each do |item| %> 
    <tr> 
    <td><%= link_to item.co_name, company_path(item.comp_id) %></td> 
    <td><%= item.contact_emails.from(some_date).to(some_date2).size %> </td> 
    <td><%= item.contact_postalcards.size %></td> 
    <td><%= item.contact_calls.from(some_date).to(some_date2).size %></td> 
</tr> 
<% end %> 

Si une entreprise ne dispose pas d'appels, mais a des contacts e-mails, il affichera simplement 0 dans les appels colonne et un certain nombre dans la colonne des courriels.

+0

aha - Je devrais peut-être utiliser le par lequel je ne .... ce qui simplifierait probablement les choses. Je devrais alors parcourir plusieurs entreprises et faire 5 requêtes par entreprise, non? J'aurais un rapport avec 10-20 entreprises, est-ce une charge légère? – Angela

+0

Je pense que je veux aller avec une approche plus lisible pour l'instant - mais je ne sais pas comment jauger si je cours "trop ​​de requêtes" --- pensées? 10-20 entreprises pour un seul utilisateur – Angela

+0

Le défi que j'ai est que j'ai des courriels, des appels, des cartes postales, et des lettres - et certains comapnies ont seulement un sous-ensemble de l'un de ces .... alors je ne pense pas que je peut charger tout dans une plage de dates de courrier électronique car je dois encore le sortir comme une matrice ... – Angela

Questions connexes