IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
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

Langage SQL Discussion :

inner join, outer join et parenthèses


Sujet :

Langage SQL

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Expert confirmé
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 197
    Billets dans le blog
    1
    Par défaut inner join, outer join et parenthèses
    Bonjour,

    Lorsque j'ai des requêtes un peu complexes combinant des INNER JOIN et OUTER JOIN en cascade, il m'arrive d'utiliser des parenthèses afin non pas de bordéliser le code, mais pour hiérarchiser les jointures.

    Il y a peu, une personne avait posté sur ce forum une requête (mal écrite, certes) contenant des parenthèses dans le FROM, et SQLPro, de mémoire, en qui j'ai toute confiance, avait répondu qu'on ne devait jamais mettre de parenthèses dans un FROM, que c'était inutile. J'avoue que ça m'a étonné.

    Mais je fus encore plus perplexe quelques jours plus tard lorsque j'ai écrit une requête vraiment complexe, et que les parenthèses se sont mises à déconner !

    Voici un exemple simple de ce que je fais habituellement :

    Nous avons des personnes, qui travaillent (ou non) dans un service, et chaque service à une adresse.
    => On veut afficher la liste du personnel, du service et son adresse si applicable

    adr (id, ville)
    srv (id, nom, a_id fk adr(id))
    prs (id, nom, s_id fk srv(id))

    J'écris donc :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    select p.id, p.nom, s.nom, a.ville
    from prs p
    left outer join (
       srv s
       inner join adr a on a.id = srv.a_id
    ) on s.id = p.s_id
    => Avec SQL Server 2014 Express, tout du moins, ça marche très bien, et je ne vois pas comment on peut écrire rigoureusement la même chose sans passer par une sous-requête (ou CTE). En effet, des outer join en cascade ne ramèneront pas le même résultat si des services n'ont pas d'adresse.

    Pourtant, l'autre jour, je me suis retrouvé avec une requête qui s'est mise à déconner, et qui a décidé de me retourner le résultat d'un inner join au lieu d'un outer join.
    Je n'arrive toujours pas à comprendre.

    J'avais quelque chose de plus complexe :
    prs (id, nom, s_id fk srv(id), a_id fk adr(id))
    => Une personne travaille dans un service, mais pas forcément à l'adresse du service. Et on veut récupérer uniquement les info des services pour lesquelles la personne travaille à la même adresse que son service.

    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    select p.id, p.nom, s.nom, a.ville
    from prs p
    left outer join (
       srv s
       inner join adr a on a.id = srv.a_id
    ) on s.id = p.s_id and a.id = p.a_id
    => J'ai beau tortiller le truc dans tous les sens, j'ai quand même le résultat attendu...

    Est-ce que vous avez des cas en tête où ce genre de syntaxe ne fonctionne pas ?

  2. #2
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    22 010
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Expert bases de données / SQL / MS SQL Server / Postgresql
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 22 010
    Billets dans le blog
    6
    Par défaut
    Je n'ai jamais dit qu'il ne fallait pas mettre de parenthèses... J'ai dit qu'il ne fallait pas mettre des parenthèses inutiles !
    Elles peuvent en effet servir, très rarement, et je le démontre dans mon cours Orsys sur les requêtes avancées (de tête référence PAV... http://www.orsys.fr/formation-sql-se...ql-avancee.asp)

    En effet, une solution à la sous requête est dans certains cas le parenthésage hierarchique....

    A +
    Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
    Le site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
    Blog SQL, SQL Server, SGBDR : http://blog.developpez.com/sqlpro
    Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
    Entreprise SQL SPOT : modélisation, conseils, audit, optimisation, formation...
    * * * * * Expertise SQL Server : http://mssqlserver.fr/ * * * * *

  3. #3
    Modérateur

    Profil pro
    dba
    Inscrit en
    Janvier 2010
    Messages
    5 643
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : dba

    Informations forums :
    Inscription : Janvier 2010
    Messages : 5 643
    Par défaut
    Bonjour,

    Je ne comprend pas bien le fond de la question.

    Est-ce qu'il s'agit de l'utilité des parenthéses, on de leur fonctionnement .

    Pour l'utilité, je ne vois pour ma part en effet pas de cas où elle sont techniquement indispensables. D'ailleurs dans les deux requêtes que vous présentez, vous pouvez simplement les supprimer, vous obtiendrez le même résultat.
    Dans des requêtes plus complexes avec de multiples imbrications, elles peuvent toutefois aider a mieux comprendre la logique de la requete (au même titre que certaines parenthèses "inutile" dans une clause WHERE mêlant dse AND et des OR ).




    Citation Envoyé par StringBuilder Voir le message
    Pourtant, l'autre jour, je me suis retrouvé avec une requête qui s'est mise à déconner, et qui a décidé de me retourner le résultat d'un inner join au lieu d'un outer join.
    Je n'arrive toujours pas à comprendre.

    J'avais quelque chose de plus complexe :
    [...]
    => J'ai beau tortiller le truc dans tous les sens, j'ai quand même le résultat attendu...
    ??? ça fonctionne, ou ça ne fonctionne pas ?

    Qu'a fait la requete qui s'est "mise à déconner" ?
    Sans doute une clause dans le WHERE qui aurait dû être placée dans la jointure externe.

  4. #4
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    22 010
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Expert bases de données / SQL / MS SQL Server / Postgresql
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 22 010
    Billets dans le blog
    6
    Par défaut
    Pour conclure, voici un exemple que je donne en cours, sur l'utilité du parenthèsage...

    Avec les tables suivantes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    CREATE TABLE T_PERSONNE_PHYSIQUE_PSP
    (  PRS_ID              INT         NOT NULL PRIMARY KEY,
       PSP_NOM             CHAR(36)    NOT NULL,
       PSP_PRENOM          VARCHAR(25)     NULL,
       PSP_DATE_NAISSANCE  DATE            NULL);
    CREATE TABLE T_R_TYPE_TELEPHONE_TTL
    (  TTL_ID              INT         NOT NULL PRIMARY KEY,
       TTL_CODE            CHAR(8)     NOT NULL,
       TTL_LIBELLE         VARCHAR(32) NOT NULL);
    CREATE TABLE T_TELEPHONE_TEL
    (  TEL_ID              INT         NOT NULL PRIMARY KEY,
       PRS_ID              INT             NULL REFERENCES T_PERSONNE_PHYSIQUE_PSP (PRS_ID),
       TTL_ID              INT             NULL REFERENCES T_R_TYPE_TELEPHONE_TTL (TTL_ID),
       TEL_NUMERO          CHAR(20)    NOT NULL);
    Et les données suivantes :
    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    INSERT INTO T_PERSONNE_PHYSIQUE_PSP VALUES
    (1, 'DUPONT', 'Alain', '1962-10-27'), 
    (2, 'MARTIN', 'Marc', '1977-12-25'), 
    (3, 'BOUVIER', 'Alain', '1960-12-05'), 
    (4, 'DUBOIS', 'Paul', '1960-04-30'), 
    (5, 'DREYFUS', 'Jean', '1961-02-03'), 
    (6, 'FAURE', 'Alain', '1974-08-19'), 
    (7, 'LACOMBE', 'Paul', '1934-12-13'), 
    (8, 'DUHAMEL', 'Evelyne', '1980-11-21'), 
    (9, 'BOYER', 'Martine', '1961-06-03'), 
    (10, 'MARTIN', 'Martin', '1978-08-22'), 
    (11, 'PAUL', 'Marcel', '1982-01-29'), 
    (12, 'DUVAL', 'Arsène', NULL), 
    (13, 'PHILIPPE', 'André', '1967-11-05'), 
    (14, 'PIERRELAYE', 'Paul', '1983-01-03'), 
    (15, 'DAUMIER', 'Amélie', '1975-05-16'), 
    (16, 'CHABAUD', 'Daniel', '1928-11-30'), 
    (17, 'BAILLY', 'Jean-François', '1982-07-28'), 
    (18, 'FAYOLLE', 'Olivier', '1981-09-17'), 
    (19, 'COCINO', 'Gérard', '1981-01-17'), 
    (20, 'FRANQUINET', 'Florent', '1978-07-09'), 
    (21, 'MALATERRE', 'Arnaud', '1983-12-23'), 
    (22, 'MEDARD', 'Jacques', '1975-12-12'), 
    (23, 'AUZENAT', 'Michel', '1977-02-21'), 
    (24, 'CHTCHEPINE', 'Dominique', '1980-11-28'), 
    (25, 'LE GUILLARD', 'Alain', '1983-11-29'), 
    (26, 'GARREAU', 'Paul', '1976-04-10'), 
    (27, 'LECUYER', 'Lionel', '1987-10-05'), 
    (28, 'PRA-LETTRY', 'Emmanuel', '1984-07-20'), 
    (29, 'SILLET', 'Jacques', '1969-07-21'), 
    (30, 'TROLLAT', 'Hervé', '1985-08-28'), 
    (31, 'BOUCHET', 'Michel', '1979-02-21'), 
    (32, 'LEBAILLIF', 'Christian', '1982-11-11'), 
    (33, 'DU HAUT CILLY', 'Guy', '1938-06-03'), 
    (34, 'GALLACIER', 'Noëlle', '1938-10-16'), 
    (35, 'PICOT', 'Dominique', '1980-05-30'), 
    (36, 'BEAUNEE', 'Pierre', '1977-10-25'), 
    (37, 'VERNET', 'Daniel', '1966-06-17'), 
    (38, 'ALBERT', 'Christian', '1985-05-16'), 
    (39, 'HESS', 'Lucette', '1962-04-06'), 
    (40, 'CHATON', 'Gérard', '1933-08-15'), 
    (41, 'PLATONOFF', 'Philippe', '1987-04-13'), 
    (42, 'LETERRIER', 'Monique', '1972-03-30'), 
    (43, 'MONTEIL', 'Jean', '1973-01-17'), 
    (44, 'SPITHAKIS', 'Jean-Paul', '1977-06-19'), 
    (45, 'ORELL', 'Olivier', '1972-02-05'), 
    (46, 'MARTINET', 'Carmen', '1980-08-27'), 
    (47, 'RAY', 'Yannick', NULL), 
    (48, 'TARSAC', 'René', '1933-07-23'), 
    (49, 'COULOMB', 'Renaud', '1988-11-19'), 
    (50, 'REINIER', 'Colette', '1967-02-18'), 
    (51, 'SAVY', 'Jean-Claude', '1977-08-24'), 
    (52, 'DAVID', 'Jacqueline', '1977-09-23'), 
    (53, 'FORGEOT', 'Jean-Bernard', '1965-06-06'), 
    (54, 'BERGER', 'Jean-Pierre', '1965-02-12'), 
    (55, 'DOUBLET', 'Thierry', '1971-09-09'), 
    (56, 'MATHIEU', 'Gérard', '1977-07-05'), 
    (57, 'MOURGUES', 'Jacqueline', '1965-05-13'), 
    (58, 'PIERROT', 'Robert', '1965-11-03'), 
    (59, 'FRANQUEBALME', 'Daniel', '1989-01-17'), 
    (60, 'ZAMPIERO', 'Annick', '1979-01-25'), 
    (61, 'PASCOT', 'Vincent', '1933-01-02'), 
    (62, 'MECHRI', 'Pierre', '1982-12-01'), 
    (63, 'THIERY', 'Fathy', '1966-04-02'), 
    (64, 'ROURE', 'Marie-Louise', '1965-12-09'), 
    (65, 'VILLE', 'Jean-Paul', '1972-02-12'), 
    (66, 'NOCENTINI', 'Alain', '1984-05-11'), 
    (67, 'LAYANI', 'Lionel', '1989-01-08'), 
    (68, 'RECHUL', 'Jacques', '1928-02-16'), 
    (69, 'DE CONINCK', 'Patricia', '1984-03-18'), 
    (70, 'LEI', 'Alain', '1972-07-11'), 
    (71, 'MICHEL', 'Fernand', '1989-10-01'), 
    (72, 'BOURA', 'André', '1980-12-29'), 
    (73, 'CARDONA', 'Philippe', '1937-07-24'), 
    (74, 'THOMASSE', 'Jean-Claude', '1978-12-27'), 
    (75, 'MOURIES', 'Nathalie', '1966-11-04'), 
    (76, 'MARTIN', 'Jean-Pierre', '1984-01-23'), 
    (77, 'BENZAQUI', 'Joël', '1981-03-08'), 
    (78, 'ROUSSILLON', 'Alain', '1978-02-03'), 
    (79, 'FARGETTON', 'Denis', '1983-05-14'), 
    (80, 'LEPERCQ', 'Jean-Claude', '1929-02-11'), 
    (81, 'OLIVIA', 'Hubert', '1987-10-30'), 
    (82, 'CASTAREDE', 'Jean-Jacques', '1935-05-30'), 
    (83, 'LEOTARD', 'Jean-Paul', '1989-01-25'), 
    (84, 'LALANDE', 'Colette', '1989-06-23'), 
    (85, 'BAVEREL', 'Frédéric', '1988-11-02'), 
    (86, 'NOEL', 'Régis', '1976-03-08'), 
    (87, 'THIRIOT', 'Jacky', '1969-03-01'), 
    (88, 'BERTRAND', 'Christophe', '1984-02-08'), 
    (89, 'LAVAL', 'Marcel', '1988-06-26'), 
    (90, 'BACQUE', 'Michel', '1934-01-18'), 
    (91, 'COUASSE', 'François', '1934-02-17'), 
    (92, 'JOLY', 'Christophe', '1975-04-11'), 
    (93, 'BENATTAR', 'Pierre', '1984-07-07'), 
    (94, 'PARIS', 'Michèle', '1982-10-20'), 
    (95, 'LEAL', 'Jany', '1988-01-14'), 
    (96, 'BENATTAR', 'Bernard', '1984-10-05'), 
    (97, 'AIACH', 'Alexandre', '1935-07-26'), 
    (98, 'GAL', 'Fabrice', '1989-03-23'), 
    (99, 'CHAMBON', 'Edith', '1938-01-27'), 
    (100, 'DUQUESNAY', 'Jacques', '1983-01-27'), 
    (101, 'CHEVALLIER-CHANTEPIE', 'Yanis', '1947-01-12'), 
    (102, 'JEAN', 'Henri', '1983-03-28'), 
    (103, 'DUFOUR', 'Alban', NULL), 
    (104, 'MARTIN', 'Marc', '1952-11-21'), 
    (105, 'BORDIER', 'Alain', '1949-03-17'), 
    (106, 'DUFOUR', 'Paule', '1976-05-03'), 
    (107, 'DE LA PATELIÈRE', 'Alexandre', '1969-07-14'), 
    (108, 'WISKAIA', 'Janicek', '1970-12-31'), 
    (109, 'ZOLTAN', 'Dragomir', '1957-06-10');
     
    INSERT INTO T_R_TYPE_TELEPHONE_TTL VALUES
    (1, 'TEL', 'Téléphone filaire'), 
    (2, 'FAX', 'Télécopie'), 
    (3, 'GSM', 'Téléphone mobile');
     
    INSERT INTO T_TELEPHONE_TEL VALUES 
    (1, 1, 1, '01-45-42-56-63'), 
    (2, 1, 2, '01-44-28-52-50'), 
    (3, 2, 1, '01-47-66-29-29'), 
    (4, 2, 2, '01-47-66-29-55'), 
    (5, 3, 1, '04-94-41-17-27'), 
    (6, 3, 3, '06-11-86-78-89'), 
    (7, 4, 1, '02-41-58-89-52'), 
    (8, 4, 2, '04-66-62-95-64'), 
    (9, 5, 1, '01-51-58-52-50'), 
    (10, 5, 2, '04-92-19-18-58'), 
    (11, 6, 1, '01-45-35-19-19'), 
    (12, 7, 1, '01-48-79-87-99'), 
    (13, 8, 1, '01-54-11-43-21'), 
    (14, 8, 2, '01-54-11-43-89'), 
    (15, 9, 1, '01-39-88-90-39'), 
    (16, 9, 3, '06-55-41-42-95'), 
    (17, 10, 1, '01-48-98-92-21'), 
    (18, 10, 2, '01-44-22-56-21'), 
    (19, 11, 1, '01-58-98-52-56'), 
    (20, 12, 1, '01-48-04-54-24'), 
    (21, 12, 3, '06-11-89-58-54'), 
    (22, 13, 1, '01-44-85-87-87'), 
    (23, 13, 2, '01-48-44-86-19'), 
    (24, 14, 1, '01-44-05-00-05'), 
    (25, 15, 1, '01-48-28-63-46'), 
    (26, 15, 2, '01-48-28-17-95'), 
    (27, 16, 1, '01-46-57-00-00'), 
    (28, 16, 2, '01-46-57-74-74'), 
    (29, 17, 1, '01-46-57-00-00'), 
    (30, 18, 1, '01-42-37-34-62'), 
    (31, 19, 1, '04-93-88-15-15'), 
    (32, 20, 1, '01-64-54-98-97'), 
    (33, 20, 2, '01-64-54-96-11'), 
    (34, 21, 1, '04-90-82-12-26'), 
    (35, 21, 3, '06-90-85-59-16'), 
    (36, 22, 1, '05-96-63-34-30'), 
    (37, 23, 1, '04-68-51-86-09'), 
    (38, 24, 1, '04-66-67-05-00'), 
    (39, 24, 2, '04-66-67-28-23'), 
    (40, 25, 1, '01-42-77-48-00'), 
    (41, 25, 2, '01-42-77-35-92'), 
    (42, 26, 1, '01-34-66-62-82'), 
    (43, 26, 2, '01-34-66-60-41'), 
    (44, 27, 1, '04-76-63-83-83'), 
    (45, 28, 1, '04-66-53-17-81'), 
    (46, 29, 1, '02-96-70-50-74'), 
    (47, 29, 3, '06-96-70-30-93'), 
    (48, 30, 1, '01-42-89-25-25'), 
    (49, 31, 1, '01-69-21-57-68'), 
    (50, 31, 2, '01-69-21-36-70'), 
    (51, 32, 1, '01-69-21-57-68'), 
    (52, 32, 2, '01-69-21-36-70'), 
    (53, 33, 1, '04-72-71-16-39'), 
    (54, 34, 1, '04-94-38-75-44'), 
    (55, 35, 1, '04-67-04-07-30'), 
    (56, 36, 1, '01-43-02-40-44'), 
    (57, 36, 3, '06-43-81-26-62'), 
    (58, 37, 1, '01-44-24-11-40'), 
    (59, 38, 1, '04-76-70-07-58'), 
    (60, 38, 3, '06-76-70-01-05'), 
    (61, 39, 1, '03-21-81-71-67'), 
    (62, 39, 2, '03-21-81-47-80'), 
    (63, 40, 1, '05-49-88-33-93'), 
    (64, 40, 2, '05-49-41-35-37'), 
    (65, 41, 1, '01-44-54-54-74'), 
    (66, 42, 1, '04-93-75-76-60'), 
    (67, 43, 1, '04-94-68-07-66'), 
    (68, 44, 1, '04-94-56-44-27'), 
    (69, 44, 3, '06-94-56-02-95'), 
    (70, 45, 1, '01-43-21-29-39'), 
    (71, 45, 3, '06-43-27-97-59'), 
    (72, 46, 1, '04-94-96-44-88'), 
    (73, 47, 1, '03-26-82-61-61'), 
    (74, 48, 1, '04-67-58-77-00'), 
    (75, 48, 2, '04-67-58-28-13'), 
    (76, 49, 1, '01-47-04-66-66'), 
    (77, 49, 2, '01-47-04-24-79'), 
    (78, 50, 1, '04-38-44-60-78'), 
    (79, 51, 1, '02-37-31-72-50'), 
    (80, 51, 3, '06-69-30-88-46'), 
    (81, 52, 1, '01-45-51-22-22'), 
    (82, 53, 1, '01-43-21-75-75'), 
    (83, 54, 1, '04-79-85-38-25'), 
    (84, 55, 1, '01-40-30-15-15'), 
    (85, 56, 1, '01-46-05-11-44'), 
    (86, 56, 3, '06-48-25-79-92'), 
    (87, 57, 1, '01-45-26-59-94'), 
    (88, 57, 2, '01-48-78-30-69'), 
    (89, 58, 1, '01-45-26-59-94'), 
    (90, 58, 2, '04-66-22-37-66'), 
    (91, 59, 1, '01-46-22-66-55'), 
    (92, 60, 1, '01-42-02-54-47'), 
    (93, 60, 3, '06-42-00-08-74'), 
    (94, 61, 1, '01-43-67-26-26'), 
    (95, 62, 1, '01-43-67-26-26'), 
    (96, 63, 1, '01-46-40-73-67'), 
    (97, 64, 1, '01-42-50-23-17'), 
    (98, 65, 1, '04-91-93-84-72'), 
    (99, 65, 2, '04-91-88-10-57'), 
    (100, 66, 1, '01-53-68-99-99'), 
    (101, 67, 1, '01-49-53-95-50'), 
    (102, 68, 1, '01-47-00-91-32'), 
    (103, 69, 1, '05-61-21-83-08'), 
    (104, 69, 2, '05-61-27-71-21'), 
    (105, 70, 1, '04-67-49-30-90'), 
    (106, 70, 3, '06-67-28-82-84'), 
    (107, 71, 1, '04-67-49-39-49'), 
    (108, 71, 2, '04-67-49-37-91'), 
    (109, 72, 1, '04-67-49-39-49'), 
    (110, 73, 1, '03-23-96-06-03'), 
    (111, 74, 1, '05-56-57-71-96'), 
    (112, 75, 1, '04-92-13-11-20'), 
    (113, 76, 1, '05-56-35-02-21'), 
    (114, 76, 3, '06-56-95-29-54'), 
    (115, 77, 1, '01-42-27-96-10'), 
    (116, 77, 3, '06-43-80-32-43'), 
    (117, 78, 1, '01-47-49-49-94'), 
    (118, 79, 1, '03-21-71-51-11'), 
    (119, 80, 1, '04-94-96-04-61'), 
    (120, 80, 2, '04-94-49-17-45'), 
    (121, 81, 1, '04-91-25-62-25'), 
    (122, 82, 1, '04-92-10-13-00'), 
    (123, 83, 1, '01-45-00-31-21'), 
    (124, 84, 1, '02-98-44-63-71'), 
    (125, 85, 1, '01-46-59-05-37'), 
    (126, 85, 2, '01-53-27-30-01'), 
    (127, 86, 1, '03-87-63-67-13'), 
    (128, 87, 1, '01-45-55-95-15'), 
    (129, 88, 1, '04-92-43-35-14'), 
    (130, 90, 1, '04-68-04-33-49'), 
    (131, 90, 2, '04-68-04-33-05'), 
    (132, 91, 1, '05-57-77-12-22'), 
    (133, 91, 2, '05-57-77-12-23'), 
    (134, 92, 1, '04-93-67-26-26'), 
    (135, 93, 1, '04-93-96-15-47'), 
    (136, 94, 1, '04-92-70-77-69'), 
    (137, 94, 2, '04-92-72-07-96'), 
    (138, 95, 1, '04-94-65-66-67'), 
    (139, 96, 1, '04-90-96-42-03'), 
    (140, 97, 1, '04-91-52-51-52'), 
    (141, 98, 1, '04-90-78-10-68'), 
    (142, 98, 2, '04-90-71-91-14'), 
    (143, 99, 1, '05-59-03-54-09'), 
    (144, 99, 2, '05-59-03-38-88'), 
    (145, 100, 1, '01-42-68-05-43'), 
    (146, 100, 2, '01-42-65-07-61'), 
    (147, 101, 1, '01-43-46-16-03'), 
    (148, 102, 1, '01-47-66-00-10'), 
    (149, 103, 1, '01-45-78-93-23'), 
    (150, 103, 2, '01-43-28-52-50'), 
    (151, 104, 1, '03-47-55-29-29'), 
    (152, 104, 2, '01-49-66-32-52'), 
    (153, 105, 1, '04-94-41-17-36'), 
    (154, 105, 3, '06-11-56-78-89'), 
    (155, 106, 1, '02-41-58-89-52'), 
    (156, 106, 2, '04-66-62-95-64'), 
    (157, 107, 1, '01-51-58-52-50'), 
    (158, 107, 2, '02-92-89-18-58'), 
    (159, 108, 1, '03-45-21-89-19'), 
    (160, 109, 1, '01-49-87-65-21');
    La question précise est :

    Affichez toutes les personnes et ne faire apparaitre que leur n° de téléphone de type mobile (code = GSM)

    Sachant qu'il y a 109 personnes et qu'aucune n'a plus d'un seul mobile, la réponse doit donner 109 lignes...

    1 - La requête suivante ne donne pas le bon résultat..
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
           LEFT OUTER JOIN T_TELEPHONE_TEL AS T
                ON PP.PRS_ID = T.PRS_ID
           LEFT OUTER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                ON T.TTL_ID = TT.TTL_ID AND TTL_CODE = 'GSM';
    ... car elle donne tous les n° de téléphone or on ne veut pas d'autres n° que ceux du mobile (161 lignes)

    2 - la requête suivante affiche bien les personnes qui ont un téléphone mobile ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
           LEFT OUTER JOIN T_TELEPHONE_TEL AS T
                ON PP.PRS_ID = T.PRS_ID
           INNER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                ON T.TTL_ID = TT.TTL_ID AND TTL_CODE = 'GSM';
    ... mais fait disparaître tous ceux qui n'en ont pas (16 lignes)

    3 - une bonne manière est la suivante ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
           LEFT OUTER JOIN (T_TELEPHONE_TEL AS T
                            INNER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                                  ON T.TTL_ID = TT.TTL_ID AND TTL_CODE = 'GSM') 
                ON PP.PRS_ID = T.PRS_ID;
    ... le parenthèsage influe donc bien sur l'ordre jointures

    4 - une autre manière d'écrire cette requête est la suivante...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
           LEFT OUTER JOIN (SELECT TEL_ID, PRS_ID, T.TTL_ID AS TTL_ID_FK, TEL_NUMERO,
                                   TT.TTL_ID, TTL_CODE, TTL_LIBELLE
                            FROM   T_TELEPHONE_TEL AS T
                                   INNER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                                         ON T.TTL_ID = TT.TTL_ID AND TTL_CODE = 'GSM') AS TTT
                ON PP.PRS_ID = TTT.PRS_ID;
    ... à l'aide d'un sous requête.

    CQFD...


    5 -notons au passage qu'il n'est pas interdit d'écrire une jointure comme cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
           LEFT OUTER JOIN T_TELEPHONE_TEL AS T
           INNER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                 ON T.TTL_ID = TT.TTL_ID AND TTL_CODE = 'GSM'
                 ON PP.PRS_ID = T.PRS_ID;
    ... même si c'est abominable !!! la cause en est de possible jointures circulaires...

    A +
    Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
    Le site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
    Blog SQL, SQL Server, SGBDR : http://blog.developpez.com/sqlpro
    Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
    Entreprise SQL SPOT : modélisation, conseils, audit, optimisation, formation...
    * * * * * Expertise SQL Server : http://mssqlserver.fr/ * * * * *

  5. #5
    Modérateur

    Profil pro
    dba
    Inscrit en
    Janvier 2010
    Messages
    5 643
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : dba

    Informations forums :
    Inscription : Janvier 2010
    Messages : 5 643
    Par défaut
    abominable... pas forcément, avec la bonne indentation :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
    LEFT OUTER JOIN     T_TELEPHONE_TEL AS T
                        INNER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                                ON T.TTL_ID = TT.TTL_ID 
                                AND TTL_CODE = 'GSM'
           ON PP.PRS_ID = T.PRS_ID;
    Bien sûr, si on multiplie les imbrications, et dans une requete plus complexe, les parenthèses peuvent devenir "utiles" pour la lisibilité, mais pour la lisibilité uniquement...

    bref, ça reste principalement une question de goût/style d'écriture, mais au final on est bien d'accord : il faut savoir user des parenthèses, sans en abuser...

  6. #6
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    22 010
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Expert bases de données / SQL / MS SQL Server / Postgresql
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 22 010
    Billets dans le blog
    6
    Par défaut
    Citation Envoyé par aieeeuuuuu Voir le message
    abominable... pas forcément, avec la bonne indentation :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
    LEFT OUTER JOIN     T_TELEPHONE_TEL AS T
                        INNER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                                ON T.TTL_ID = TT.TTL_ID 
                                AND TTL_CODE = 'GSM'
           ON PP.PRS_ID = T.PRS_ID;
    Entre nous ton identation est assez dégeulasse. Mieux vaut :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
           LEFT OUTER JOIN     T_TELEPHONE_TEL AS T
                INNER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                      ON T.TTL_ID = TT.TTL_ID 
                         AND TTL_CODE = 'GSM'
                ON PP.PRS_ID = T.PRS_ID;
    A +
    Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
    Le site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
    Blog SQL, SQL Server, SGBDR : http://blog.developpez.com/sqlpro
    Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
    Entreprise SQL SPOT : modélisation, conseils, audit, optimisation, formation...
    * * * * * Expertise SQL Server : http://mssqlserver.fr/ * * * * *

  7. #7
    J1
    J1 est déconnecté
    Membre expérimenté Avatar de J1
    Inscrit en
    Mai 2004
    Messages
    321
    Détails du profil
    Informations forums :
    Inscription : Mai 2004
    Messages : 321
    Par défaut
    Citation Envoyé par SQLpro Voir le message
    Pour conclure, voici un exemple que je donne en cours, sur l'utilité du parenthèsage...

    [...]

    La question précise est :

    Affichez toutes les personnes et ne faire apparaitre que leur n° de téléphone de type mobile (code = GSM)

    Sachant qu'il y a 109 personnes et qu'aucune n'a plus d'un seul mobile, la réponse doit donner 109 lignes...

    1 - La requête suivante ne donne pas le bon résultat..
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
           LEFT OUTER JOIN T_TELEPHONE_TEL AS T
                ON PP.PRS_ID = T.PRS_ID
           LEFT OUTER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                ON T.TTL_ID = TT.TTL_ID AND TTL_CODE = 'GSM';
    ... car elle donne tous les n° de téléphone or on ne veut pas d'autres n° que ceux du mobile (161 lignes)

    2 - la requête suivante affiche bien les personnes qui ont un téléphone mobile ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
           LEFT OUTER JOIN T_TELEPHONE_TEL AS T
                ON PP.PRS_ID = T.PRS_ID
           INNER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                ON T.TTL_ID = TT.TTL_ID AND TTL_CODE = 'GSM';
    ... mais fait disparaître tous ceux qui n'en ont pas (16 lignes)

    3 - une bonne manière est la suivante ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
           LEFT OUTER JOIN (T_TELEPHONE_TEL AS T
                            INNER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                                  ON T.TTL_ID = TT.TTL_ID AND TTL_CODE = 'GSM') 
                ON PP.PRS_ID = T.PRS_ID;
    ... le parenthèsage influe donc bien sur l'ordre jointures

    4 - une autre manière d'écrire cette requête est la suivante...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
           LEFT OUTER JOIN (SELECT TEL_ID, PRS_ID, T.TTL_ID AS TTL_ID_FK, TEL_NUMERO,
                                   TT.TTL_ID, TTL_CODE, TTL_LIBELLE
                            FROM   T_TELEPHONE_TEL AS T
                                   INNER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                                         ON T.TTL_ID = TT.TTL_ID AND TTL_CODE = 'GSM') AS TTT
                ON PP.PRS_ID = TTT.PRS_ID;
    ... à l'aide d'un sous requête.

    CQFD...


    5 -notons au passage qu'il n'est pas interdit d'écrire une jointure comme cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT *
    FROM   T_PERSONNE_PHYSIQUE_PSP AS PP
           LEFT OUTER JOIN T_TELEPHONE_TEL AS T
           INNER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
                 ON T.TTL_ID = TT.TTL_ID AND TTL_CODE = 'GSM'
                 ON PP.PRS_ID = T.PRS_ID;
    ... même si c'est abominable !!! la cause en est de possible jointures circulaires...

    A +
    Bonjour.

    Les cas que vous évoquez sont intéressants mais je n'arrive pas aux mêmes conclusions que vous.
    Vous déduisez du cas 3 que les parenthèses influent sur l'ordre d'exécution des des jointures. Or le changement dans l'ordre d'exécution des jointures (par rapport au cas 2) me semble plutôt lié à votre réécriture des jointures.
    Vous passez en effet d'une structure (cas 2)...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    LEFT JOIN...
    ON...
    INNER JOIN...
    ON...
    à une structure (cas 3)...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    LEFT JOIN (...
      INNER JOIN...
      ON...)
    ON
    C'est cette structure "imbriquée" qui modifie d'après moi l'ordre d'exécution des jointures.
    Ainsi, en dépit des parenthèses utilisées, cette dernière structure me semble strictement équivalente à celle de votre cas 5, à savoir...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    LEFT JOIN...
      INNER JOIN...
      ON...
    ON
    Qu'en pensez-vous ?

  8. #8
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 636
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 636
    Billets dans le blog
    10
    Par défaut
    En effet,
    Ce ne sont pas les parenthèses mais bien la construction des jointures qui provoque ici un résultat différent entre la solution 2 et la solution 3
    Les parenthèses rendent seulement la construction plus lisible, à ce titre j'y suis favorable.

    Quand à la dernière solution, elle ressemble fort à ce que l'on trouve dans les requêtes générées par access ou par genio

  9. #9
    J1
    J1 est déconnecté
    Membre expérimenté Avatar de J1
    Inscrit en
    Mai 2004
    Messages
    321
    Détails du profil
    Informations forums :
    Inscription : Mai 2004
    Messages : 321
    Par défaut
    Citation Envoyé par escartefigue Voir le message
    En effet,
    Ce ne sont pas les parenthèses mais bien la construction des jointures qui provoque ici un résultat différent entre la solution 2 et la solution 3
    Nous sommes donc d'accord
    A noter d'ailleurs que les jointures imbriquées (cas 3 et 5) et/ou les tables dérivées (cas 4) ne sont pas indispensables pour parvenir à ses fins. Des jointures "classiques" (c'est-à-dire non-imbriquées) peuvent en effet mener au même résultat, moyennant une réorganisation de la requête :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT *
    FROM   T_TELEPHONE_TEL AS T
    INNER JOIN T_R_TYPE_TELEPHONE_TTL AS TT
    	ON TT.TTL_ID = T.TTL_ID AND TT.TTL_CODE = 'GSM'
    RIGHT JOIN T_PERSONNE_PHYSIQUE_PSP AS PP
    	ON PP.PRS_ID = T.PRS_ID;
    Après, il y aurait aussi des solutions impliquant des sous-requêtes, mais ce n'est pas l'objet de cette (intéressante) discussion.

Discussions similaires

  1. Problème Inner Outer Join
    Par silmortes dans le forum SAP Crystal Reports
    Réponses: 1
    Dernier message: 23/10/2006, 16h10
  2. [MySQL] INNER & OUTER JOIN imbriqués avec WHERE
    Par kelson dans le forum Langage SQL
    Réponses: 1
    Dernier message: 28/02/2006, 12h00
  3. Problème de jointure avec INNER JOIN et LEFT OUTER JOIN
    Par tonio-lille dans le forum Langage SQL
    Réponses: 4
    Dernier message: 10/02/2006, 12h45
  4. [ requeste sql ]INNER JOIN / OUTER JOIN
    Par hocinema dans le forum Langage SQL
    Réponses: 2
    Dernier message: 12/04/2004, 21h28

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo