qua*_*ant 5 t-sql sql-server dax ssas-tabular powerbi
假设我有一张表描述每个工作人员的主要和次要报告项目。让我们想象一下一个组织结构,其中CEO(雇员0
)有2位经理(1
和2
)向他报告。
经理2
在其团队(3
和4
)中有2名员工,但是该员工4
实际上在经理所在1
的时区工作,因此,虽然他2
是其主要报告,但他也向经理1
报告是次要报告,以便1
能够履行正常的受托管理义务(提供支持等)。
除了为员工担任二级管理角色外4
,经理2
还拥有一个团队成员向其报告(5
)。
编辑:为了说明多父母问题,让我们给团队成员4
实习生,工作人员6
。团队成员6
现下属两个经理1
和2
-后者是通过二级报告线继承。
组织结构如下所示:
+--+-------+---------+
|ID|Primary|Secondary|
|0 |NULL |NULL |
|1 |0 |NULL |
|2 |0 |NULL |
|3 |1 |NULL |
|4 |1 |2 |
|5 |2 |NULL |
|6 |4 |NULL |
+--+-------+---------+
Run Code Online (Sandbox Code Playgroud)
现在,我想将其扩展到一个SQL视图中,该视图为我提供了一个在任何给定工作人员以下的人员列表,涵盖了主要报告和辅助报告。因此,对于工作人员2
(具有主要和辅助报告的经理),我希望看到团队成员4
和5
,对于CEO(0
),我希望看到除CEO之外的其他工作人员。我们新来的实习生,6
是的CEO,管理者的下属1
和2
,以及他的直接经理4
。
看起来像这样:
+--+-----------+
|ID|Subordinate|
|0 |1 |
|0 |2 |
|0 |3 |
|0 |4 |
|0 |5 |
|0 |6 |
|1 |3 |
|1 |4 |
|1 |6 |
|2 |4 |
|2 |5 |
|2 |6 |
|4 |6 |
+--+-----------+
Run Code Online (Sandbox Code Playgroud)
如何在SQL中实现呢?我正在考虑OUTER APPLY
对ID进行某种操作,但正在努力解决解决此问题(我认为)需要的可重入性。我的背景是过程编程,我认为这是我在这里苦苦挣扎的部分原因。
NB:我想在这里预料到一个明显的问题:“这肯定是XY问题-你到底为什么要这样做?”
我想在PowerBI中使用行级安全性,以使每个工作人员可以访问有关组织结构中位于其下方的个人的某些信息。不幸的是,RLS不允许每个人执行存储过程,因此我坚持进行这种组合扩展,然后仅基于登录名过滤上表。
话虽如此,我对解决这个问题的更好方法持开放态度。
要在 SQL 中获得所需的结果,最简单的方法是使用递归 CTE。
在下面的示例中,我将工作分为两个 CTE。第一个将集合转变为经理和下属对。第二个 CTE 获取第一个 CTE 的所有结果,然后使用 UNION ALL 连接到自身,其中第一个 CTE 的管理器是递归 CTE 中的下级。这将不断重复,直到没有可以进行的匹配。
由于下属可能有多个经理,因此可能会为每个祖先返回重复的行。因此,从递归 CTE 返回结果时使用 DISTINCT。
WITH all_reports AS (
SELECT [Primary] [ManagerID], ID [Subordinate]
FROM tbl
WHERE [Primary] IS NOT NULL
UNION
SELECT [Secondary], ID
FROM tbl
WHERE [Secondary] IS NOT NULL
)
, recursive_cte AS (
SELECT ManagerID, Subordinate
FROM all_reports
UNION ALL
SELECT ancestor.ManagerID, descendant.Subordinate
FROM recursive_cte ancestor
INNER JOIN all_reports descendant ON descendant.ManagerID = ancestor.Subordinate
)
SELECT DISTINCT ManagerID, Subordinate
FROM recursive_cte
Run Code Online (Sandbox Code Playgroud)
如果您想要经理和下属之间的距离,请重写递归 CTE,如下所示:
SELECT ManagerID, Subordinate, 1 [Distance]
FROM all_reports
UNION ALL
SELECT ancestor.ManagerID, descendant.Subordinate, ancestor.Distance + 1
FROM recursive_cte ancestor
INNER JOIN all_reports descendant ON descendant.ManagerID = ancestor.Subordinate
Run Code Online (Sandbox Code Playgroud)