我还有一个EMPLOYEE表,它也有内置的层次结构(使用管理器列)
我是另一个表示经理 - 区域关系的REGION表
我尝试创建一个SQL,通过跟踪层次结构链来显示哪些员工属于哪个区域。
约束/规则:
我保证链上4级会有人有区域。
如果在第4级之前找到区域,则使用较低经理的区域
这是我提出的天真的SQL(但结果有重复 - 第3条规则失败)
select distinct e.name, r.region
from employee e
left outer join employee mgr1 on mgr1.id = e.manager
left outer join employee mgr2 on mgr2.id = mgr1.manager
left outer join employee mgr3 on mgr3.id = mgr2.manager
left outer join employee mgr4 on mgr4.id = mgr3.manager
left outer join REGION r on
( r.id = mgr1.id
or r.id = mgr2.id
or r.id = mgr3.id
or r.id = mgr4.id )
where e.IS_MANAGER = 'N'; //only interested in users for now; assume a flag
这是ResultSet:
如果我已找到某个区域,如何有条件地停止左外连接?
答案 0 :(得分:1)
试试这个:
select distinct e.name, COALESCE(r1.region, r2.region, r3.region, r4.region, 'No Region') region
from employee e left outer join
region r1 on e.manager = r1.id
left outer join employee mgr1 on mgr1.id = e.manager left outer join
region r2 on mgr1.manager = r2.id
left outer join employee mgr2 on mgr2.id = mgr1.manager left outer join
region r3 on mgr2.manager = r3.id
left outer join employee mgr3 on mgr3.id = mgr2.manager left outer join
region r4 on mgr3.manager = r4.id
where e.IS_MANAGER = 'N'; //only interested in users for now; assume a flag
我不确定所有mysql版本是否支持COALESCE函数,但你可以找到一个等价函数(它返回第一个非null参数)。
答案 1 :(得分:1)
我不得不稍微修改一下你的脚本,但这样做有效:
select distinct e.Name,
CASE
WHEN r1.RegionName IS NOT NULL THEN r1.RegionName
WHEN r2.RegionName IS NOT NULL THEN r2.RegionName
WHEN r3.RegionName IS NOT NULL THEN r3.RegionName
WHEN r4.RegionName IS NOT NULL THEN r4.RegionName
ELSE 'NA'
END AS 'RegionName'
from employee e
left outer join employee mgr1 on mgr1.id = e.Manager
left outer join employee mgr2 on mgr2.id = mgr1.Manager
left outer join employee mgr3 on mgr3.id = mgr2.Manager
left outer join employee mgr4 on mgr4.id = mgr3.Manager
left outer join Region r1 on r1.id = mgr1.RegionID
left outer join Region r2 on r2.id = mgr2.RegionID
left outer join Region r3 on r3.id = mgr3.RegionID
left outer join Region r4 on r4.id = mgr4.RegionID
where e.IS_MANAGER = 'N';
这里是SQL小提琴:http://sqlfiddle.com/#!9/93b45/5
答案 2 :(得分:0)
DB2(几乎所有版本)都支持递归CTE,它们用于处理这种分层数据(某些版本也支持Oracles CONNECT BY
,但我对此并不熟悉)。使用它可以使连接更容易推理:
WITH Employee_Region AS (SELECT name, manager, CAST(null AS VARCHAR(2)) AS region
FROM Employee
WHERE is_manager = 'N'
UNION ALL
SELECT ER.name, Manager.manager, Region.regionName
FROM Employee_Region ER
JOIN Employee Manager
ON Manager.id = ER.manager
LEFT JOIN Region
ON Region.id = Manager.regionId
WHERE ER.region IS NULL)
SELECT name, region
FROM Employee_Region
WHERE region IS NOT NULL
SQL Fiddle Example
(小提琴基础取自@PhilWalton - 谢谢!PostgreSQL需要RECURSIVE
关键字,但DB2不需要)
查询从底部开始(假设您有一个标志),但可以将其反转并从顶级经理开始。
对于递归部分(UNION ALL
之后CTE中的所有内容):
JOIN
到Employee
表以获得下一位经理LEFT JOIN
到该表。最后,在主查询中,我们排除员工没有区域的那些行。大多数情况下,这将删除生成的中间行,直到找到具有区域的管理器,但是如果某个树没有区域(不知何故),它将排除它们。
使用WHERE
几乎肯定比DISTINCT
所需的散列函数便宜,虽然我不确定递归部分会有什么影响(如果大多数直接管理者有一个区域,或者在一跳内,它可能比做四个连接表现更好)