如何使用t-sql检查逻辑项组合?

时间:2014-01-26 08:12:00

标签: sql sql-server tsql

我必须检查一名工人是否具备所有必需的技能。这是通过将工人技能组合与所需的一组技能进行比较来完成的。所以,为了使它更清楚,这里是我所拥有的表的DDL:

CREATE TABLE [WorkerSkills](
    [WorkerId] [bigint] NOT NULL,
    [SkillName] [varchar](100) NOT NULL
) GO

CREATE TABLE [SkillCombinator](
    [SetId] [int] NOT NULL,
    [SkillCombinator] [varchar](5) NOT NULL
) GO

CREATE TABLE [RequiredSkills](
    [SetId] [int] NOT NULL,
    [SkillName] [varchar](100) NOT NULL
) GO

以下是示例数据:

INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (1, 'A')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (1, 'B')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (1, 'C')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (2, 'D')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (2, 'X')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (3, 'E')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (4, 'A')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (4, 'B')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (4, 'H')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (4, 'I')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (5, 'A')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (5, 'B')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (5, 'C')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (5, 'E')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (5, 'G')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (5, 'H')
INSERT [WorkerSkills] ([WorkerId], [SkillName]) VALUES (5, 'I')

INSERT [SkillCombinator] ([SetId], [SkillCombinator]) VALUES (1, 'AND')
INSERT [SkillCombinator] ([SetId], [SkillCombinator]) VALUES (2, 'OR')
INSERT [SkillCombinator] ([SetId], [SkillCombinator]) VALUES (3, 'AND')

INSERT [RequiredSkills] ([SetId], [SkillName]) VALUES (1, 'A')
INSERT [RequiredSkills] ([SetId], [SkillName]) VALUES (1, 'B')
INSERT [RequiredSkills] ([SetId], [SkillName]) VALUES (1, 'C')
INSERT [RequiredSkills] ([SetId], [SkillName]) VALUES (2, 'D')
INSERT [RequiredSkills] ([SetId], [SkillName]) VALUES (2, 'E')
INSERT [RequiredSkills] ([SetId], [SkillName]) VALUES (2, 'F')
INSERT [RequiredSkills] ([SetId], [SkillName]) VALUES (3, 'G')
INSERT [RequiredSkills] ([SetId], [SkillName]) VALUES (3, 'H')
INSERT [RequiredSkills] ([SetId], [SkillName]) VALUES (3, 'I')

这意味着有3组,每组有3种技能定义。

set 1: A and B and C
set 2: D or E or F
set 3: G and H and I

有些工人具备以下技能:

worker 1: A, B, C
worker 2: D, X
worker 3: E
worker 4: A, B, H, I
worker 5: A, B, C, E, G, H, I

现在,问题是在Sql Server 2008中编写一个接受 WorkerId SetCombinator 参数的函数,并返回一个值,指示Worker是否具备所有必需技能。

示例输入1:

WorkerId: 1
SetCombinator: OR

这意味着所有集合都应该具有 OR 组合子,即:

set 1: A and B and C
OR
set 2: D or E or F
OR
set 3: G and H and I

结果应为 true ,因为工人具有与第1组相对应的技能。

示例输入2:

WorkerId: 4
SetCombinator: OR

结果应为 false

示例输入3:

WorkerId: 1
SetCombinator: AND

这意味着所有集合都应该具有 AND 组合子,即:

set 1: A and B and C
AND
set 2: D or E or F
AND
set 3: G and H and I

结果应该是 false ,因为工人的技能只对应于第1组,而不是第2组和第3组。

示例输入4:

WorkerId: 5
SetCombinator: AND

结果应为 true ,因为工人具有与所有集合相对应的技能。

这个函数应该是什么样的想法?

更新:我忘了提到RequiredSkills表中的技能不是常量,它们经常会被修改,而且这些技能的数量也是动态的。因此,具有硬编码值的解决方案将无效。

3 个答案:

答案 0 :(得分:0)

这是我到目前为止所拥有的。我希望你可以继续努力并进一步发展以适应你的问题。 我使用了按位逻辑 - 但我不是这方面的专家,这里可能还有其他人可以改进这一点。

DECLARE @WorkerID       BIGINT  = 1,
        @LogicalCase    BIT     = 1,    --  [0 = OR], [1 = AND]
        @SkillScore     INT     = 0,
        @isTrue         BIT     = 0

SELECT @SkillScore  =   SUM (
                                CASE    WHEN SkillName = 'A' THEN 1
                                        WHEN SkillName = 'B' THEN 2
                                        WHEN SkillName = 'C' THEN 4
                                        WHEN SkillName = 'D' THEN 8
                                        WHEN SkillName = 'E' THEN 16
                                        WHEN SkillName = 'F' THEN 32
                                        WHEN SkillName = 'G' THEN 64
                                        WHEN SkillName = 'H' THEN 128
                                        WHEN SkillName = 'I' THEN 256
                                END
                            )
FROM WorkerSkills
WHERE WorkerID = @WorkerID


IF @LogicalCase = 0
BEGIN
    IF (@SkillScore & 7 = 7)                                                            --      set 1: A and B and C 
    OR ( (@SkillScore & 8 = 8) OR (@SkillScore & 16 = 16) OR (@SkillScore & 32 = 32) )  --  OR  set 2: D or E or F
    OR (@SkillScore & 448 = 448)                                                        --  OR  set 3: G and H and I 
    BEGIN   
        SET @isTrue = 1
    END
END
IF @LogicalCase = 1
BEGIN
    IF (@SkillScore & 7 = 7)                                                            --      set 1: A and B and C 
    AND ( (@SkillScore & 8 = 8) OR (@SkillScore & 16 = 16) OR (@SkillScore & 32 = 32) ) --  AND set 2: D or E or F
    AND (@SkillScore & 448 = 448)                                                       --  AND set 3: G and H and I 
    BEGIN   
        SET @isTrue = 1
    END
END


SELECT @isTrue

答案 1 :(得分:0)

可能不是最有效的,但它适用于任何数量的技能和组合:

declare @workerid int =5 -- input param
declare @setcombinator varchar(3) ='AND' -- input param

declare @skillsleft varchar(max)
declare @result varchar(10)
declare @getsets cursor
declare @set int

set @getsets = CURSOR FOR SELECT distinct SetId FROM SkillCombinator
OPEN @getsets
FETCH NEXT FROM @getsets INTO @set
-- set result by default
IF(@setcombinator='OR') set @result='FALSE' else set @result='TRUE'

WHILE @@FETCH_STATUS = 0
BEGIN -- compares each skillset against worker's and determine match or mismatch

if (select skillcombinator from skillcombinator where setid=@set) = 'AND'
    BEGIN --needs all the skills in the set
        SET @skillsleft=
        (select count(*) from
        (select skillname from RequiredSkills a 
        join SkillCombinator b on a.setid=b.setid 
        where a.setid=@set
        except
        select skillname from workerskills where workerid=@workerid) t)
        -- override default depending on the logical combinations
        if (@skillsleft = 0 and @setcombinator='OR') set @result='TRUE' 
        if (@skillsleft > 0 and @setcombinator='AND')set @result='FALSE'
    END

if (select skillcombinator from skillcombinator where setid=@set) = 'OR'
    BEGIN --needs at least one of the skills in the set 
        SET @skillsleft=
        (select count(*) from
        (select skillname from RequiredSkills a 
        join SkillCombinator b on a.setid=b.setid 
        where a.setid=@set
        intersect
        select skillname from workerskills where workerid=@workerid) t)
        -- override default depending on the logical combinations
        if (@skillsleft > 0 and @setcombinator='OR') set @result='TRUE'
        if (@skillsleft = 0 and @setcombinator='AND') set @result='FALSE'
    END

FETCH NEXT FROM @getsets INTO @set
END
select @result
CLOSE @getsets
DEALLOCATE @getsets

答案 2 :(得分:0)

好的,所以这是迄今为止我找到的最佳解决方案。

create function fnMatchedToSkillsSet
(
 @WorkerId int,
 @Condition varchar(3)
)
returns table
as
return (
 with x as
 (
  select
   sc.SetId,
   nullif(case
    when sc.SkillCombinator = 'AND' and count(distinct ws.SkillName) = count(*) then count(distinct ws.SkillName)
    when sc.SkillCombinator = 'OR' then count(distinct ws.SkillName)
   end, 0) as SkillsCount
  from
   dbo.SkillCombinator sc join
   dbo.RequiredSkills rs on rs.SetId = sc.SetId left join
   dbo.WorkerSkills ws on ws.WorkerId = @WorkerId and ws.SkillName = rs.SkillName
  group by
   sc.SetId, sc.SkillCombinator
 )
 select
  case
   when @Condition = 'AND' and count(SkillsCount) = (select count(*) from dbo.SkillCombinator) then 1
   when @Condition = 'OR' and count(SkillsCount) > 0 then 1
   else 0
  end as Result
 from
  x
);