【问题标题】:Complex SQL Join help!复杂的SQL加入求助!
【发布时间】:2010-07-01 18:04:25
【问题描述】:

(要快速回顾一下,请参阅标题为数据的部分。只能在图 A 和图 B 中得到结果,我想要的是期望的结果,尝试了答案部分中发布的所有内容。)

所有,我一直在努力在我的应用程序中巧妙地使用联接。男孩,我花了一段时间,但我想我已经掌握了这个窍门,但我无法让这个查询工作。

请帮帮我!以下是原样加入:

SELECT     TOP (100) PERCENT a.Id, a.DateCreated, a.DateModified, a.LastUpdatedBy, a.AccomplishmentTypeId, a.Title, a.Description, a.ApplicableDate, 
                  a.LocalId, at.Name AS AccomplishmentTypeName, a.Name AS AreaName, u.FirstName, u.LastName, ug.Name AS UserGroupName, 
                  ugo.Name AS OtherUserGroupName
FROM         dbo.Accomplishment AS a INNER JOIN
                  dbo.AccomplishmentType AS at ON at.Id = a.AccomplishmentTypeId INNER JOIN
                  dbo.AccomplishmentArea AS aal ON aal.AccomplishmentId = a.Id INNER JOIN
                  dbo.Area AS al ON al.Id = aal.AreaId INNER JOIN
                  dbo.UserAccomplishment AS ua ON ua.AccomplishmentId = a.Id INNER JOIN
                  dbo.[User] AS u ON u.Id = ua.UserId INNER JOIN
                  dbo.UserUserGroup AS uug ON uug.UserId = u.Id INNER JOIN
                  dbo.UserGroup AS ug ON ug.Id = uug.UserGroupId INNER JOIN
                  dbo.UserGroupType AS ugt ON ugt.Id = ug.UserGroupTypeId INNER JOIN
                  dbo.UserUserGroup AS uugo ON uugo.UserId = u.Id LEFT OUTER JOIN
                  dbo.UserGroup AS ugo ON ugo.Id = uugo.UserGroupId LEFT OUTER JOIN
                  dbo.UserGroupType AS ugto ON ugto.Id = ugo.UserGroupTypeId
WHERE     (ug.LocalId = 2) AND (ugo.LocalId <> 2) AND (ugto.LocalId = 4)
ORDER BY a.DateCreated DESC, u.LastName, u.FirstName, u.Id, UserGroupName

现在这有点复杂,但我正在做的是从所有这些有成就的相关表中选择信息,其中有一个与之关联的用户,该用户的用户组 localId = 2,但是如果他们确实有,我还想返回 usertype.localid = 4 的用户所在的所有用户组。

让我举一些示例数据来说明这一点,现在我在数据库中有 3 个成就,如下所示:

-------(数据)

Accomplishment 1:
   User: John
      Usergroups: Group A (Of UserGroupType 2), Group B (Of UserGroupType 3), 
                  Group C (Of UserGroupType 4), Group D (Of UsergroupType 4)
Accomplishment 2:
   User: John
      Usergroups: Group A (Of UserGroupType 2), Group B (Of UserGroupType 3), 
                  Group C (Of UserGroupType 4), Group D (Of UsergroupType 4)
Accomplishment 3:
   User: Sue
      Usergroups: Group A (Of UserGroupType 2)

现在我上面的加入结果是 4 行:

-------(图 A):

 Accomplishment 1, John, Group A, Group C
 Accomplishment 1, John, Group A, Group D
 Accomplishment 2, John, Group A, Group C
 Accomplishment 2, John, Group A, Group D

现在这是正确的,因为用户拥有的每个 userGroup 都有一行 usergrouptype.localid 为 4,但是,我怎样才能使它也显示 Accomplishment 3?我首先从所有的连接都是内部连接开始,然后认为制作最后几个左连接会照顾它以返回用户,即使它没有用户组类型 localid 4 的用户组,但它没有。基本上我希望它返回 A 组中用户的任何成就,如果他们有任何用户组类型为 4 的用户组,也返回所有这些用户组。

有什么想法吗?如果我不清楚,请告诉我,这非常复杂,我可能解释得不够充分。

编辑:

(要快速回顾一下,请参阅标题为数据的部分。只能在图 A 和图 B 中得到结果,我想要的是期望的结果,尝试了答案部分中发布的所有内容。)

HLGEM 和 Tom H.s 的结果都是相同的,并且更接近我需要的结果,但仍然不符合要求。现在我在图 B 中得到 9 个结果。

-------(图 B):

Accomplishment 1, John, Group A, Group C
Accomplishment 1, John, Group A, Group D
Accomplishment 1, John, Group A, Null
Accomplishment 1, John, Group A, Group B (Group B is UsergroupType 3)
Accomplishment 3, Sue, Group A, Null
Accomplishment 2, John, Group A, Group C
Accomplishment 2, John, Group A, Group D
Accomplishment 2, John, Group A, Null
Accomplishment 2, John, Group A, Group B (Group B is UsergroupType 3)

因此,出于某种原因,它为 John 包含了一个空行,即使他有用户组类型为 4 的用户组,并且它包含一个用户组类型为 3 且不应显示的组 B 的行。

再次编辑:

(要快速回顾一下,请参阅标题为数据的部分。只能在图 A 和图 B 中得到结果,我想要的是期望的结果,尝试了答案部分中发布的所有内容。)

是的,我真的被这个难住了,我已经尝试了几乎所有在 where 子句中添加一些东西的组合,它要么是 4 行没有成就 3,要么是 9 行,其中用户 John 每个成就获得两行额外的行,或者6 行没有完成 3 和用户组类型 3 用户组的额外行。更令人费解的是,将ugto.localid = 4 添加到最后一行似乎对结果没有任何影响。它显示了我的用户组,它的用户组类型为 3,但我在查询中看不到任何可以的地方。

编辑 5/02/10

(要快速回顾一下,请参阅标题为数据的部分。只能在图 A 和图 B 中得到结果,我想要的是期望的结果,尝试了答案部分中发布的所有内容。)

我认为我希望我的设置是不可能的??无论如何,我想要的结果是这 5 行:

--------(期望的结果):

Accomplishment 1, John, Group A, Group C
Accomplishment 1, John, Group A, Group D
Accomplishment 2, John, Group A, Group C
Accomplishment 2, John, Group A, Group D
Accomplishment 3, Sue, Group A, Null

这是我尝试过的所有内容的列表以及它们给出的结果:(请原谅蹩脚的 SSMS 格式)

以下所有内容均基于此基本查询:

开始查询/尝试 1:

SELECT     TOP (100) PERCENT a.Id, a.DateCreated, a.DateModified, a.LastUpdatedBy, a.AccomplishmentTypeId, a.Title, a.Description, a.ApplicableDate, 
                  a.LocalId, at.Name AS AccomplishmentTypeName, al.Name AS AreaName, u.FirstName, u.LastName, ug.Name AS UserGroupName, 
                  ugo.Name AS OtherUsergroupName
  FROM         dbo.Accomplishment AS a INNER JOIN
                  dbo.AccomplishmentType AS at ON at.Id = a.AccomplishmentTypeId INNER    JOIN
                  dbo.AccomplishmentArea AS aal ON aal.AccomplishmentId = a.Id INNER JOIN
                  dbo.Area AS al ON al.Id = aal.AreaId INNER JOIN
                  dbo.UserAccomplishment AS ua ON ua.AccomplishmentId = a.Id INNER JOIN
                  dbo.[User] AS u ON u.Id = ua.UserId INNER JOIN
                  dbo.UserUserGroup AS uug ON uug.UserId = u.Id INNER JOIN
                  dbo.UserGroup AS ug ON ug.Id = uug.UserGroupId INNER JOIN
                  dbo.UserGroupType AS ugt ON ugt.Id = ug.UserGroupTypeId INNER JOIN
                  dbo.UserUserGroup AS uugo ON uugo.UserId = u.Id LEFT OUTER JOIN
                  dbo.UserGroup AS ugo ON ugo.Id = uugo.UserGroupId AND ugo.LocalId <> 2 LEFT OUTER JOIN
                  dbo.UserGroupType AS ugto ON ugto.Id = ugo.UserGroupTypeId AND ugto.LocalId = 4
 WHERE     (ug.LocalId = 2)
 ORDER BY a.DateCreated DESC, u.LastName, u.FirstName, u.Id, UserGroupName

结果: 图B(见上图) 为什么不好: John 的 Nulls 出现了,并且类型 3 的用户组出现了。

尝试 2:

尝试 1,减去所有内联连接过滤,(仍然保留最后两个的左连接)和以下 where 语句:

