Problème de jointure cartésienne SQL
-
22-07-2019 - |
Question
j'ai trois tables
- A: clé primaire A.pID, A.Name nvarchar (250)
- B: clé primaire B.pID, B.Name nvarchar (250)
- C: clé primaire C.pID, C.Name nvarchar (250)
Il existe une relation entre A et B (table lA_B
avec clé primaire lA_B.pID
et .pInstanceA
clé étrangère vers table A et .pInstanceB
Clé étrangère vers la table B)
Il existe une relation entre A et C ( table lA_C
avec clé primaire lA_C.pID
et .pInstanceA
clé étrangère vers la table A et .pInstanceB
clé étrangère vers la table C)
- A1 est en relation avec B1, B2 et C1
- A2 est en relation avec B3 et C2, C3
- A3 est en relation avec B4
- A4 est en relation avec C4
- A5 n'a pas de relation
Voici mon code SQL:
CREATE TABLE [dbo].[A]( [pID] [bigint] NOT NULL, [Name] [nvarchar](250) NULL )
CREATE TABLE [dbo].[B]( [pID] [bigint] NOT NULL, [Name] [nvarchar](250) NULL )
CREATE TABLE [dbo].[C]( [pID] [bigint] NOT NULL, [Name] [nvarchar](250) NULL)
CREATE TABLE [dbo].[lA_B]( [pID] [bigint] NOT NULL, [pInstanceA] [bigint] NULL, [pInstanceB] [bigint] NULL )
CREATE TABLE [dbo].[lA_C]( [pID] [bigint] NOT NULL, [pInstanceA] [bigint] NULL, [pInstanceB] [bigint] NULL )
INSERT INTO [dbo].[A] ([pID] ,[Name]) VALUES (1,'A1')
INSERT INTO [dbo].[A] ([pID] ,[Name]) VALUES (2,'A2')
INSERT INTO [dbo].[A] ([pID] ,[Name]) VALUES (3,'A3')
INSERT INTO [dbo].[A] ([pID] ,[Name]) VALUES (4,'A4')
INSERT INTO [dbo].[A] ([pID] ,[Name]) VALUES (5,'A5')
INSERT INTO [dbo].[B] ([pID] ,[Name]) VALUES (1,'B1')
INSERT INTO [dbo].[B] ([pID] ,[Name]) VALUES (2,'B2')
INSERT INTO [dbo].[B] ([pID] ,[Name]) VALUES (3,'B3')
INSERT INTO [dbo].[B] ([pID] ,[Name]) VALUES (4,'B4')
INSERT INTO [dbo].[C] ([pID] ,[Name]) VALUES (1,'C1')
INSERT INTO [dbo].[C] ([pID] ,[Name]) VALUES (2,'C2')
INSERT INTO [dbo].[C] ([pID] ,[Name]) VALUES (3,'C3')
INSERT INTO [dbo].[C] ([pID] ,[Name]) VALUES (4,'C4')
INSERT INTO [dbo].[lA_B] ([pID],[pInstanceA],[pInstanceB]) VALUES (1,1,1)
INSERT INTO [dbo].[lA_B] ([pID],[pInstanceA],[pInstanceB]) VALUES (2,1,2)
INSERT INTO [dbo].[lA_B] ([pID],[pInstanceA],[pInstanceB]) VALUES (3,2,3)
INSERT INTO [dbo].[lA_B] ([pID],[pInstanceA],[pInstanceB]) VALUES (4,3,4)
INSERT INTO [dbo].[lA_C] ([pID],[pInstanceA],[pInstanceB]) VALUES (1,1,1)
INSERT INTO [dbo].[lA_C] ([pID],[pInstanceA],[pInstanceB]) VALUES (2,2,2)
INSERT INTO [dbo].[lA_C] ([pID],[pInstanceA],[pInstanceB]) VALUES (3,2,3)
INSERT INTO [dbo].[lA_C] ([pID],[pInstanceA],[pInstanceB]) VALUES (4,4,4)
cette requête:
SELECT
A.Name AS A,
B.Name AS B,
C.Name AS C
FROM
A
left JOIN lA_B ON (A.pID = lA_B.pInstanceA)
left JOIN B ON (B.pID = lA_B.pInstanceB)
left JOIN lA_C ON (A.pID = lA_C.pInstanceA)
left JOIN C ON (C.pID = lA_C.pInstanceB)
retourne
A1 B1 C1 A1 B2 C1 A2 B3 C2 A2 B3 C3 A3 B4 NULL A4 NULL C4 A5 NULL NULL
Et maintenant la question :-) comment interroger pour recevoir
A1 B1 NULL A1 B2 NULL A1 NULL C1 A2 B3 NULL A2 NULL C2 A2 NULL C3 A3 B4 NULL A4 NULL C4 A5 NULL NULL
Le problème est que lorsque je fais la jointure à la fois avec B et avec C, le résultat contient toutes les combinaisons de B C. Comment puis-je éliminer cela?
La solution
Vous pourrez peut-être faire cela avec une UNION:
SELECT A.Name AS A, B.Name AS B, NULL AS C
FROM A
left JOIN lA_B ON (A.pID=lA_B.pInstanceA)
left JOIN B ON (lA_B.pInstanceB=B.pID)
UNION
SELECT A.Name AS A, NULL AS B, C.Name AS C
FROM A
left JOIN lA_C ON (A.pID=lA_C.pInstanceA)
left JOIN C ON (lA_C.pInstanceB=C.pID)
La première partie sélectionne toutes les combinaisons de A et B, la seconde toutes les combinaisons de A et C.
Si vous souhaitez filtrer les lignes telles que (A4, NULL, NULL) car il y en a déjà une (A4, NULL, C4), essayez la requête suivante:
SELECT A.Name AS A, B.Name AS B, NULL AS C
FROM A
LEFT JOIN lA_B ON (A.pID=lA_B.pInstanceA)
LEFT JOIN B ON (lA_B.pInstanceB=B.pID)
WHERE b.name is not null
or not exists(select * from lA_C where A.pID=lA_C.pInstanceA)
UNION
SELECT A.Name AS A, NULL AS B, C.Name AS C
FROM A
LEFT JOIN lA_C ON (A.pID=lA_C.pInstanceA)
LEFT JOIN C ON (lA_C.pInstanceB=C.pID)
WHERE c.name is not null
ORDER BY A,B,C
Pour la jointure sur B, cela signifie d'inclure les lignes qui ont une correspondance dans B ou pour lesquelles il n'y a pas de correspondance dans C. La jointure sur C inclut uniquement les lignes qui correspondent dans C. Les lignes qui ne correspondent à aucune ne être inclus à partir de la jointure sur B.
Notez que UNION filtre les lignes en double, comme DISTINCT. Pour inclure chaque ligne, vous pouvez utiliser UNION ALL.
Autres conseils
Je pense que cela le fait:
select a,
case when b='zzz' then null else b end as b,
case when c='zzz' then null else c end as c
from (SELECT A.Name AS A
,b.Name as b
,'zzz' as c
FROM A
JOIN lA_B ON (A.pID = lA_B.pInstanceA)
JOIN B ON (B.pID = lA_B.pInstanceB)
union
select a.Name
,'zzz'
,c.NAme
from A
left JOIN lA_C ON (A.pID = lA_C.pInstanceA)
left JOIN C ON (C.pID = lA_C.pInstanceB)) as a