2011-01-16 6 views
2

J'ai un problème. J'ai 4 tables:mySQL Left Rejoindre plusieurs tables avec conditions

Invoice_Payment, la facture, le client et le calendrier

Fondamentalement, je la requête ci-dessous, qui fonctionne bien, sauf, mois sans date_due ne reviennent pas. I.E seulement mois avec un date_due sera retourné.

Remarque: la table de calendrier répertorie simplement tous les jours de l'année. Il contient un seul appel col de date_field

Base de données: http://oberto.co.nz/jInvoice.txt

Sortie prévue: ma requête en cours, ci-dessous, retournera quelque chose comme:

month  total 
August  5 
September 0 
October 196 
November 205 
December 214 
January 229 

Remarquez comment Septembre n'est pas retourné? C'est parce que la table Invoice_Payment n'a pas d'enregistrement date_due

Je pense que je dois utiliser une jointure gauche et rejoindre la table du calendrier avec quelque chose comme: LEFT JOIN Calendrier ON Invoice_Payments.date_paid = Calendar.date_field. Mais je n'ai pas de chance

SELECT MONTHNAME(Invoice_Payments.date_paid) as month, SUM(Invoice_Payments.paid_amount) AS total 
FROM Invoice, Client, Invoice_Payments 
WHERE Client.registered_id = 1 
AND Client.id = Invoice.client_id 
And Invoice.id = Invoice_Payments.invoice_id 
AND date_paid IS NOT NULL 
GROUP BY YEAR(Invoice_Payments.date_paid), MONTH(Invoice_Payments.date_paid) 

Toute aide appréciée.

+0

date_due est dans le tableau de facture ou d'une table invoice_payment ?? – shankhan

+0

Je ne vois nulle part dans la requête filtrant 'date_due'. êtes-vous sûr qu'il ne retourne pas ces lignes? –

+0

Pourquoi ** abandonnerait-il ** la table 'calendar'? –

Répondre

4

Il semble que vous essayiez de trouver des valeurs pour toutes les dates d'une plage, qu'il y ait ou non une valeur. Supposons que nous avons une table de calendrier structuré comme ceci:

Create Table Calendar 
(
    [Date] not null Primary Key 
) 

Votre requête pourrait ressembler si (où X et Y représentent le début et la date de fin de la plage dans laquelle vous enquêtez):

Select Year(C.Date), MonthName(C.Date) As Month 
    , Coalesce(Sum(IP.paid_amount),0) As Total 
From Calendar As C 
    Left Join (Invoice As I 
     Join Client As C1 
      On C1.id = I.client_id 
       And C.registered_id = 1 
     Join Invoice_Payments As IP 
      On IP.Invoice_id = I.Id) 
     On IP.date_paid = C.date 
Where C.Date Between X and Y 
Group By Year(C.Date), MonthName(C.Date) 

Techniquement, la requête ci-dessus devrait faire l'affaire. Cependant, une autre alternative est d'utiliser une table dérivée dont vous enquis dans les commentaires:

Select Year(C.Date), MonthName(C.Date) As Month 
    , Coalesce(Sum(Z.paid_amount),0) As Total 
From Calendar As C 
    Left Join (
       Select IP.date_paid, IP.paid_amount 
       From Invoice As I 
         Join Client As C1 
          On C1.id = I.client_id 
           And C.registered_id = 1 
         Join Invoice_Payments As IP 
          On IP.Invoice_id = I.Id 
       ) As Z 
     On Z.date_paid = C.date 
Where C.Date Between X and Y 
Group By Year(C.Date), MonthName(C.Date) 
+0

Salut Thomas. Merci d'avoir répondu. Je reçois "# 1066 - Pas unique table/alias: 'C'" – Jarrod

+0

@Jarrod - Correction. J'ai utilisé l'alias "C" pour la table Calendrier et la table Client. Avoir simplement changé l'alias sur la table client. – Thomas

+0

Salut Thomas, merci pour l'édition. Cependant, j'ai toujours des erreurs que je ne peux pas résoudre: "Colonne inconnue 'I.date_paid' dans 'on clause'". J'ai ajouté le diagramme suivant pour aider à comprendre mon problème: http://oberto.co.nz/db-sql.png – Jarrod

0

Je définirais explicitement toutes vos jointures pour la lisibilité.

Un exemple simple d'une jointure externe en conservant toutes les lignes de la première table, remplissant les colonnes manquantes dans la seconde table avec nulls:

select column1, 
from table1 
left outer join table2 
    on table1.key = table2.key 

En supposant que la table de calendrier est celui que vous faire perdre des données, Voici une photo de votre requête:

SELECT MONTHNAME(Invoice_Payments.date_paid) as month, 
     SUM(Invoice_Payments.paid_amount) 
FROM Invoice 
JOIN Client 
    ON Client.id = Invoice.client_id 
AND Client.registered_id = 1 
JOIN Invoice_Payments 
    ON Invoice_Payments.invoice_id = Invoice.id 
LEFT OUTER JOIN Calendar 
    ON Calendar.key = #yourothertable#.key  
WHERE date_paid IS NOT NULL 
GROUP BY YEAR(Invoice_Payments.date_paid), MONTH(Invoice_Payments.date_paid) 

Bonne chance!

+0

Salut Cag. Merci d'avoir répondu. Je pense que c'est sur la bonne voie. Mais c'est la table Invoice_Payment qui me fait perdre des données. J'ai mis à jour ma question ci-dessus pour le montrer. S'il n'y a pas d'enregistrement dans la table Invoice_Payment pour un mois donné, alors ce mois ne s'affichera pas. Et c'est où je suis coincé – Jarrod

+0

Hey Jarrod, vous pourriez avoir compris maintenant, mais si vous perdez des données à cause de la table Invoice_Payment alors vous pouvez changer cette jointure particulière dans la déclaration que j'ai écrit à: 'LEFT OUTER JOIN Invoice_Payments 'et supprimer les deux lignes sur le calendrier – cag

1

J'ai rencontré un problème similaire et résolu en déplaçant les conditions de table non civile de la clause WHERE et dans la jointure Voir mon explication complète ici: https://gist.github.com/1137089

Dans votre cas, vous devez prendre en compte le de table1, table2, table3 en jointure gauche sur table1 x = y etc ...