简体   繁体   English

复杂的SQL Join帮助!

[英]Complex SQL Join help!

(For a quick recap please see sections titled , DATA. Can only get results in FIGURE A, and FIGURE B, what I want is DESIRED RESULTS, tried everything posted in the answers section.) (有关快速回顾,请参见标题为DATA的部分。只能在图A和图B中获得结果,我想要的是所需的结果,请尝试在答案部分中发布的所有内容。)

All, I have been working hard to start smartly using joins in my application. 总而言之,我一直在努力从我的应用程序中聪明地开始使用联接。 Boy it took me a while but I think I am getting the hang of this, but I can't get this query to work. 男孩花了我一段时间,但我想我已经掌握了这个主意,但是我无法使该查询正常工作。

Please help me! 请帮我! Here is the join as is: 这是按原样的联接:

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

Now this is kind of complex, but what I am doing is select info from all these related tables where there is an accomplishment, which has a user associated with it where that user has a usergroup localId = 2, but then if they do have that, I also want to return all the usergroups that user is in where the usertype.localid = 4. 现在,这有点复杂,但是我正在做的是从所有相关表中选择一项有成就的信息,其中有一个与之相关联的用户,而该用户的用户组的localId = 2,但是如果确实有,我还想返回该用户所在的所有用户组,其中usertype.localid = 4。

Let me give some example data to hopefully make this clear, right now I have 3 accomplishments in the database that look like this: 让我给出一些示例数据来希望使这一点变得清楚,现在我在数据库中有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)

Now my above Join results 4 rows : 现在我上面的Join结果4行:

-------(FIGURE A): -------(图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

Now this is correct insofar as there is a row for each userGroup the user has that has a usergrouptype.localid of 4, however, how can I make it so that it also displays Accomplishment 3?? 现在,这是正确的,因为该用户拥有的每个userGroup都有一行,其usergrouptype.localid为4,但是,如何使其显示完成3? I first started with all the joins being inner joins, and then thought making the last few left joins would take care of it to return the user even if it has no usergroups of usergrouptype localid 4, but it doesn't. 我首先将所有联接作为内部联接开始,然后认为进行最后的几个左联接将使它返回用户,即使它没有用户组类型为localid 4的用户组,也没有。 Basically I want it to return any accomplishments of a user who is in group A, and if they have any usergroups that are of usergrouptype 4, return all those usergroups as well. 基本上,我希望它返回组A中用户的任何成就,并且如果他们具有usergrouptype 4的任何用户组,则也返回所有这些用户组。

Any thoughts? 有什么想法吗? And if I am not being clear please let me know, this is super complex and I may not have explained enough. 如果我不清楚,请告诉我,这太复杂了,我可能还没有足够的解释。

EDIT: 编辑:

(For a quick recap please see sections titled , DATA. Can only get results in FIGURE A, and FIGURE B, what I want is DESIRED RESULTS, tried everything posted in the answers section.) (有关快速回顾,请参见标题为DATA的部分。只能在图A和图B中获得结果,我想要的是所需的结果,请尝试在答案部分中发布的所有内容。)

Both HLGEM and Tom Hs results are the same, and closer to what I need, but still off. HLGEM和Tom Hs的结果都相同,虽然更接近我的需求,但仍然相差甚远。 Now I get 9 results in Figure B. 现在,在图B中我得到9个结果。

-------(FIGURE B): -------(图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)

So for some reason it's including a null row for John even though he has usergroups that are of usergrouptype 4, and its including a row with group B which is of usergrouptype 3 and shouldn't be showing up. 因此,出于某种原因,即使John的用户组的用户组类型为4,该行也包含空行,并且其中包含组B的行的用户组类型为3,因此不应显示该行。

EDIT AGAIN: 再次编辑:

(For a quick recap please see sections titled , DATA. Can only get results in FIGURE A, and FIGURE B, what I want is DESIRED RESULTS, tried everything posted in the answers section.) (有关快速回顾,请参见标题为DATA的部分。只能在图A和图B中获得结果,我想要的是所需的结果,请尝试在答案部分中发布的所有内容。)

Yep I am truely stumped by this, I have tried pretty much every combination of putting some stuff in the where clause and it either is 4 rows without accomplishment 3, or 9 rows where user John gets two extra rows per accomplishment, or 6 Rows without accomplish 3 and an extra row for usergrouptype 3 usergroup. 是的,我确实为此感到困惑,我已经尝试了将东西放在where子句中的几乎每一种组合,它要么是4行没有成就3,要么是9行,其中用户John每个成就又获得了2行,或者6行没有完成3和usergrouptype 3用户组的额外行。 What's even more baffling is that adding the ugto.localid = 4 to the last line doesn't seem to have any affect on the results. 更令人困惑的是,在最后一行添加ugto.localid = 4似乎对结果没有任何影响。 It's showing my a usergroup which has usergrouptype 3 and I don't see anywhere in the query where that is okay. 它向我显示了一个用户组,该用户组的用户组类型为3,在查询的任何地方都看不到。

EDIT 5/02/10 编辑5/02/10

(For a quick recap please see sections titled , DATA. Can only get results in FIGURE A, and FIGURE B, what I want is DESIRED RESULTS, tried everything posted in the answers section.) (有关快速回顾,请参见标题为DATA的部分。只能在图A和图B中获得结果,我想要的是所需的结果,请尝试在答案部分中发布的所有内容。)

I think what I want my be impossible with how it's set up?? 我认为我希望它的设置是不可能的吗? Anyway the result I want is these 5 rows: 无论如何,我想要的结果是这5行:

-------(DESIRED RESULT): -------(所需结果):

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

Here is a list of everything I have tried and the results they give: (Please forgive the crappy SSMS formatting) 这是我尝试过的所有内容及其提供的结果的列表:(请原谅糟糕的SSMS格式)

Everything below is based on this base query: 以下所有内容均基于此基本查询:

Starting Query/Attempt 1: 开始查询/尝试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

Result: Figure B (See way above) Why it's bad: Nulls for John show up and usergroup of type 3 is showing up. 结果: 图B (请参见上面的方法) 为什么不好:出现 John的Null并显示类型3的用户组。

Attempt 2: 尝试2:

Attempt 1, minus all in-line join filtering, (still keeping left joins on the last two) and the following where statement: 尝试1,减去所有的内联联接筛选(仍然在最后两个联接上保持左联接)和以下where语句:

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

Result: Figure A (See way above) Why it's bad: Doesn't include Accomplishment 3 结果: 图A (请参见上面的方法) 不好的原因:不包括成就3

Attempt 3: 尝试3:

Same as attempt 1 with changes starting underneath this line: 与尝试1相同,但在此行下开始更改:

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

Changes under that line: 在该行下的更改:

                  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

(For a quick recap please see sections titled , DATA. Can only get results in FIGURE A, and FIGURE B, what I want is DESIRED RESULTS, tried everything posted in the answers section.) (有关快速回顾,请参见标题为DATA的部分。只能在图A和图B中获得结果,我想要的是所需的结果,请尝试在答案部分中发布的所有内容。)

@ChaosPandion's comment about reformatting is a good idea, but perhaps you need some help understanding what good formatting is. @ChaosPandion关于重新格式化的评论是一个好主意,但是也许您需要一些帮助来了解什么是好的格式。 Well, it's probably different for everyone :-) but if I was writing your query I'd format it as follows: 好吧,对于每个人来说,它可能都不同:-),但是如果我正在编写查询,我将其格式设置如下:

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