WHERE (ug.LocalId = 2)
  AND (ugo.LocalId <> 2 OR ugo.LocalId is null)
  AND (ugto.LocalId = 4 OR ugto.LocalId is null) 

结果: 图A(见上图) 为什么不好:不包括成就3

尝试 3:

与尝试 1 相同,更改从以下行开始:

 dbo.[User] AS u ON u.Id = ua.UserId INNER JOIN

该行下的更改:

                  dbo.UserUserGroup AS uug ON uug.UserId = u.Id INNER JOIN
                  dbo.UserGroup AS ug ON ug.Id = uug.UserGroupId AND ug.LocalId = 2 INNER JOIN
                  dbo.UserGroupType AS ugt ON ugt.Id = ug.UserGroupTypeId INNER JOIN
                  dbo.UserUserGroup AS uugo ON uugo.UserId = u.Id LEFT OUTER JOIN
                  dbo.UserGroup AS ugo ON ugo.Id = uugo.UserGroupId AND ugo.LocalId <> 2 LEFT OUTER JOIN
                  dbo.UserGroupType AS ugto ON ugto.Id = ugo.UserGroupTypeId
WHERE     (ugto.LocalId = 4)
ORDER BY a.DateCreated DESC, u.LastName, u.FirstName, u.Id, UserGroupName

(要快速回顾一下,请参阅标题为数据的部分。只能在图 A 和图 B 中得到结果,我想要的是期望的结果,尝试了答案部分中发布的所有内容。)

【问题讨论】:

  • 我在这里看到的最大问题是 SQL 格式不佳。我几乎可以保证,如果你把它格式化得更好,你会更容易让它正常工作。
  • 不知道你的意思,我的意思不是我如何输入它,但是SQL管理工作室在我执行SQL时将它转换为这种格式,但我这样阅读它也没有问题。你会如何格式化它?
  • 我从不喜欢 SSMS 自己格式化 SQL 的方式。将 JOIN 关键字放在行尾对我来说毫无意义。它们不适用于所在行的条件。

标签: sql left-join inner-join join


【解决方案1】:

@ChaosPandion 关于重新格式化的评论是个好主意,但也许您需要一些帮助来理解什么是好的格式化。好吧,每个人的情况可能都不一样 :-) 但如果我正在编写您的查询,我会将其格式化如下:

SELECT  TOP (100) PERCENT
          a.Id,
          a.DateCreated,
          a.DateModified,
          a.LastUpdatedBy,
          a.AccomplishmentTypeId,
          a.Title,
          a.Description,
          a.ApplicableDate,  
          a.LocalId,
          at.Name AS AccomplishmentTypeName,
          a.Name AS AreaName,
          u.FirstName,
          u.LastName,
          ug.Name AS UserGroupName,  
          ugo.Name AS OtherUserGroupName 
FROM dbo.Accomplishment AS a
INNER JOIN dbo.AccomplishmentType AS at
  ON at.Id = a.AccomplishmentTypeId
INNER JOIN dbo.AccomplishmentArea AS aal
  ON aal.AccomplishmentId = a.Id
INNER JOIN dbo.Area AS al
  ON al.Id = aal.AreaId
INNER JOIN dbo.UserAccomplishment AS ua
  ON ua.AccomplishmentId = a.Id
INNER JOIN dbo.[User] AS u
  ON u.Id = ua.UserId
INNER JOIN dbo.UserUserGroup AS uug
  ON uug.UserId = u.Id
INNER JOIN dbo.UserGroup AS ug
  ON ug.Id = uug.UserGroupId
INNER JOIN dbo.UserGroupType AS ugt
  ON ugt.Id = ug.UserGroupTypeId
INNER JOIN dbo.UserUserGroup AS uugo
  ON uugo.UserId = u.Id
LEFT OUTER JOIN dbo.UserGroup AS ugo
  ON ugo.Id = uugo.UserGroupId
LEFT OUTER JOIN dbo.UserGroupType AS ugto
  ON ugto.Id = ugo.UserGroupTypeId 
WHERE ug.LocalId = 2 AND
      ugo.LocalId <> 2 AND
      ugto.LocalId = 4
ORDER BY a.DateCreated DESC,
         u.LastName,
         u.FirstName,
         u.Id,
         UserGroupName

我认为这种格式更易于阅读。

分享和享受。

