我有用户,职位和许可。
关系是:
因此,我可以轻松获得每个职位的许可证要求以及每个用户的有效许可证。
但我想知道匹配两个集的最佳方法是什么?逻辑上,用户至少需要某个位置所需的许可证。可能有更多,但剩下的不相关。
我希望得到用户和符合条件的排名结果。
PersonID PositionID
1 1 -> user 1 is eligible to work on position 1
1 2 -> user 1 is eligible to work on position 2
2 1 -> user 2 is eligible to work on position 1
3 2 -> user 3 is eligible to work on position 2
4 ...
正如您所看到的,我需要为所有用户提供结果,而不是每个用户只需一个结果,这会使事情变得更加容易。
这里实际上有5个表:
create table Person ( PersonID, ...)
create table Position (PositionID, ...)
create table License (LicenseID, ...)
和关系
create table PersonLicense (PersonID, LicenseID, ...)
create table PositionLicense (PositionID, LicenseID, ...)
所以基本上我需要找到某个人获得许可的职位。当然这里有一个更复杂的问题,因为还有其他因素,但主要目标是相同的:
如何将一个关系表的多个记录与另一个关系表的多个记录进行匹配。这也可以描述为每组记录inner join
而不是每个记录为它通常在TSQL中完成。
我正在考虑TSQL语言结构:
intersect
语句可能虽然这些语句可能只适用于整个集合而不是组答案 0 :(得分:4)
与此同时,当你的开发人员回答我的问题时,这是我提出的并使用CTE和分区,当然可以在SQL Server 2008 R2上使用。我之前从未使用过结果分区,所以我不得不学习一些新的东西(这完全是一个加号)。这是代码:
with CTEPositionLicense as (
select
PositionID,
LicenseID,
checksum_agg(LicenseID) over (partition by PositionID) as RequiredHash
from PositionLicense
)
select per.PersonID, pos.PositionID
from CTEPositionLicense pos
join PersonLicense per
on (per.LicenseID = pos.LicenseID)
group by pos.PositionID, pos.RequiredHash, per.PersonID
having pos.RequiredHash = checksum_agg(per.LicenseID)
order by per.PersonID, pos.PositionID;
所以我对这三种技术进行了比较,我将其命名为:
我已经按人数和位置订购了结果,所以我也将其添加到其他两个,以便返回相同的结果。
我还将表变量版本更改为CTE版本(而不是表变量使用了CTE),最后删除了order by
并比较了它们的估计执行计划。仅供参考CTE版本43%,而原始版本有53%(10%+ 43%)。
答案 1 :(得分:2)
有效写这个的一种方法是在LicenceId上将PositionLicences与PersonLicences连接起来。然后计算按位置和人员分组的非空值,并与所有位置许可证的计数进行比较 - 如果等于该人员符合条件:
DECLARE @tmp TABLE(PositionId INT, LicenseCount INT)
INSERT INTO @tmp
SELECT PositionId as PositionId
COUNT(1) as LicenseCount
FROM PositionLicense
GROUP BY PositionId
SELECT per.PersonID, pos.PositionId
FROM PositionLicense as pos
INNER JOIN PersonLicense as per ON (pos.LicenseId = per.LicenseId)
GROUP BY t.PositionID, t.PersonId
HAVING COUNT(1) = (
SELECT LicenceCount FROM @tmp WHERE PositionId = t.PositionID
)
答案 2 :(得分:1)
我会像这样解决问题:
从PersonLicense
获取所有(不同)用户。
与PositionLicense
交叉加入。
使用PersonLicense
和PersonID
左键加入包含LicenseID
的结果集。
按PersonID
和PositionID
对结果进行分组。
过滤掉(PersonID, PositionID)
对中PositionLicense
中的许可数与PersonLicense
中的许可数不匹配的SELECT
u.PersonID,
pl.PositionID
FROM (SELECT DISTINCT PersonID FROM PersonLicense) u
CROSS JOIN PositionLicense pl
LEFT JOIN PersonLicense ul ON u.PersonID = ul.PersonID
AND pl.LicenseID = ul.LicenseID
GROUP BY
u.PersonID,
pl.PositionID
HAVING COUNT(pl.LicenseID) = COUNT(ul.LicenseID)
对。
这是我的实施:
{{1}}