I think this formatting makes it easier to read. 我认为这种格式使其更易于阅读。

Share and enjoy. 分享并享受。

I'm not sure that I completely understand what you're trying to do, but I think that you need to move your criteria into the LEFT OUTER JOINs. 我不确定我是否完全了解您要执行的操作,但是我认为您需要将标准移至“左外部联接”中。 If you put it in the WHERE clause then if no match is found by the LEFT OUTER JOIN, those columns will appear as NULL and so they will fail the WHERE clause check. 如果将其放在WHERE子句中,则如果LEFT OUTER JOIN未找到匹配项,则这些列将显示为NULL,因此它们将无法通过WHERE子句检查。 You are in effect turning the LEFT OUTER JOIN into an 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

You have made a classic mistake when learning left joins, you put conditions in the where clause that turn them into inner joins 在学习左联接时,您犯了一个经典错误,您在where子句中放置了将其变为内部联接的条件

Try this 尝试这个

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 

see this link for an explanation of this: http://wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN 请参阅此链接以获取对此的解释: http : //wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN

Here's what I re-wrote so far: 到目前为止,这是我重写的内容:

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

You've got a redundant join to UserUserGroup , and your joins to UserGroup can be consolidated because the LEFT JOIN criteria would cancel out what's done in the previous join to the same table. 您有一个到UserUserGroup的多余UserUserGroup ,并且可以合并到UserGroup的联接,因为LEFT JOIN条件将取消上一次联接到同一表的操作。

You JOINs are all fine. 您的联接都很好。 It's the filtering! 这是过滤!

If you write a filter that does not allow nulls in left joined tables, then your filter will remove the additional records that the left joins allow. 如果编写的过滤器不允许左连接表中的空值,那么您的过滤器将删除左连接允许的其他记录。

ugo and ugto are both reached by LEFT JOIN 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 - mixing JOIN and FILTERING criteria (both in ON or both in WHERE) is a recipe for insanity. PS-混合JOIN和FILTERING标准(在ON或在WHERE中都使用)是导致精神错乱的秘诀。 Stay sane! 保持理智!


Edit: found an error in the join: 编辑:在连接中发现错误:

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

Should be 应该

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM