Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Tsql 获取与另一个表中的所有记录匹配的记录_Tsql_Sql Server 2008 R2 - Fatal编程技术网

Tsql 获取与另一个表中的所有记录匹配的记录

Tsql 获取与另一个表中的所有记录匹配的记录,tsql,sql-server-2008-r2,Tsql,Sql Server 2008 R2,在这个问题的范围内,我有3个实体: 用户 位置 许可证 然后我有两个关系(多对多)表: PositionLicense-这一个连接了Position和License即特定职位需要哪些许可证 UserLicense-此选项将User与License连接起来,即特定用户拥有的许可证。但还有一个额外的复杂性:用户许可证具有有效日期范围(ValidFrom和ValidTo) 问题 这些是输入变量: 识别特定用户的UserID RangeFrom定义日期范围下限 RangeTo定义了日期范围上限 我需

在这个问题的范围内,我有3个实体:

  • 用户
  • 位置
  • 许可证
  • 然后我有两个关系(多对多)表:

  • PositionLicense
    -这一个连接了
    Position
    License
    即特定职位需要哪些许可证
  • UserLicense
    -此选项将
    User
    License
    连接起来,即特定用户拥有的许可证。但还有一个额外的复杂性:用户许可证具有有效日期范围(
    ValidFrom
    ValidTo
  • 问题 这些是输入变量:

    • 识别特定
      用户的
      UserID
    • RangeFrom
      定义日期范围下限
    • RangeTo
      定义了日期范围上限
    我需要得到什么?对于一个特定的用户(和日期范围),我需要得到这个特定用户可以工作的职位列表。问题是,用户必须至少拥有每个匹配位置所需的所有许可证

    我在编写SQL查询以获取此列表时遇到了巨大的问题

    如果可能的话,我希望使用一个SQL查询(当然可以有额外的CTE)。如果你能让我相信在几个查询中这样做会更有效率,我愿意倾听

    一些可行的数据 复制并运行此脚本。3个用户,3个职位,6个许可证。马克和约翰应该比赛,但简不应该

    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)