复杂SQL加入帮助!

时间:2010-07-01 18:04:25

标签: sql left-join inner-join join

(如需快速回顾,请参阅标题为DATA的章节。只能在图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个成就,如下所示:

-------(DATA)

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)

现在我的上面的Join结果有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

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

有什么想法?如果我不清楚请告诉我,这是非常复杂的,我可能没有足够的解释。

修改

(如需快速回顾,请参阅标题为DATA的章节。只能在图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的空行,即使他的用户组是usergrouptype 4,并且它包含一个B组的行,该行是usergrouptype 3并且不应该出现。

再次编辑:

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

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

编辑5/02/10

(如需快速回顾,请参阅标题为DATA的章节。只能在图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

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

5 个答案:

答案 0 :(得分:3)

@ 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

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

分享并享受。

答案 1 :(得分:1)

我不确定我是否完全理解你要做的事情,但我认为你需要将你的标准移到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

答案 2 :(得分:1)

在学习左连接时你犯了一个经典错误,你把条件放在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

答案 3 :(得分:0)

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

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条件会取消之前加入到同一个表中的内容。

答案 4 :(得分:0)

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

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

LEFT JOIN

达到了ugo和ugto
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