寻找一些分支机构工作人员的假期

时间:2019-04-23 06:26:22

标签: sql sql-server

我有3个数据表:有些人在一个分支中工作,而另一些人在2个或更多分支中工作。

我们定义了2个例假:类型1的所有分支都已关闭,类型0的某个分支已关闭(不是全部)。

现在我想找人,如果所有人都关闭了,假期怎么样?

假日

ID  Date           AllBranch
1   2019/02/01     1
2   2019/02/05     0
3   2019/02/06     0

BranchHoliday

ID  HolidayID       BranchID
1   2               2
2   2               3
3   3               2

PersonBranch

ID     PersonID       BranchID
1      10             2
2      11             2
3      11             3
4      12             2
5      12             4

我想要的结果是:

 PersonID      Hdate
    10       2019/02/01
    11       2019/02/01
    12       2019/02/01
    10       2019/02/05
    11       2019/02/05
    10       2019/02/06

在日期(2019/02/01)中,AllBranch是1,所以所有人都在度假

在日期(2019/02/05)中,AllBranch为0,因此:

  • 仅在BranchId 2中工作的PersonID-10工作是在BranchHoliday表中定义的,假期中也是如此
  • PersonID-11在2个分支(2,3)中工作,并且所有2,3在BranchHoliday表中定义为关闭,因此在假期中
  • PersonID-12在2个branch(2,4)中工作,并且BranchId 4未在BranchHoliday中定义为关闭,所以不在假期中

在日期(2019/02/06)中,AllBranch为0,因此:

  • PersonID-10仅在BranchId 2中工作,而分支2在BranchHoliday表中定义,因此在假期中
  • PersonID-11在2个分支(2,3)中工作,并且在BranchHoliday表中未定义仅关闭第3个分支,因此在NOT假期中没有 PersonID-12在2个branch(2,4)中工作,而Branch 3未在BranchHoliday表中定义为关闭,因此不在假期中

我需要SQL查询,并且正在使用SQL Server 2016

2 个答案:

答案 0 :(得分:2)

一种解决方案是将其分为两部分:

首先选择AllBranch假期的人员,然后再选择特定假期的人员,在这些假期中,他们所工作的所有分支机构均具有确定的假期:

SELECT AllPers.PersonID, H.Date
FROM Holiday H
INNER JOIN (
  SELECT DISTINCT PersonID 
  FROM PersonBranch
) AllPers ON H.AllBranch = 1
UNION 
SELECT Sub.PersonID, H.Date
FROM Holiday H
INNER JOIN (
  SELECT PB.PersonID 
    , SUM(IIF(BH.BranchID IS NULL, 1, 0)) AS AnyBranchOpen
  FROM PersonBranch PB
  LEFT JOIN BranchHoliday BH ON PB.BranchID = BH.BranchID
  GROUP BY PB.PersonID
) Sub ON AnyBranchOpen = 0
WHERE H.AllBranch   = 0

答案 1 :(得分:2)

鉴于您的预期结果是这样,我不知道您为什么接受Esteban的回答。仅6行。

PersonID      Hdate
    10       2019/02/01
    11       2019/02/01
    12       2019/02/01
    10       2019/02/05
    11       2019/02/05
    10       2019/02/06

但是Esteban的查询产生7行。在2月6日的假期中,错误地将11号人员包括在2月6日的假期中。这与您的问题定义相矛盾:

In date (2019/02/06) AllBranch is 0 so:
* ...

* PersonID-11 work in 2 branch(2,3) and only branch 3 is NOT define closed 
  in BranchHoliday table so is in NOT holiday

大概应该在假期表中检查(加入)人员的BranchHoliday,对吗?但是Esteban的查询没有这样做。

Esteban的查询产生不正确的输出:

实时测试:http://sqlfiddle.com/#!18/86728/2

| PersonID |       Date |
|----------|------------|
|       10 | 2019-02-01 |
|       11 | 2019-02-01 |
|       12 | 2019-02-01 |
|       10 | 2019-02-05 |
|       11 | 2019-02-05 |
|       10 | 2019-02-06 |
|       11 | 2019-02-06 |

给出这些不同的数据:

假日

ID  Date           AllBranch
1   2019/02/01     1
2   2019/02/05     0
3   2019/02/06     0
4   2019/04/02     0

BranchHoliday

ID  HolidayID       BranchID
1   2               2
2   2               3
3   3               2
4   4               2
5   4               4

Esteban的查询再次产生不正确的输出:

实时测试:http://sqlfiddle.com/#!18/10348/1