【讨论】:

  • 谢谢鲍勃,我会记住这一点,因为我同意这更容易,我刚刚习惯了我的上述格式,因为 SQL 管理工作室每次都将其转换为上述格式,所以我只是习惯了给它。要是能关掉自动格式化就好了><.>
  • 我类似地缩进,除了我通常缩进 JOIN 行以便 FROM 和 WHERE 弹出。我还将 ON 放在与连接表相同的行上,以便轻松查看使用的表列表。
【解决方案2】:

你在学习左连接时犯了一个典型的错误,你把条件放在 where 子句中,把它们变成内连接

试试这个

SELECT     a.Id, a.DateCreated, a.DateModified, a.LastUpdatedBy, a.AccomplishmentTypeId, a.Title,
a.Description, a.ApplicableDate,  a.LocalId, at.Name AS AccomplishmentTypeName, 
a.Name AS AreaName, u.FirstName, u.LastName, ug.Name AS UserGroupName, ugo.Name AS OtherUserGroupName 
FROM        dbo.Accomplishment AS a 
INNER JOIN  dbo.AccomplishmentType AS at ON at.Id = a.AccomplishmentTypeId 
INNER JOIN  dbo.AccomplishmentArea AS aal ON aal.AccomplishmentId = a.Id 
INNER JOIN  dbo.Area AS al ON al.Id = aal.AreaId 
INNER JOIN  dbo.UserAccomplishment AS ua ON ua.AccomplishmentId = a.Id 
INNER JOIN  dbo.[User] AS u ON u.Id = ua.UserId 
INNER JOIN  dbo.UserUserGroup AS uug ON uug.UserId = u.Id 
INNER JOIN  dbo.UserGroup AS ug ON ug.Id = uug.UserGroupId 
INNER JOIN  dbo.UserGroupType AS ugt ON ugt.Id = ug.UserGroupTypeId 
INNER JOIN  dbo.UserUserGroup AS uugo ON uugo.UserId = u.Id 
LEFT OUTER JOIN  dbo.UserGroup AS ugo ON ugo.Id = uugo.UserGroupId  AND ugo.LocalId <> 2
LEFT OUTER JOIN  dbo.UserGroupType AS ugto ON ugto.Id = ugo.UserGroupTypeId  AND ugto.LocalId = 4
WHERE     ug.LocalId = 2 
ORDER BY a.DateCreated DESC, u.LastName, u.FirstName, u.Id, UserGroupName 

请参阅此链接以获取对此的解释: http://wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN

【讨论】:

  • 与 Tom H. 的答案相同的结果,9 行,而不是 5 行。完整解释请参见评论。
【解决方案3】:

我不确定我是否完全理解您要执行的操作,但我认为您需要将您的条件移到 LEFT OUTER JOIN 中。如果将它放在 WHERE 子句中,那么如果 LEFT OUTER JOIN 未找到匹配项,这些列将显示为 NULL,因此它们将无法通过 WHERE 子句检查。您实际上是将 LEFT OUTER JOIN 变成了 INNER JOIN。

FROM
    dbo.Accomplishment AS a
INNER JOIN dbo.AccomplishmentType AS at ON
    at.Id = a.AccomplishmentTypeId
INNER JOIN dbo.AccomplishmentArea AS aal ON
    aal.AccomplishmentId = a.Id
INNER JOIN dbo.Area AS al ON
    al.Id = aal.AreaId
INNER JOIN dbo.UserAccomplishment AS ua ON
    ua.AccomplishmentId = a.Id
INNER JOIN dbo.[User] AS u ON
    u.Id = ua.UserId
INNER JOIN dbo.UserUserGroup AS uug ON
    uug.UserId = u.Id
INNER JOIN dbo.UserGroup AS ug ON
    ug.Id = uug.UserGroupId AND
    ug.localid = 2
INNER JOIN dbo.UserGroupType AS ugt ON
    ugt.Id = ug.UserGroupTypeId
INNER JOIN dbo.UserUserGroup AS uugo ON
    uugo.UserId = u.Id
LEFT OUTER JOIN dbo.UserGroup AS ugo ON
    ugo.Id = uugo.UserGroupId AND
    ugo.localid <> 2
LEFT OUTER JOIN dbo.UserGroupType AS ugto ON
    ugto.Id = ugo.UserGroupTypeId
WHERE
    ugto.localid = 4
