在两个结果集中查找“缺失”行

时间:2013-11-10 17:44:39

标签: sql-server tsql sql-server-2012

如果我过度混淆了一些事情,我几天都在为此苦苦挣扎......

我有一系列定义的表:

  1. 技能组
  2. 技能
  3. 上述群体中的技能(多对多)
  4. 工作角色
  5. 具有这些工作角色的技能(多对多)
  6. 数据库的结构如下所示: Schema

    我需要创建一个显示以下数据的结果集:

      

    对于所有工作角色,展示这些工作中的所有技能   角色。但是,也包括各自组内的技能   在每个角色中不包括(用NULL或任何表示   其他方法)。

    请参阅此工作代码以创建表和数据。 SQL Fiddle
    抱歉,我做了很多插入来创建一个真实的例子。

    请注意,PowerPoint技能未添加到HR Manager角色,但会添加来自同一组的其他技能。另请注意Recruitment Policy技能未添加到Software Manager角色,但我不需要看到这个差距,因为该角色中不存在该组中的其他技能。

    我的目标结果与此类似(为简洁起见,不包括超级明星角色):

    RoleTitle               GroupTitle                 SkillTitle               SkillIsInRole
    ----------------------- -------------------------- --------------------------------------
    Software Manager        Microsoft Office           Excel                    1
    Software Manager        Microsoft Office           Word                     1
    Software Manager        Microsoft Office           PowerPoint               1
    Software Manager        Microsoft SQL Server       Query Design             1
    Software Manager        Microsoft SQL Server       Stored Procedures        1
    Software Manager        Microsoft SQL Server       Failover Clustering      1
    HR Manager              Microsoft Office           Excel                    1
    HR Manager              Microsoft Office           Word                     1
    HR Manager              Microsoft Office           PowerPoint               NULL <-- not added to role but exists in same group as other used skills
    HR Manager              HR                         Recruitment Policy       1
    

2 个答案:

答案 0 :(得分:3)

获取与角色相关的组的所有技能有点简单,并在下面相对自我解释的roles cte中处理。从这个角度来看,我能想到获得技能是否与角色“直接”相关的唯一方法是OUTER APPLY ING将结果集设置为角色的实际技能结果集。

;WITH skills AS
(
  SELECT g.GroupId, g.GroupTitle, s.SkillId, s.SkillTitle
  FROM @tbl_GroupsSkills gs
  INNER JOIN @tbl_Groups g ON g.GroupId = gs.GroupId
  INNER JOIN @tbl_Skills s ON s.SkillId = gs.SkillId
)
, roles AS
(
  SELECT DISTINCT jr.Id RoleId, jr.RoleTitle, gs.GroupId
  FROM @tbl_jobroles jr
  INNER JOIN @tbl_rolesskills rs ON rs.RoleId = jr.ID
  INNER JOIN @tbl_GroupsSkills gs ON gs.LinkId = rs.LinkId
)
SELECT 
    roles.RoleTitle, 
    skills.GroupTitle, 
    skills.SkillTitle,
    t.SkillIsInRole
FROM skills
JOIN roles ON roles.GroupId = skills.GroupId
OUTER APPLY
(
  SELECT 1 SkillIsInRole
  FROM @tbl_rolesskills rs 
  INNER JOIN @tbl_jobroles r ON rs.RoleID = r.ID 
  INNER JOIN @tbl_groupsskills gs ON gs.LinkID = rs.LinkID 
  INNER JOIN @tbl_groups g ON g.groupID = gs.GroupID 
  INNER JOIN @tbl_skills s ON s.skillID = gs.SkillID
  WHERE s.SkillId = skills.SkillId
  AND g.GroupId = skills.GroupId
  AND r.Id = roles.RoleId
) t
ORDER BY roles.RoleTitle, skills.GroupTitle, skills.SkillTitle

修改:可以使用OUTER APPLY

处理LEFT JOIN
LEFT JOIN (
  SELECT s.SkillId, g.GroupId, r.Id RoleId, 1 SkillIsInRole
  FROM @tbl_rolesskills rs 
  INNER JOIN @tbl_jobroles r ON rs.RoleID = r.ID 
  INNER JOIN @tbl_groupsskills gs ON gs.LinkID = rs.LinkID 
  INNER JOIN @tbl_groups g ON g.groupID = gs.GroupID 
  INNER JOIN @tbl_skills s ON s.skillID = gs.SkillID
) t ON t.SkillId = skills.SkillId
   AND t.GroupId = skills.GroupId
   AND t.RoleId = roles.RoleId

demo

答案 1 :(得分:1)

我认为你必须从一方准备一组所有可能的组合角色,从另一方面准备组技能。这是通过进行Decart乘法(通常由CROSS JOIN完成)来完成的。因此,您将获得每个角色的列表以及所有可能的群组技能组合的列表。之后,您可以使用表tbl_RolesSkills LEFT JOIN此结果。这将满足您的需求。您可以使用CTE或子查询来完成此操作。

必须看起来像this example

实际上不需要CROSS JOIN,我错过了“特定角色只有特定的群组”。只有一个子查询,也可以用CTE(公用表表达式)完成。

我也排除了“超级明星”角色。如果你想添加它,只需删除WHERE部分。