Tsql 获取与另一个表中的所有记录匹配的记录
在这个问题的范围内,我有3个实体:Tsql 获取与另一个表中的所有记录匹配的记录,tsql,sql-server-2008-r2,Tsql,Sql Server 2008 R2,在这个问题的范围内,我有3个实体: 用户 位置 许可证 然后我有两个关系(多对多)表: PositionLicense-这一个连接了Position和License即特定职位需要哪些许可证 UserLicense-此选项将User与License连接起来,即特定用户拥有的许可证。但还有一个额外的复杂性:用户许可证具有有效日期范围(ValidFrom和ValidTo) 问题 这些是输入变量: 识别特定用户的UserID RangeFrom定义日期范围下限 RangeTo定义了日期范围上限 我需
用户
位置
许可证
PositionLicense
-这一个连接了Position
和License
即特定职位需要哪些许可证UserLicense
-此选项将User
与License
连接起来,即特定用户拥有的许可证。但还有一个额外的复杂性:用户许可证具有有效日期范围(ValidFrom
和ValidTo
)- 识别特定
用户的
UserID
定义日期范围下限RangeFrom
定义了日期范围上限RangeTo
create table [User] (
UserID int identity not null
primary key,
Name nvarchar(100) not null
)
go
create table Position (
PositionID int identity not null
primary key,
Name nvarchar(100) not null
)
go
create table License (
LicenseID int identity not null
primary key,
Name nvarchar(100) not null
)
go
create table UserLicense (
UserID int not null
references [User](UserID),
LicenseID int not null
references License(LicenseID),
ValidFrom date not null,
ValidTo date not null,
check (ValidFrom < ValidTo),
primary key (UserID, LicenseID)
)
go
create table PositionLicense (
PositionID int not null
references Position(PositionID),
LicenseID int not null
references License(LicenseID),
primary key (PositionID, LicenseID)
)
go
insert [User] (Name) values ('Mark the mechanic');
insert [User] (Name) values ('John the pilot');
insert [User] (Name) values ('Jane only has arts PhD but not medical.');
insert Position (Name) values ('Mechanic');
insert Position (Name) values ('Pilot');
insert Position (Name) values ('Doctor');
insert License (Name) values ('Mecha');
insert License (Name) values ('Flying');
insert License (Name) values ('Medicine');
insert License (Name) values ('PhD');
insert License (Name) values ('Phycho');
insert License (Name) values ('Arts');
insert PositionLicense (PositionID, LicenseID) values (1, 1);
insert PositionLicense (PositionID, LicenseID) values (2, 2);
insert PositionLicense (PositionID, LicenseID) values (2, 5);
insert PositionLicense (PositionID, LicenseID) values (3, 3);
insert PositionLicense (PositionID, LicenseID) values (3, 4);
insert UserLicense (UserID, LicenseID, ValidFrom, ValidTo) values (1, 1, '20110101', '20120101');
insert UserLicense (UserID, LicenseID, ValidFrom, ValidTo) values (2, 2, '20110101', '20120101');
insert UserLicense (UserID, LicenseID, ValidFrom, ValidTo) values (2, 5, '20110101', '20120101');
insert UserLicense (UserID, LicenseID, ValidFrom, ValidTo) values (3, 4, '20110101', '20120101');
insert UserLicense (UserID, LicenseID, ValidFrom, ValidTo) values (3, 6, '20110101', '20120101');
创建表[用户](
UserID int-identity不为null
主键,
名称nvarchar(100)不为空
)
去
创建表格位置(
PositionID int标识不为空
主键,
名称nvarchar(100)不为空
)
去
创建表许可证(
LicenseID int标识不为空
主键,
名称nvarchar(100)不为空
)
去
创建表UserLicense(
UserID int不为空
引用[用户](用户ID),
LicenseID int不为空
参考许可证(被许可证ID),
生效日期不为空,
有效日期不为空,
检查(从<有效到>有效),
主键(用户ID、许可证ID)
)
去
创建表位置许可证(
位置ID int不为空
参考位置(位置ID),
LicenseID int不为空
参考许可证(被许可证ID),
主键(PositionID、LicenseID)
)
去
插入[用户](名称)值(“标记机械师”);
插入[用户](名称)值(“飞行员约翰”);
插入[用户](名称)值(“Jane只有艺术博士学位,但没有医学学位”);
插入位置(名称)值(“机械师”);
插入位置(名称)值(“导频”);
插入位置(名称)值(“医生”);
插入许可证(名称)值(“机械”);
插入许可证(名称)值(“飞行”);
插入许可证(名称)值(“药品”);
插入许可证(名称)值(“PhD”);
插入许可证(名称)值('Phycho');
插入许可证(名称)值(“Arts”);
插入PositionLicense(PositionID,LicenseID)值(1,1);
插入PositionLicense(PositionID,LicenseID)值(2,2);
插入PositionLicense(PositionID,LicenseID)值(2,5);
插入PositionLicense(PositionID,LicenseID)值(3,3);
插入PositionLicense(PositionID,LicenseID)值(3,4);
插入UserLicense(UserID,LicenseID,ValidFrom,ValidTo)值(1,1,'20110101','20120120101');
插入UserLicense(UserID,LicenseID,ValidFrom,ValidTo)值(2,2,'20110101','20120120101');
插入UserLicense(UserID,LicenseID,ValidFrom,ValidTo)值(2,5,'20110101','20120120101');
插入UserLicense(UserID,LicenseID,ValidFrom,ValidTo)值(3,4,'20110101','20120120101');
插入UserLicense(UserID,LicenseID,ValidFrom,ValidTo)值(3,6,'20110101','20120120101');
最终解决方案
我基于公认的答案,它为这个问题提供了最简单的解决方案。如果您想使用查询,只需点击编辑/克隆(无论您是否登录)。可以改变的是:
- 三个变量:
- 设置日期范围的两个变量(
和@从
)@到
- 用户ID(
)@user
- 您可以在第一个CTE中切换注释代码,以在完全重叠的用户许可证或部分重叠的用户许可证之间切换代码
Select ...
From User As U
Cross Join Position As P
Where Exists (
Select 1
From PositionLicense As PL1
Join UserLicense As UL1
On UL1.LicenseId = PL1.LicenseId
And UL1.ValidFrom <= @RangeTo
And UL1.ValidTo >= @RangeFrom
Where PL1.PositionId = P.Id
And UL1.UserId = U.Id
Except
Select 1
From PositionLicense As PL2
Left Join UserLicense As UL2
On UL2.LicenseId = PL2.LicenseId
And UL2.ValidFrom <= @RangeTo
And UL2.ValidTo >= @RangeFrom
And UL2.UserId = U.Id
Where PL2.PositionId = P.Id
And UL2.UserId Is Null
)
您可能会将其转换为在生效日期参数化的内联表值函数。Select。。。
Select ...
From User As U
Cross Join Position As P
Where Exists (
Select 1
From PositionLicense As PL1
Join UserLicense As UL1
On UL1.LicenseId = PL1.LicenseId
And UL1.ValidFrom <= @RangeTo
And UL1.ValidTo >= @RangeFrom
Where PL1.PositionId = P.Id
And UL1.UserId = U.Id
Except
Select 1
From PositionLicense As PL2
Left Join UserLicense As UL2
On UL2.LicenseId = PL2.LicenseId
And UL2.ValidFrom <= @RangeTo
And UL2.ValidTo >= @RangeFrom
And UL2.UserId = U.Id
Where PL2.PositionId = P.Id
And UL2.UserId Is Null
)
从用户作为U
交叉连接位置为P
哪里有(
选择1
从位置许可证到PL1
以UL1身份加入UserLicense
在UL1.LicenseId=PL1.LicenseId上
和UL1.ValidFrom=@RangeFrom
其中,PL1.PositionId=P.Id
和UL1.UserId=U.Id
除了
选择1
从位置许可证到PL2
左加入用户许可证为UL2
在UL2.LicenseId=PL2.LicenseId上
和UL2.ValidFrom=@RangeFrom
和UL2.UserId=U.Id
式中,PL2.PositionId=P.Id
并且UL2.UserId为Null
)
如果要求用户和职位在整个范围内都有效,则更为棘手:
With Calendar As
(
Select @RangeFrom As [Date]
Union All
Select DateAdd(d, 1, [Date])
From Calendar
Where [Date] <= @RangeTo
)
Select ...
From User As U
Cross Join Position As P
Where Exists (
Select 1
From UserLicense As UL1
Join PositionLicense As PL1
On PL1.LicenseId = UL1.LicenseId
Where UL1.UserId = U.Id
And PL1.PositionId = P.Id
And UL1.ValidFrom <= @RangeTo
And UL1.ValidTo >= @RangeFrom
Except
Select 1
From Calendar As C1
Cross Join User As U1
Cross Join PositionLicense As PL1
Where U1.Id = U.Id
And PL1.PositionId = P.Id
And Not Exists (
Select 1
From UserLicense As UL2
Where UL2.LicenseId = PL1.LicenseId
And UL1.UserId = U1.Id
And C1.Date Between UL2.ValidFrom And UL2.ValidTo
)
)
Option ( MaxRecursion 0 );
将日历作为
(
选择@RangeFrom作为[日期]
联合所有
选择日期添加(d,1,[日期])
从日历
其中[日期]选择。。。
从用户作为U
交叉连接位置为P
哪里有(
选择1
从位置许可证到PL1
以UL1身份加入UserLicense
在UL1.LicenseId=PL1.LicenseId上
和UL1.ValidFrom=@RangeFrom
SELECT pl.PositionId
from PositionLicense pl
left outer join (-- All licenses user has for the entirety (sp?) of the specified date range
select LicenseId
from UserLicense
where UserId = @UserId
and @RangeFrom <= ValidFrom
and @RangeTo >= ValidTo) li
on li.LicenseId = pl.LicenseId
group by pl.PositionId
-- Where all licenses required by position are held by user
having count(pl.LicenseId) = count(li.LicenseId)