Vous êtes sur le site Web historique de la documentation de 4D. Les documentations sont progressivement déplacées vers developer.4d.com |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
4D v20 R8
Prise en charge des jointures
|
name | depID | cityID |
Alain | 10 | 30 |
Anne | 11 | 39 |
Bernard | 10 | 33 |
Fabrice | 12 | 35 |
Martine | 15 | 30 |
Philippe | NULL | 33 |
Thomas | 10 | NULL |
depID | depName |
10 | Program |
11 | Engineering |
NULL | Marketing |
12 | Development |
13 | Quality |
cityID | cityName |
30 | Paris |
33 | New York |
NULL | Berlin |
Si vous le souhaitez, vous pouvez générer cette base automatiquement en exécutant le code suivant :
Début SQL
DROP TABLE IF EXISTS Employees;
CREATE TABLE Employees ( depID INT32, name VARCHAR, cityID INT32);
INSERT INTO Employees (name, depID, cityID) VALUES ('Alain', 10, 30);
INSERT INTO Employees (name, depID, cityID) VALUES ('Anne', 11, 39);
INSERT INTO Employees (name, depID, cityID) VALUES ('Bernard', 10, 33);
INSERT INTO Employees (name, depID, cityID) VALUES ('Fabrice', 12, 35);
INSERT INTO Employees (name, depID, cityID) VALUES ('Martine', 15, 30);
INSERT INTO Employees (name, depID, cityID) VALUES ('Philippe', NULL, 33);
INSERT INTO Employees (name, depID, cityID) VALUES ('Thomas', 10, NULL);
DROP TABLE IF EXISTS Departments;
CREATE TABLE Departments ( depID INT32, depName VARCHAR );
INSERT INTO Departments (depID, depName) VALUES (10, 'Program');
INSERT INTO Departments (depID, depName) VALUES (11, 'Engineering');
INSERT INTO Departments (depID, depName) VALUES (NULL, 'Marketing');
INSERT INTO Departments (depID, depName) VALUES (12, 'Development');
INSERT INTO Departments (depID, depName) VALUES (13, 'Quality');
DROP TABLE IF EXISTS Cities;
CREATE TABLE Cities ( cityID INT32, cityName VARCHAR );
INSERT INTO Cities (cityID, cityName) VALUES (30, 'Paris');
INSERT INTO Cities (cityID, cityName) VALUES (33, 'New York');
INSERT INTO Cities (cityID, cityName) VALUES (NULL, 'Berlin');
Fin SQL
Une jointure interne (inner join) est une jointure basée sur une comparaison d’égalité entre deux colonnes.
Voici un exemple de jointure interne implicite :
SELECT *
FROM employees, departments
WHERE employees.DepID = departments.DepID;
Dans 4D, vous pouvez également utiliser le mot-clé JOIN afin de définir une jointure interne explicite :
SELECT *
FROM employees
INNER JOIN departments
ON employees.DepID = departments.DepID;
Cette requête peut être insérée dans le code 4D de la manière suivante :
TABLEAU TEXTE(aName;0)
TABLEAU TEXTE(aDepName;0)
TABLEAU ENTIER(aEmpDepID;0)
TABLEAU ENTIER(aDepID;0)
Debut SQL
SELECT Employees.name, Employees.depID, Departments.depID, Departments.depName
FROM Employees
INNER JOIN Departments
ON Employees.depID = Departments.depID
INTO :aName, :aEmpDepID, :aDepID, :aDepName;
Fin SQL
Résultat de cette jointure :
aName | aEmpDepID | aDepID | aDepName |
Alain | 10 | 10 | Program |
Anne | 11 | 11 | Engineering |
Bernard | 10 | 10 | Program |
Fabrice | 12 | 12 | Development |
Thomas | 10 | 10 | Program |
A noter que ni les employés Philippe et Martine ni les départements Marketing et Quality n’apparaissent dans la jointure résultante car :
4D vous permet de générer des jointures externes (OUTER JOINs). Dans une jointure externe, il n’est pas nécessaire qu’il existe une correspondance entre les lignes des tables jointes. La table résultante contient toutes les lignes des tables (ou d’au moins une des tables de la jointure) même s’il n’y a pas de ligne correspondante. Ce principe permet de s’assurer que toutes les informations d’une table sont exploitées, même si des lignes ne sont pas renseignées entre les différentes tables jointes.
Il existe trois types de jointures externes, définies par les mots-clés LEFT, RIGHT et FULL. LEFT et RIGHT permettent de désigner la table (située à gauche ou à droite du mot-clé) dont la totalité des données devra être traitée. FULL indique une jointure externe bilatérale.
Note : Seules les jointures externes explicites sont prises en charge par 4D.
Dans les jointures externes à deux tables, les conditions peuvent être complexes mais elles doivent toujours être basées sur un test d’égalité entre des colonnes incluses dans la jointure. Par exemple, il n’est pas possible d’utiliser l’opérateur >= dans une condition de jointure explicite. Les jointures implictes autorisent tout type de comparaison. En interne, les comparaisons d’égalité sont effectuées directement par le moteur de 4D, ce qui leur assure une grande rapidité d’exécution.
Le résultat d’une jointure externe gauche (ou jointure gauche) contient toujours tous les enregistrements de la table située à gauche du mot-clé même si la condition de jointure ne trouve pas d’enregistrement correspondant dans la table de droite. Cela signifie que si pour une ligne de la table gauche la requête trouve zéro ligne correspondant dans la table droite, la jointure contiendra la ligne avec la valeur NULL pour chaque colonne de la table de droite. Autrement dit, une jointure externe gauche retourne toutes les lignes de la table gauche plus celles de la table droite qui correspondent à la condition de jointure (ou NULL si aucune ne correspond). A noter que si la table la droite contient plus d’une ligne correspondant au prédicat de la jointure pour une ligne de la table gauche, les valeurs de la table gauche seront répétées pour chaque ligne distincte de la table droite.
Voici un exemple de code 4D effectuant une jointure externe gauche :
TABLEAU TEXTE(aName;0)
TABLEAU TEXTE(aDepName;0)
TABLEAU ENTIER(aEmpDepID;0)
TABLEAU ENTIER(aDepID;0)
Debut SQL
SELECT Employees.name, Employees.depID, Departments.depID, Departments.depName
FROM Employees
LEFT OUTER JOIN Departments
ON Employees.DepID = Departments.DepID
INTO :aName, :aEmpDepID, :aDepID, :aDepName;
Fin SQL
Résultat de cette jointure avec notre base d’exemple (les lignes additionnelles sont en rouge) :
aName | aEmpDepID | aDepID | aDepName |
Alain | 10 | 10 | Program |
Anne | 11 | 11 | Engineering |
Bernard | 10 | 10 | Program |
Fabrice | 12 | 12 | Development |
Thomas | 10 | 10 | Program |
Martine | 15 | NULL | NULL |
Philippe | NULL | NULL | NULL |
A l’exact opposé de la jointure externe gauche, le résultat d’une jointure externe droite contient toujours tous les enregistrements de la table située à droite du mot-clé même si la condition de jointure ne trouve pas d’enregistrement correspondant dans la table gauche.
Voici un exemple de code 4D effectuant une jointure externe droite :
TABLEAU TEXTE(aName;0)
TABLEAU TEXTE(aDepName;0)
TABLEAU ENTIER(aEmpDepID;0)
TABLEAU ENTIER(aDepID;0)
Debut SQL
SELECT Employees.name, Employees.depID, Departments.depID, Departments.depName
FROM Employees
RIGHT OUTER JOIN Departments
ON Employees.DepID = Departments.DepID
INTO :aName, :aEmpDepID, :aDepID, :aDepName;
Fin SQL
Résultat de cette jointure avec notre base d’exemple (les lignes additionnelles sont en rouge) :
aName | aEmpDepID | aDepID | aDepName |
Alain | 10 | 10 | Program |
Anne | 11 | 11 | Engineering |
Bernard | 10 | 10 | Program |
Fabrice | 12 | 12 | Development |
Thomas | 10 | 10 | Program |
NULL | NULL | NULL | Marketing |
NULL | NULL | 13 | Quality |
La jointure externe bilatérale combine simplement les résultats d’une jointure externe gauche et d’une jointure externe droite. La table jointure résultante contient tous les enregistrements des tables gauche et droite et remplit les champs manquants de chaque côté avec des valeurs NULL.
Voici un exemple de code 4D effectuant une jointure externe bilatérale :
TABLEAU TEXTE(aName;0)
TABLEAU TEXTE(aDepName;0)
TABLEAU ENTIER(aEmpDepID;0)
TABLEAU ENTIER(aDepID;0)
Debut SQL
SELECT Employees.name, Employees.depID, Departments.depID, Departments.depName
FROM Employees
FULL OUTER JOIN Departments
ON Employees.DepID = Departments.DepID
INTO :aName, :aEmpDepID, :aDepID, :aDepName;
Fin SQL
Résultat de cette jointure avec notre base d’exemple (les lignes additionnelles sont en rouge) :
aName | aEmpDepID | aDepID | aDepName |
Alain | 10 | 10 | Program |
Anne | 11 | 11 | Engineering |
Bernard | 10 | 10 | Program |
Fabrice | 12 | 12 | Development |
Thomas | 10 | 10 | Program |
Martine | 15 | NULL | NULL |
Philippe | NULL | NULL | NULL |
NULL | NULL | NULL | Marketing |
NULL | NULL | 13 | Quality |
A compter de 4D v15 R4, le serveur SQL intégré de 4D étend la prise en charge des jointures SQL externes aux requêtes impliquant trois tables ou plus. Cette implémentation particulière inclut ses propres règles et limitations, qui sont décrites dans cette section.
Comme les jointures à deux tables, les jointures externes impliquant trois tables ou plus peuvent être LEFT, RIGHT ou FULL. Pour des informations générales sur les jointures externes, veuillez vous référer ci-dessus au paragraphe Jointures externes entre deux tables.
A la différence des jointures externes à deux tables, les jointures externes impliquant trois tables ou plus acceptent plusieurs opérateurs de comparaison, en plus de l'égalité (=) : <, >, >=, ou <=. Ces opérateurs peuvent être combinés dans les clauses ON.
Par exemple, la requête suivante sera exécutée avec succès:
SELECT * FROM T1
LEFT JOIN
(T2 LEFT JOIN T3 ON T2.ID=T3.ID) -- ici T2 est juste à gauche et T3 est juste à droite
ON T1.ID=T3.ID -- ici T1 est juste à gauche et T3 est juste à droite
Avec nos trois tables, cette requête pourrait être par exemple :
TABLEAU TEXTE(aName;0)
TABLEAU TEXTE(aDepName;0)
TABLEAU TEXTE(aCityName;0)
TABLEAU ENTIER(aEmpDepID;0)
TABLEAU ENTIER(aEmpCityID;0
TABLEAU ENTIER(aDepID;0)
TABLEAU ENTIER(aCityID;0)
Debut SQL
SELECT Employees.name, Employees.depID, Employees.cityID, Departments.depID, Departments.depName, Cities.cityID, Cities.cityName
FROM Departments
LEFT JOIN
(Employees LEFT JOIN Cities ON Employees.cityID=Cities.cityID)
ON Departments.depID=Employees.depID
INTO :aName, :aEmpDepID, :aEmpCityID, :aDepID, :aDepName, :aCityID, :aCityName;
Fin SQL
Les résultats sont alors :
aName | aEmpDepID | aEmpCityID | aDepID | aDepName | aCityID | aCityName |
Alan | 10 | 30 | 10 | Program | NULL | NULL |
Bernard | 10 | 33 | 10 | Program | 30 | Paris |
Anne | 11 | 39 | 11 | Engineering | 33 | New York |
Fabrice | 12 | 35 | 12 | Development | NULL | NULL |
Thomas | 10 | NULL | 10 | Program | NULL | NULL |
NULL | NULL | NULL | NULL | Marketing | NULL | NULL |
NULL | NULL | NULL | 13 | Quality | NULL | NULL |
D'un autre côté, les trois requêtes suivantes seront rejetées car elles violent certaines règles :
SELECT * FROM T1
LEFT JOIN
(T2 LEFT JOIN T3 ON T2.ID=T1.ID) -- ici T2 est bien est juste à gauche mais T1 n'est pas présent juste à droite
ON T1.ID=T3.ID
SELECT * FROM
(T1 LEFT JOIN T2 ON T1.ID=T2.ID)
LEFT JOIN
(T3 LEFT JOIN T4 ON T3.ID=T4.ID)
ON T3.Name=T4.Name -- ici T3 et T4 proviennent de la partie droite de la clause JOIN et aucune table ne provient de la gauche
SELECT * FROM T1
LEFT JOIN
(T2 LEFT JOIN T3 ON T2.ID=T3.ID)
ON T1.ID=T3.ID AND T1.ID=T2.ID -- ici plus de deux tables sont utilisées dans la clause ON : T1, T2 et T3
En général, si les tables (Tx1, Tx2..., Txn) à la gauche de la clause JOIN et les tables (Ty1, Ty2..., Tyn) à sa droite sont jointes, alors l'expression ON doit référencer exactement une table gauche Txa et une table droite Tyb.
Non accepté dans la clause ON | Accepté dans la clause ON | |
Opérations booéennes | OR | AND et NOT |
Prédicats et fonctions | IS NULL, COALESCE | Tous les autres prédicats et fonctions intégrées (toutes combinaisons) |
Références de variables 4D | - | Prise en charge sans restriction |
Appels de méthodes 4D | Lorsque soit la partie gauche soit la partie droite de la clause JOIN courante est une jointure externe explicite | Tous les autres cas (voir exemples ci-dessous |
L'exemple suivant avec un appel de méthode 4D est pris en charge car il n'y a pas de sous-jointures non internes dans la jointure :
SELECT * FROM T1
LEFT JOIN T2
ON T1.ID={FN My4DCall (T2.ID) AS INT32}
Par contre, cet exemple d'appel de méthode 4D n'est pas pris en charge car des sous-jointures non internes sont présentes dans la jointure :
SELECT * FROM
(T1 LEFT JOIN T2 ON T1.ID=T2.ID)
LEFT JOIN -- Les deux parties gauche et droite de cette clause de jointure contiennent des jointures LEFT explicites
(T3 LEFT JOIN T4 ON T3.ID=T4.ID)
ON T1.ID={FN My4DCall (T4.ID) AS INT32} -- jointure avec des sous-jointures non internes
SELECT T2.ID FROM T2
WHERE T2.ID=(
SELECT COUNT ( * ) FROM
(T1 LEFT JOIN T3 ON T1.ID=T3.ID)
RIGHT JOIN T4 ON T3.ID=T4.ID)
Produit : 4D
Thème : Utiliser le SQL dans 4D
Nom intl. : Support of joins
4D - Référence SQL ( 4D v20 R8)