critères de jointure sql left dans join vs clause clause

J'ai une requête existante pour sélectionner certains paiements

Je souhaite filterr les paiements destinés aux clients ayant une alerte active dans une autre table appelée ClientAlert

J'ai donc pensé que je ferais une jointure à gauche et que je verifierais si le ClientAlertId est nul.

select * from payments p left join client c on c.clientid = p.clientid left join ClientAlert ca on ca.CRMId = c.CRMId and ca.ClientAlertSubjectId = 1 and ca.IsActive = 1 and (ca.ExpiryDate is null or ca.ExpiryDate > GetDate()) where ca.clientalertid is null and p.PaymentStatusId = 2 and p.PaymentDate <= GetDate() and p.PaymentCategoryId = 1 

Cela semble fonctionner je pense

Mais j'ai deux questions:

  1. Pourrait-il y avoir un scénario qui entraînerait le renvoi de plusieurs paiements au lieu d'un en ajoutant cette jointure?

  2. quand j'ai spécifié ce qui suit dans la clause where au lieu de la jointure, il n'a pas donné les mêmes résultats et je ne comprends pas pourquoi

    et ca.ClientAlertSubjectId = 1 et ca.IsActive = 1 et (ca.ExpiryDate est null ou ExpiryDate> GetDate ())

Je pensais avoir ce critère dans la clause où serait équivalent à l'avoir dans la jointure

  1. S'ils peuvent avoir plusieurs alertes, théoriquement. Cependant, puisque vous excluez les paiements avec alertes, cela ne devrait pas poser de problème. Si vous les incluez, ça pourrait l'être. Si cela a été un problème, vous devez utiliser une sous-requête "not in" au lieu de la jointure externe gauche car cela peut entraîner des loggings en double si ce n'est pas 1: 1.

  2. Avoir des critères dans la clause where exclut la ligne entière si elle ne correspond pas aux critères. L'avoir dans la clause join signifie que l'logging joint n'est pas affiché mais que le "parent" l'est.

  1. Vous pouvez get des multiples par logging de paiement s'il est lié à plusieurs loggings Client. Basé sur la clause WHERE cependant, je ne vois pas comment plusieurs loggings de ClientAlert pourraient causer la duplication.
  2. LEFT JOIN loggings LEFT JOIN renvoient des valeurs NULL sur toutes leurs colonnes lorsqu'il n'y a pas de correspondance. L'ajout de ca.ClientAlertSubjectId = 1 and ca.IsActive = 1 à la clause WHERE oblige fondamentalement cette jointure à se comporter comme une INNER JOIN car il DEVRAIT find un logging correspondant, mais je suppose qu'il ne returnnerait jamais de données car ClientAlertId est un colonne non nullable. Donc, fondamentalement, vous avez créé une requête où vous avez besoin d'une ligne NULL (indiquant qu'il n'y a pas d'alertes), mais la ligne doit contenir des données.
 select * from payments p left join client c on c.clientid = p.clientid left join ClientAlert ca on ca.CRMId = c.CRMId and ca.ClientAlertSubjectId = 1 and ca.IsActive = 1 and (ca.ExpiryDate is null or ca.ExpiryDate > GetDate()) where ca.CRMId is null and p.PaymentStatusId = 2 and p.PaymentDate <= GetDate() and p.PaymentCategoryId = 1 
  1. Ce petit changement vous assurera que vous n'obtiendrez jamais de duplicates SI clientid est unique dans le client de table

  2. Les conditions de déplacement de la jointure gauche à l'endroit où vous déplacez la condition de jointure gauche dans une condition pour la ligne. Donc, si la condition n'est pas remplie, la ligne n'est pas returnnée. La condition de jointure gauche inclura toujours la ligne du côté gauche de la jointure

Premièrement, à less qu'il n'y ait des paiements qui ne sont pas associés à un client, la première jointure devrait être une jointure interne.

Deuxièmement, une fois que le client a l'alerte appropriée, vous filterz tous les paiements effectués par ce client, même ceux effectués des mois ou des années avant que l'alerte ne prenne effet. Est-ce que c'est ce que tu veux?

Troisième et dernier (bien que ce soit long): une jointure externe a ce format:

 select ... from InnerTable i left [outer] join OuterTable o on <join criteria> where <filter criteria>; 

Pour les jointures externes uniquement, il est important que toutes les vérifications impliquant la table externe soient considérées comme des critères de jointure. À un minimum d'ours, il y aura

  on o.JoinField = i.OtherJoinField 

ou dans votre cas

  on ca.CRMId = c.CRMId 

Pour conserver la sortie attendue d'une jointure externe, les vérifications des autres champs de la table externe doivent également figurer dans la clause ON en tant que critères de jointure supplémentaires:

  on ca.CRMId = c.CRMId and ca.ClientAlertSubjectId = 1 and ca.IsActive = 1 and (ca.ExpiryDate is null or ca.ExpiryDate > GetDate()) 

L'ajout de ces vérifications d'addition à la clause WHERE, comme vous l'avez découvert, filter complètement tous les avantages de la jointure externe pour fournir le même jeu de résultats qu'une jointure interne.

Cependant, vous pouvez get la sortie pour returnner au jeu de résultats de jointure externe en faisant un petit changement:

 where (ca.clientalertid is null or (ca.ClientAlertSubjectId = 1 and ca.IsActive = 1 and (ca.ExpiryDate is null or ca.ExpiryDate > GetDate())) and p.PaymentStatusId = 2 and ... 

Mais cela ne revient pas tout à fait à un jeu de résultats de jointure externe normal. Dans votre cas, la jointure externe régulière affichera datatables de paiement et de client avec des valeurs NULL pour datatables d'alerte pour:

  1. Paiements par les clients sans aucune alerte.
  2. Paiements par les clients avec des alertes mais pas entièrement qualifiés par les critères supplémentaires. Par exemple, si IsActive contient 0.

Ensuite, il y aurait des données d'alerte pour ces paiements par les clients qui ont une alerte complète.

Le déplacement des critères supplémentaires vers la clause WHERE mais dans le format indiqué ci-dessus affichera datatables de paiement et de client avec des valeurs NULL pour datatables d'alerte uniquement pour les paiements sans alertes, ainsi que datatables d'alerte pour les alertes complètes. Les paiements et les clients avec des alertes pas entièrement qualifiés ne s'afficheraient pas du tout.

Rappelez-vous cela. Il peut y avoir des moments où c'est exactement ce que vous voulez.

Mais cela ne semble pas être ce que vous voulez en ce moment. Si vous ne souhaitez pas voir d'alertes et d'alertes partielles mais filterr les alertes entièrement qualifiées, vous ne pouvez pas placer les autres critères dans la clause WHERE. Votre requête d'origine est le seul moyen de le faire.

Il n'y a vraiment aucune raison de placer les critères supplémentaires dans la clause WHERE. Ça ne t'apporte rien. Il n'y a aucun avantage de performance à en tirer, même pour la jointure interne où cela n'affecte pas la sortie. Comparez le plan d'exécution pour les jointures internes avec les critères supplémentaires dans la clause ON et dans la clause WHERE. Ils sont identiques. La même comparaison pour les jointures externes sont différentes. En fait, avec les critères d'ajout dans la clause WHERE, le plan d'exécution est identique à ceux des jointures internes.

Il suffit donc d'utiliser la requête que vous avez et soyez satisfait que vous obtenez la sortie que vous voulez.