| PersonID |       Date |
|----------|------------|
|       10 | 2019-02-01 |
|       11 | 2019-02-01 |
|       12 | 2019-02-01 |
|       10 | 2019-02-05 |
|       11 | 2019-02-05 |
|       12 | 2019-02-05 |
|       10 | 2019-02-06 |
|       11 | 2019-02-06 |
|       12 | 2019-02-06 |
|       10 | 2019-04-02 |
|       11 | 2019-04-02 |
|       12 | 2019-04-02 |

它应该产生这个:

实时测试:http://sqlfiddle.com/#!17/7b6be/1

| id |       date |
|----|------------|
| 10 | 2019-02-01 |
| 11 | 2019-02-01 |
| 12 | 2019-02-01 |
| 10 | 2019-02-05 |
| 11 | 2019-02-05 |
| 10 | 2019-02-06 |
| 10 | 2019-04-02 |
| 12 | 2019-04-02 |

并给出这些数据。请注意,只有#5分支机构的人员才可以在4月2日放假。

假日

ID  Date           AllBranch
1   2019/02/01     1
2   2019/02/05     0
3   2019/02/06     0
4   2019/04/02     0

BranchHoliday

ID  HolidayID       BranchID
1   2               2
2   2               3
3   3               2
4   4               5

Esteban的查询再次产生不正确的输出,因为只允许分支#5上的人员在4月2日放假。但是,他的查询中包括不应被允许在4月2日上假期的人。#10和#11未包含在分支#5中,因此不应在4月2日出现。

实时测试:http://sqlfiddle.com/#!18/fd1d3/1

| PersonID |       Date |
|----------|------------|
|       10 | 2019-02-01 |
|       11 | 2019-02-01 |
|       12 | 2019-02-01 |
|       10 | 2019-02-05 |
|       11 | 2019-02-05 |
|       10 | 2019-02-06 |
|       11 | 2019-02-06 |
|       10 | 2019-04-02 |
|       11 | 2019-04-02 |

它应该产生这个。不允许任何员工在4月2日放假,甚至10号员工也不允许在4月2日放假,因为10号员工仅在#2分支机构。只有#5分支机构的人员可以放假。

实时测试:http://sqlfiddle.com/#!17/299f73/1

| id |       date |
|----|------------|
| 10 | 2019-02-01 |
| 11 | 2019-02-01 |
| 12 | 2019-02-01 |
| 10 | 2019-02-05 |
| 11 | 2019-02-05 |
| 10 | 2019-02-06 |

这是正确的查询(Postgres版本):

实时测试:http://sqlfiddle.com/#!17/6cf97/2

select h.date, p.id 
from holiday h 
cross join person p

join personbranch pb 
on p.id = pb.personid

left join branchholiday bh 
on h.id = bh.holidayid and pb.branchid = bh.branchid

group by h.date, p.id
having  
    every(h.allbranch)
    or 
    -- choose only every person whose branch ids are all present in branchholiday 
    -- filtered by holidayid from holiday.id
    every(bh.branchid is not null) 

order by h.date, p.id

输出与问题定义的预期输出匹配:

| id |       date |
|----|------------|
| 10 | 2019-02-01 |
| 11 | 2019-02-01 |
| 12 | 2019-02-01 |
| 10 | 2019-02-05 |
| 11 | 2019-02-05 |
| 10 | 2019-02-06 |

SQL Server版本:

实时测试:http://sqlfiddle.com/#!18/41a54/3

select p.id, h.date
from holiday h 
cross join person p

join personbranch pb 
on p.id = pb.personid

left join branchholiday bh 
on h.id = bh.holidayid and pb.branchid = bh.branchid

group by h.date, p.id
having  
    count(case when h.allbranch = 1 then 1 end) = count(*)
    or 
    -- choose only every person whose branch ids are all present in branchholiday 
    -- filtered by holidayid from holiday.id
    count(case when bh.branchid is not null then 1 end) = count(*)

order by h.date, p.id

输出与问题定义的预期输出匹配:

| id |       date |
|----|------------|
| 10 | 2019-02-01 |
| 11 | 2019-02-01 |
| 12 | 2019-02-01 |
| 10 | 2019-02-05 |
| 11 | 2019-02-05 |
| 10 | 2019-02-06 |

这是另一个SQL Server的every习惯用法。您可以将count(condition) = count(*)更改为min的方法:

实时测试:http://sqlfiddle.com/#!18/41a54/4

having  
    min(case when h.allbranch = 1 then 1 else 0 end) = 1
    or 
    -- choose only every person whose branch ids are all present in branchholiday 
    -- filtered by holidayid from holiday.id
    min(case when bh.branchid is not null then 1 else 0 end) = 1 

输出与问题定义的预期输出匹配:

| id |       date |
|----|------------|
| 10 | 2019-02-01 |
| 11 | 2019-02-01 |
| 12 | 2019-02-01 |
| 10 | 2019-02-05 |
| 11 | 2019-02-05 |
| 10 | 2019-02-06 |