ORDER BY
    a.DateCreated DESC, u.LastName, u.FirstName, u.Id, UserGroupName

【讨论】:

  • 这非常接近,我现在得到 9 个结果,但我应该得到 5 个结果。它现在包括成就 3,但它还包括成就 1 和 2 的额外行。有组 C 和 D(在我的示例中)为空,并且该列显示的是用户组类型 3 的组(不应该显示)。还有什么建议吗?
  • 好的,我想我知道你在找什么了。您的一些标准应该在 JOIN 中,而有些则不应该。我不知道我是否准确地捕捉到了你正在寻找的东西,但也许它更接近。如果您提供(简化的)脚本来创建表和插入示例数据,这可能会有所帮助。
  • 啊,编辑后的版本现在又回到了 4 行,不包括成就 3。该死,这令人沮丧
【解决方案4】:

这是我到目前为止重写的内容:

SELECT a.Id, a.DateCreated, a.DateModified, a.LastUpdatedBy, a.AccomplishmentTypeId, 
       a.Title, a.Description, a.ApplicableDate, a.LocalId, 
       at.Name AS AccomplishmentTypeName, a.Name AS AreaName, u.FirstName, 
       u.LastName, ug.Name AS UserGroupName, ugo.Name AS OtherUserGroupName
  FROM dbo.Accomplishment a 
  JOIN dbo.AccomplishmentType AS at ON at.Id = a.AccomplishmentTypeId 
  JOIN dbo.AccomplishmentArea AS aal ON aal.AccomplishmentId = a.Id 
  JOIN dbo.Area AS al ON al.Id = aal.AreaId 
  JOIN dbo.UserAccomplishment AS ua ON ua.AccomplishmentId = a.Id 
  JOIN dbo.[User] AS u ON u.Id = ua.UserId 
  JOIN dbo.UserUserGroup AS uug ON uug.UserId = u.Id 
  JOIN dbo.UserGroupType AS ugt ON ugt.Id = ug.UserGroupTypeId 
  JOIN dbo.UserGroup AS ug ON ug.Id = uug.UserGroupId 
  JOIN dbo.UserGroupType AS ugto ON ugto.Id = ug.UserGroupTypeId
 ORDER BY a.DateCreated DESC, u.LastName, u.FirstName, u.Id, UserGroupName

您有一个与UserUserGroup 的冗余联接,并且您与UserGroup 的联接可以合并,因为 LEFT JOIN 条件将取消在先前联接到同一个表中所做的操作。

【讨论】:

  • 这行不通,因为那时用户组只有一列,但我有不同的用户组,根据类型的不同表示不同的东西,在选择中,.ugo 现在什么都不是。
  • 您还需要第二个 UserUserGroup,否则正如您所说它确实取消了它,但使用第二个 UserUserGroup,它不会。
【解决方案5】:

你的加入都很好。是过滤!

如果您编写的过滤器不允许左连接表中出现空值,那么您的过滤器将删除左连接允许的其他记录。

ugo 和 ugto 都通过 LEFT JOIN 到达

WHERE (ug.LocalId = 2)
  AND (ugo.LocalId <> 2 OR ugo.LocalId is null)
  AND (ugto.LocalId = 4 OR ugto.LocalId is null) 

PS - 混合 JOIN 和 FILTERING 标准(两者都在 ON 或两者都在 WHERE)会导致精神错乱。保持清醒!


编辑:在连接中发现错误:

INNER JOIN dbo.UserUserGroup AS uugo ON uugo.UserId = u.Id

应该是

LEFT JOIN dbo.UserUserGroup AS uugo ON uugo.UserId = u.Id

【讨论】:

  • 这仍然返回 4 个结果,(就像在我的帖子中一样)。但我需要 5 个。我想要所有拥有用户组“A 组”中的用户的成就,然后如果是,则还返回用户所在的任何其他用户组,usergrouptype.localid = 4 并将其粘贴到另一个用户组列。问题是,要包含 Sue 的成就 3,我需要能够允许 OtherUserGroup 列为空,因为 Sue 没有 usergrouptype.localid = 4 的用户组,但仍然有用户组 A,所以它应该在那里,但是我为返回成就 3 所做的每个查询都返回 9 行。
猜你喜欢
  • 1970-01-01
  • 2020-04-30
  • 1970-01-01
  • 1970-01-01
  • 2011-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多