【问题标题】:SQL cartesian join problemSQL笛卡尔连接问题
【发布时间】:2010-10-24 18:41:55
【问题描述】:

我有三张桌子

  • A:A.pID 主键,A.Name nvarchar(250)
  • B:B.pID 主键,B.Name nvarchar(250)
  • C: C.pID 主键,C.Name nvarchar(250)

A 和 B 之间存在 m 到 n 关系(表 lA_B 主键为 lA_B.pID 和 .pInstanceA 表 A 的外键和 .pInstanceB 表 B 的外键)

A 和 C 之间存在 m 对 n 关系(table lA_C 主键 lA_C.pID 和 .pInstanceA 表 A 的外键和 .pInstanceB 表 C 的外键)

  • A1 与 B1、B2 和 C1 相关
  • A2 与 B3 和 C2、C3 相关
  • A3 与 B4 相关
  • A4 与 C4 相关
  • A5 没有关系

这是我的 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)

这个查询:

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) 

返回

A1 B1 C1 A1 B2 C1 A2 B3 C2 A2 B3 C3 A3 B4 空 A4 空 C4 A5 空 空

现在的问题:-) 如何查询接收

A1 B1 空 A1 B2 空 A1 空 C1 A2 B3 空 A2 空 C2 A2 空 C3 A3 B4 空 A4 空 C4 A5 空 空

问题是当我用 B 和 C 进行连接时,结果是 B C 的所有组合。我怎样才能消除这种情况?

【问题讨论】:

  • CREATE TABLE 语句似乎被缩短了,您可以编辑帖子并完整包含它们吗?

标签: sql join hierarchical-data cartesian-product


【解决方案1】:

你也许可以用 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)

第一部分选择A和B的所有组合,第二部分选择A和C的所有组合。

如果你想过滤掉像 (A4,NULL,NULL) 这样的行,因为已经有一行 (A4,NULL,C4),试试这个查询:

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

对于 B 上的连接,这表示包括在 B 中匹配或在 C 中没有匹配的行。在 C 上的连接仅包括在 C 中匹配的行。不匹配的行将从 B 上的连接中获取。

请注意,UNION 会过滤掉重复的行,例如 DISTINCT。要包含每一行,您可以使用 UNION ALL。

【讨论】:

  • A1 NULL NULL A1 B1 NULL A1 B2 NULL A2 NULL NULL A2 B3 NULL A3 NULL NULL A3 B4 NULL A4 NULL NULL A5 NULL NULL 不太好......我不需要这行 A1 NULL NULL A2 NULL NULL A3 NULL NULL
  • @Andomar - 看起来您已经编辑了答案以反映@bogdanbrudiu 的评论?在答案文本中注意这一点会很有帮助。
  • @hainstech:在发布完整表定义之后,但在我看到评论之前编辑了答案。编辑是关于同一件事。
【解决方案2】:

我认为这样做:

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

【讨论】:

  • 如果“zzz”是一个真实的名字会怎样?
  • 嗯,你会生活在一个非常奇怪的地方,或者你没有验证名称列上的条目。在这种情况下,你自己!
猜你喜欢
  • 1970-01-01
  • 2015-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多