1
votes

Comment convertir une requête SQL avec IN, NOT IN, UNION à l'aide de jointures

Est-il possible de convertir une sous-requête avec les opérateurs NOT IN, IN et UNION pour la rejoindre? J'ai besoin de cette requête en utilisant des jointures sans sous-requêtes pour que spring jpa puisse comprendre que

 SELECT * 
  FROM CONTACT 
  WHERE partner_idpartner = (
     SELECT partner_idpartner 
     FROM BUSINESSCHANNEL WHERE idBusinessChannel in (
       select idbusinessFrom from business WHERE idbusiness = 5943
     )
   )  
   and idcontact not in (
     SELECT CONTACT_IDCONTACT FROM businesscontact 
     WHERE BUSINESS_IDBUSINESS=5943
   )   
  UNION
  SELECT *  
  FROM CONTACT 
  WHERE partner_idpartner = (
    SELECT partner_idpartner 
    FROM BUSINESSCHANNEL WHERE idBusinessChannel in (
      select idbusinessTo FROM business WHERE idbusiness = 5943
    )
  )  
   and idcontact not in (
    SELECT CONTACT_IDCONTACT FROM businesscontact 
    WHERE BUSINESS_IDBUSINESS=5943
  ) 


9 commentaires

Quelle sous-requête, vous avez deux niveaux de sous-requêtes?


@ Joakim Danielson je ne pense pas que je comprends ce que vous demandez, vous ne voyez pas de sous-requêtes?


Oui, bien sûr, mais vous avez des sous-requêtes dans les sous-requêtes, votre question se réfère-t-elle à toutes les sous-requêtes ou seulement aux plus profondes ou ...?


Oui, nous migrons vers un framework qui ne prend pas en charge les sous-requêtes, je dois donc aplatir complètement cette vilaine requête


Votre «nouveau framework» prend-il en charge les CTE (Common Table Expressions)?


@The Impaler oui, mais j'évite de l'utiliser, car je dois prendre en charge plusieurs sources de données


@Joakim Danielson il n'y a pas un seul sélectionne idbusinessDe et deuxième idbusinessTo


Je pense que c'est une bonne question dont la réponse est probablement "ne le faites pas". Je vais voter pour la question car elle est utile à d'autres personnes.


Sur une note latérale: WHERE partner_idpartner = (...) me semble douteux, car la sous-requête semble renvoyer plusieurs lignes, donc elle devrait plutôt être WHERE partner_idpartner IN (...) . La requête entière pourrait facilement être simplifiée en passant en utilisant EXISTS afin de rechercher idbusinessFrom et idbusinessFrom en une seule étape. Nous pourrions ainsi nous débarrasser de la moitié de la requête et elle s'exécuterait probablement plus rapidement aussi.


4 Réponses :


0
votes

Essayez cette requête: je n'ai pas testé

DECLARE @tblNotInIUdContact TABLE (  CONTACT_IDCONTACT INT)
DECLARE @tblIdBusinessFrom TABLE (   IdBusiness INT)
DECLARE @tblIdBusinessTo TABLE (     IdBusiness INT)
DECLARE @tblIdPartner TABLE (    partner_idpartner INT)

INSERT INTO @tblNotInIUdContact(CONTACT_IDCONTACT)
SELECT CONTACT_IDCONTACT FROM businesscontact WHERE BUSINESS_IDBUSINESS=5943

INSERT INTO @tblIdBusinessFrom(IdBusiness)
select idbusinessFrom from business  WHERE idbusiness=5943

INSERT INTO @tblIdBusinessTo(IdBusiness)
select idbusinessTo FROM business    WHERE idbusinessroute=5943

INSERT INTO @tblIdPartner(partner_idpartner)
SELECT partner_idpartner FROM BUSINESSCHANNEL bc
INNER JOIN @tblIdBusinessFrom bf ON bc.partner_idpartner = bf.IdBusiness

INSERT INTO @tblIdPartner(partner_idpartner)
SELECT partner_idpartner FROM BUSINESSCHANNEL bc
INNER JOIN @tblIdBusinessTo bf ON bc.partner_idpartner = bf.IdBusiness



SELECT * FROM CONTACT c
    INNER JOIN @tblIdPartner p ON c.partner_idpartner = p.partner_idpartner  
