Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Requêtes PostgreSQL Discussion :

Jointure avec une autre requête


Sujet :

Requêtes PostgreSQL

  1. #1
    Nouveau membre du Club
    Jointure avec une autre requête
    Bonjour à tous,

    Je débute presque totalement en SQL (j'en ai fais un peu à l'école il y a plus de 10 ans ) et classiquement je suis plus VBA, donc ma question est peut être toute bête mais je ne trouve pas la solution.

    Voila j'ai un projet dans lequel j'ai écris une requête assez balèze (enfin pour mon niveau ) qui me donne comme résultat 2 colonnes A et B avec plusieurs A qui peuvent lié à un même B.

    A B
    A1 B1
    A2 B1
    A3 B2
    A4 B1
    A5 B1
    A6 B3
    A7 B2
    A8 B1
    A9 B4

    Ensuite j'ai besoin de faire un autre requête qui me recherche des B et auxquels je voudrait lié les A que j'ai trouvé. J'ai fais une truc du genre :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    select *
    from base_B
    join (select A, B
    		from base_A) rA on B = rA.B


    Donc est-ce que j'ai le droit de faire ça ? Et après dans l'éxécution comment cela se comporte-t-il ? Le select du join est recalculé à chaque "ligne" trouvé par le select * ou est-ce qu'il est calculé une fois au départ et après il se fait une jointure comme cela se passerait avec une autre table ?

    Voilà j'ai essayé de faire simple et clair j'espère que ça le sera.

    Merci d'avance pour vos coups de main.

  2. #2
    Expert éminent sénior
    Le résultat de la requête entre parenthèses est une table (on parle de table dérivée).

    La jointure se comporte comme n'importe quelle autre jointure.
    Par contre, il faut utiliser un alias pour cette table dérivée, comme vous l'avez fait en utilisant "RA" mais aussi pour l'autre table (base_b) par exemple ainsi : on rB.B = rA.B.

    Il est sans doute possible de produire le résultat final en une seule requête, pour ça, il faut que vous communiquiez la description de toutes les tables utilisées et un extrait du contenu.

    Note : évitez les select *, n'utilisez que les colonnes utiles pour le traitement

  3. #3
    Nouveau membre du Club
    Bonjour,

    Merci pour la réponse.

    Donc en fait oui je ne met de select * là c'était pour l'exemple, pareil pour le rB c'est un oubli.

    Par contre je viens de comprendre ce qui me pose problème, c'est qu'en fait dans ma table dérivée le fait d'avoir plusieurs A pour un même B la requette va me sortir une ligne par B. Et vu la quantité de données que je gère ca mouline pas mal. J'ai encore du mal avec la logique SQL .

  4. #4
    Expert éminent sénior
    Pouvez-vous communiquer la première requête, celle qui produit le premier "tableau", encore une fois, il est certainement possible de produire le résultat final à partir d'une seule requête

  5. #5
    Nouveau membre du Club
    Bonjour,

    Je veux bien mais pas sur que vous y compreniez grand chose sans toutes les tables.

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    select b.id_metier_ as pb, b.pa, si.PB, si.PA_from_SIREO,z.id_metier_ as pa
    from ftth_pf b
    join (select fi.id_metier_, si.code_imb, z.id_metier_ as PB, si.id_pa as PA_from_SIREO, f1.id_regleme as PA_PB
    		from ftth_immeuble fi
    		full join sireo_immeuble si on si.code_imb = fi.id_metier_
    		full join ftth_zone_eligibilite z on st_intersects(z.geom,fi.geom)
    		inner join ftth_pf f on f.id_metier_ = z.id_metier_ 
    		inner join ftth_pf f1 on f1.id_metier_ = f.pa
    		where (si.code_plaque like '%1017' or si.code_plaque like '%1117') and (z.id_metier_ like '%PB%' or z.id_metier_ like '%Autre%')) si on pb = si.PB
    inner join ftth_zone_eligibilite z on st_intersects(z.geom,b.geom)
    where (b.id_metier_ like '%PB%' or b.id_metier_ like '%Autre%') and z.id_metier_ like '%PA%'

  6. #6
    Expert éminent sénior
    Beaucoup de choses à dire sur cette requête

    2 full outer join c'est suspect, mais admettons, par contre le fait de les coder ainsi en combinaison avec des jointures inner est dangereux.
    En cas de combinaisons de jointures inner et outer, il faut utiliser la syntaxe suivante
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT [...]
    FROM T1
    (FULL/LEFT/RIGTH) OUTER JOIN T2
        INNER JOIN T3   
           ON critères de jointure T2/T3
        ON critères de joiture T2/T1


    De plus, il y a des restrictions sur des tables OUTER, de ce fait, les jointures deviennent implicitement des jointures INNER.

    Enfin, les critères de restriction ne sont pas sargables (début des arguments du like %) ==> aucun index n'est éligible

  7. #7
    Nouveau membre du Club
    Beaucoup de choses que je ne comprends pas dans ce que vous dites, je débute vraiment en sql .

    Pourquoi c'est dangereux la manière dont j'ai voulu faire ?
    Et votre syntaxe ca fait quoi exactement ?

    Et sargeables aussi j'ai pas compris .

    En gros je vais essayé de vous décrire ce que j'ai voulu faire.

    En gros j'ai une carte qui contient des imeubles, des poteaux principaux pb et des poteaux pères pa. J'ai actuellement 2 méthodes pour déterminer le poteau père d'un poteau principal. Soit je regarde géométriquement dans quelle zone d'éligibité des poteau père se trouve mon poteau principale soit je regarde les immeubles associés à mon poteau principal car dans ceux-ci j'ai l'information du poteau père et pour cela je repasse par une géométrie car dans les immeubles j'ai l'info du poteau père mais pas du poteau principal (oui c'est un peu étrange).

    Du coup moi je voulais comparer les 2 méthodes et ressortir tous les écarts.

    Le premier select a donc pour objectif de rechercher le poteau père en passant par les informations des immeubles :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    select fi.id_metier_, si.code_imb, z.id_metier_ as PB, si.id_pa as PA_from_SIREO, f1.id_regleme as PA_PB
    		from ftth_immeuble fi
    		full join sireo_immeuble si on si.code_imb = fi.id_metier_

    Au départ je veux "fusionner" mes 2 tables contenant des immeubles ftth_immeuble et sireo_immeuble en fonction de leurs identifiant (id_metier_ pour la première code_imb pour la deuxième) d'ou le full join.
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    		full join ftth_zone_eligibilite z on st_intersects(z.geom,fi.geom)

    Ensuite je veux prendre cette liste nouvellement crée et la joindre de manière géométrique pour associer une zone géométrique (zone d'eligibilité dans mon cas) à chaque immeuble. Sachant qu'en fait les immeubles provenant de sireo_immeuble n'ont pas de coordonnées compatible avec cette jointure.
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    		inner join ftth_pf f on f.id_metier_ = z.id_metier_

    Ensuite grace à lzone d'eligibilité je peux récupérer le nom du poteau principal de cette zone. Je joint donc avec la table ftth_pf pour récupérer les autres infos liées à ce poteau.
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    		inner join ftth_pf f1 on f1.id_metier_ = f.pa

    Ce qui me permet de trouver le poteau père (pa ici) et de récupérer également ses infos détaillées
    where (si.code_plaque like '%1017' or si.code_plaque like '%1117') and (z.id_metier_ like '%PB%' or z.id_metier_ like '%Autre%')
    Et là je fais juste quelques filtres, qui je l'ai découvert bien plus tard vont en fait faire mes full join servent plus a grand chose.

    Bref à la sortie je me retouve avec une table comme ça :
    id_metier_ code_imb pb pa_from_sireo pa_pb
    id de l'immeuble dans ma table ftth_immeuble id de l'immeuble dans ma table sireo_immeuble poteau principale de la zone de l'immeuble poteau père calculer par la requète poteau père inscrit dans les infos du poteau principale

    Ensuite le deuxième select a pour objectif de lister tous les poteaux principaux de mon projet et de comparer les 2 méthodes.
    Pour cela je fais donc un join avec mon premier select (méthode des immeubles)
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    inner join ftth_zone_eligibilite z on st_intersects(z.geom,b.geom)

    Et j'applique la méthode des zones


    Voilà je ne sais pas si c'est très clair.

  8. #8
    Expert éminent sénior
    Citation Envoyé par Coco47 Voir le message
    Pourquoi c'est dangereux la manière dont j'ai voulu faire ?
    Parce que les jointures OUTER combinées avec des jointures INNER, peuvent être traitées comme des jointures INNER si le prédicat de jointure est mal placé
    il suffit de se créer un petit jeu d'essai dans 3 tables et d'examiner le résultat des deux syntaxes pour s'en convaincre.



    Citation Envoyé par Coco47 Voir le message
    Et votre syntaxe ca fait quoi exactement ?
    Ca permet justement d'éviter le problème dont je parle juste au dessus



    Citation Envoyé par Coco47 Voir le message
    Et sargeables aussi j'ai pas compris .
    SArgAble est un acronyme signifiant "Search Argument Able" autrement dit : capable d'utiliser un index
    Pour qu'une opération soit sargable, il faut bien sûr qu'un index existe (merci lapalisse ) mais aussi qu'il soit utilisable.
    Or rechercher toutes les chaînes de caractères dont on ne fournit pas le début (puis que le caractère % est mis en première position) revient à consulter séquentiellement toute la table. Aucun index n'est éligible. Le predicat WHERE colonne like '%chaîne' n'est pas sargable.
    Par analogie : comment voulez vous rechercher dans un annuaire si vous ne connaissez même pas l'initiale du nom à trouver... vous serez bien contraint de parcourir tout l'annuaire.
    Il existe des index particuliers pour ce genre de besoin, mais c'est une autre histoire.

  9. #9
    Expert éminent sénior
    Je viens de créer une entrée dans mon blog pour expliquer le phénomène des jointures INNER/OUTER combinées, ce type de question revenant assez souvent

    à consulter et expérimenter ICI

  10. #10
    Membre extrêmement actif
    @escartefigue) Merci beaucoup
    Au nom du pèze, du fisc et du St Estephe
    Au nom du fric, on baisse son froc...

  11. #11
    Nouveau membre du Club
    Super c'est top j'ai bien compris.

    J'aurais une question supplémentaire.

    Dans le cas ou je voudrais faire un where sur les résultats de ma jointure INNER comment ca s'écrirait avec votre syntaxe.

    Par exemple il m'arrive de faire ça :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    SELECT [...]
    FROM T1
    (FULL/LEFT/RIGTH) OUTER JOIN T2
        INNER JOIN T3   
           ON critères de jointure T3/T2
        ON critères de jointure T2/T1
    WHERE T3.A = "A"


    Et du coup j'imagine que toutes les lignes ou il y a 'null' disparaissent et l'intéret de la jointure OUTER a disparu, c'est bien ca ?
    Là pour l'instant je met un truc du genre :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    WHERE T3.A = "A" OR T3.A = 'null'

    Mais ca ne me parait super optimiser.

  12. #12
    Expert éminent sénior
    Une restriction WHERE sur une jointure interne ne pose aucun problème
    C'est sur une jointure externe que l'ajout d'une restriction implique que la jointure devient interne (sauf si la restriction est de type WHERE Tx.ColX is null)

    En effet, vérifier where Tx.Colx='A' implique d'avoir trouvé la ligne dans la table Tx...

    Dans ce cas, il faut déporter la restriction sur la clause de jointure ON... pour conserver une jointure externe.

    Pour le cas ci-dessus, T3 est jointe à T2 elle même jointe à T1 en jointure externe. Du coup, poser une restriction sur T3 crée une jointure interne implicite !

  13. #13
    Nouveau membre du Club
    Ah ok je vois super merci.

  14. #14
    Membre actif
    Pourquoi ne pas décomposer votre problème en deux ou trois requêtes et regrouper les résultats avec un UNION.

    Je ne suis pas sûr que votre requete reponde à vos besoins, au vue de toutes ces jointures.
    Je constate aussi que vos jointures sont essentiellement appliquées sur des colonnes de type texte, en espérant que vous
    A quoi sert cette jointure ? :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    inner join ftth_pf f1 on f1.id_metier_ = f.pa


    Juste une piste pour reformuler votre requête.
    En utilisant les CTE (WITH), on arrive à regrouper les sous requetes de manière plus concise.

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    WITH
    	immeubles as (
    			SELECT i.geom, a.code_plaque, a.id_pa as sireo_pa
    			FROM ftth_immeuble i
    			FULL JOIN sireo_immeuble a on a.code_imb = i.id_metier_
    		),
     
    	zones as (
    			SELECT z.id_metier_ as zone_pb, z.geom
    			FROM ftth_zone_eligibilite z
    			INNER JOIN ftth_pf f on f.id_metier_ = z.id_metier_ 
    		),
     
    	poteaux as (
    			SELECT p.id_metier_ as poteau_pb, p.pa poteau_pa, z.id_metier_ as zone_pa
    			FROM ftth_pf p
    			INNER JOIN ftth_zone_eligibilite z on st_intersects( z.geom, p.geom )
    		)
     
    SELECT p.poteau_pb, p.poteau_pa, z.zone_pb, i.sireo_pa, p.zone_pa
     
    FROM poteaux p
    	INNER JOIN zones z ON z.zone_pb = p.poteau_pb
    	FULL JOIN immeubles i ON st_intersects( i.geom, z.geom )
     
    WHERE z.zone_pb ~* '.*PA.*' AND
    	  p.poteau_pb ~* '.*PB.*|.*Autre.*' AND
    	  i.code_plaque ~* '.*1017|.*1117';

###raw>template_hook.ano_emploi###