EXCEPT
SELECT * FROM CONTACT c
    INNER JOIN @tblNotInIUdContact ic ON c.idcontact = ic.CONTACT_IDCONTACT


0 commentaires

2
votes

Essayez ceci:

SELECT    DISTINCT CONTACT.*
FROM      CONTACT
JOIN      BUSINESSCHANNEL
ON        CONTACT.partner_idpartner = BUSINESSCHANNEL.partner_idpartner
JOIN      business
ON        (BUSINESSCHANNEL.idBusinessChannel = business.idbusinessFrom
AND       business.idbusiness = 5943)
OR        (BUSINESSCHANNEL.idBusinessChannel = business.idbusinessTo)
AND       business.idbusinessroute = 5943)
LEFT JOIN businesscontact
ON        CONTACT.idcontact = businesscontact.CONTACT_IDCONTACT 
AND       BUSINESS_IDBUSINESS = 5943
WHERE     businesscontact.CONTACT_IDCONTACT IS NULL


2 commentaires

Bien que je pense que cette requête pourrait vous donner les résultats, sa cardinalité peut être incorrecte. Peut-être que l'ajout d'une clause DISTINCT sur la sélection principale pourrait aider.


Je n'ai même pas pensé à cela, maintenant ma requête est laide: /



2
votes

Vous pouvez convertir toutes ces sous-requêtes en jointures. L'inconvénient est que vous perdez la lisibilité et peut produire un énorme résultat intermédiaire dont vous devez alors vous débarrasser avec DISTINCT . Cela peut être assez coûteux. NOT IN peut être converti en une anti-jointure (jointure externe, puis sélection des lignes non correspondantes), qui est un autre modèle qui peut produire un résultat intermédiaire trop volumineux.

SELECT DISTINCT c.*  
FROM contact c
JOIN businesschannel bc ON bc.partner_idpartner = c.partner_idpartner
JOIN business b ON bc.idbusinesschannel IN (b.idbusinessfrom, b.idbusinessto)
                AND b.idbusiness = 5943 
LEFT JOIN businesscontact bco ON bco.contact_idcontact = c.idcontact
                              AND bco.business_idbusiness = 5943
WHERE bco.contact_idcontact IS NULL;


1 commentaires

Oui, je suis totalement d'accord. La suppression des sous-requêtes peut théoriquement être effectuée, mais je pense que ce n'est pas pratique. Les performances de la requête résultante peuvent être catastrophiques. Même si vous disposez des meilleurs index, la requête peut être très lente.



0
votes

Ici, allez

  1. pour remplacer where in: utilisez la jointure interne
  2. pour remplacer où pas dans: utiliser la jointure externe gauche tab2 où tab2.col est nul
  3. pour remplacer union, Je crée une table temporaire #mycontact avec la même structure que CONTACTER

    sélectionnez * dans mon contact à partir du contact où 1 = 0

    insérer dans #mycontact CHOISIR * DE CONTACT TA

    jointure interne (SELECT partner_idpartner FROM BUSINESSCHANNEL T0 jointure interne (sélectionnez idbusinessFrom de business WHERE idbusiness = 5943) T1 sur T0.idBusinessChannel = T1.idbusinessFrom) jointure externe gauche (SELECT CONTACT_IDCONTACT FROM businesscontact WHERE BUSINESS_IDBUSINESS = 5943) T2 sur T0.idcontact = T2.CONTACT_IDCONTACT où T2.CONTACT_IDCONTACT est nul) TB sur TA.partner_idpartner = TB.partner_idpartner

    insérer dans #mycontact

    SELECT *
    DE CONTACT TC jointure interne (

    TD on TC.partner_idpartner=TD.partner_idpartner
    

    où T2.CONTACT_IDCONTACT est nul)

    (SELECT partner_idpartner FROM BUSINESSCHANNEL  T0
    INNER JOIN (  select idbusinessTo FROM business     WHERE idbusinessroute=5943) T1 on T0.idBusinessChannel=T1.idbusinessFrom)  
    
    left outer join ( SELECT CONTACT_IDCONTACT FROM businesscontact WHERE BUSINESS_IDBUSINESS=5943)  T2
    on T0.idcontact=T2.CONTACT_IDCONTACT
    

    sélectionnez * dans #mycontact


0 commentaires