返回UNION查询中尚未返回的行

时间:2019-12-05 13:34:14

标签: sql sql-server

首先,道歉,因为这是很多代码和表格,但不管您相信与否,这确实是我能做出的最小可重复的示例,同时能够准确地对其进行清楚地解释。

编辑:对于上下文-该数据​​集将绑定到前端连续表单,该表单需要具有这种格式的数据,因此它可以是“可更新的”记录集,并且调度程序用户可以每天切换“打开/关闭”。将其作为带有日期/天列的单个字段表示(乍一看)将无法实现。

我有一个数据表,其中以布尔值的形式显示了所有雇员的列表以及星期一至星期日的可用性,并以整数形式显示自上次工作以来的天数:

+-------+-----------+-----+-----+-----+-----+-----+---------+
| EmpID |   Name    | Mon | Tue | Wed | Thu | Fri | LastDay |
+-------+-----------+-----+-----+-----+-----+-----+---------+
|     1 | Paul      |   1 |   1 |   1 |   0 |   1 |      11 |
|     2 | Rob       |   1 |   1 |   1 |   0 |   1 |      19 |
|     3 | Christine |   0 |   1 |   0 |   0 |   0 |      15 |
|     4 | Annie     |   0 |   1 |   0 |   1 |   0 |      14 |
|     5 | Billie    |   0 |   0 |   1 |   0 |   1 |       4 |
|     6 | Ben       |   1 |   1 |   1 |   1 |   1 |       1 |
|     7 | Lacey     |   0 |   0 |   0 |   0 |   0 |     110 |
|     8 | Satia     |   1 |   0 |   1 |   0 |   1 |      25 |
|     9 | Derek     |   0 |   1 |   0 |   1 |   0 |      29 |
|    10 | Larry     |   0 |   0 |   1 |   0 |   1 |      11 |
|    11 | Matt      |   1 |   1 |   1 |   1 |   1 |      22 |
|    12 | Steven    |   0 |   0 |   0 |   0 |   0 |      14 |
|    13 | Ellie     |   1 |   1 |   1 |   1 |   1 |      17 |
|    14 | Amy       |   1 |   1 |   1 |   1 |   1 |      16 |
+-------+-----------+-----+-----+-----+-----+-----+---------+

需要以类似的格式为用户安排一个工作周的时间,为此,我需要一个查询,该查询将根据最长的工作时间来提取有资格工作的TOP 2个用户为了公平。

预期输出为以下格式:

+-------+-----------+-----+-----+-----+-----+-----+
| EmpID |   Name    | Mon | Tue | Wed | Thu | Fri |
+-------+-----------+-----+-----+-----+-----+-----+
|     2 | Rob       |   1 |     |     |     |     |
|     1 | Paul      |   1 |     |     |     |     |
|     3 | Christine |     |   1 |     |     |     |
|     4 | Annie     |     |   1 |     |     |     |
|     5 | Billie    |     |     |   1 |     |     |
|     6 | Ben       |     |     |   1 |     |     |
|     9 | Derek     |     |     |     |   1 |     |
|    11 | Matt      |     |     |     |   1 |     |
|     8 | Satia     |     |     |     |     |   1 |
|    10 | Larry     |     |     |     |     |   1 |
+-------+-----------+-----+-----+-----+-----+-----+

这实际上将接下来的两个合格用户作为星期一,将他们按照最近工作的人的降序排列,然后移至星期二。这就是我的问题所在,因为当我提取下一个TOP 2用户时,它将再次选择Paul,因为他最近工作过。但是由于他已被选为星期一,我希望它现在不包括他。

我发现了几种完全无效的方法,其中包括一种实际上使SSMS资源最大化的方法,并且发现我现在要做的实际上是每天进行一次UNION查询,新的UNION必须检查以前的UNION查询来检查是否已选择它们,这变得异常复杂和低效:

-- MONDAY
SELECT *
  FROM 
  (
    SELECT TOP 8
           a.EmpID,
           a.Name,
           1 as [Mon],
           0 as [Tue],
           0 as [Wed],
           0 as [Thu],
           0 as [Fri]
      FROM availability AS a
     WHERE 1=1
           AND a.MonAvailable = 1
     ORDER BY LastDay
  ) as Monday

 UNION ALL

-- TUESDAY
SELECT *
  FROM 
  (
    SELECT TOP 8
           a.EmpID,
           a.Name,
           0 as [Mon],
           1 as [Tue],
           0 as [Wed],
           0 as [Thu],
           0 as [Fri]
      FROM availability AS a
     WHERE 1=1
           AND a.TueAvailable = 1
           AND a.EmpID NOT IN
           (
              SELECT a.EmpID
                FROM 
                (
                    SELECT TOP 8
                        a.EmpID
                    FROM availability AS a
                    WHERE 1=1
                        AND a.MonAvailable = 1
                    ORDER BY LastDay
                ) as Monday
           )
     ORDER BY LastDay
  ) as Tuesday

  UNION ALL

  -- WEDNESDAY
  SELECT *
  FROM 
  (
    SELECT TOP 8
           a.EmpID,
           a.Name,
           0 as [Mon],
           0 as [Tue],
           1 as [Wed],
           0 as [Thu],
           0 as [Fri]
      FROM availability AS a
     WHERE 1=1
           AND a.WedAvailable = 1
           AND a.EmpID NOT IN
           (
              SELECT EmpID
                FROM 
                (
                    SELECT TOP 8
                        EmpID
                    FROM availability
                    WHERE 1=1
                        AND MonAvailable = 1
                    ORDER BY LastDay
                ) as Monday

               UNION ALL

              SELECT EmpID
                FROM 
                (
                    SELECT TOP 8
                        EmpID
                    FROM availability
                    WHERE 1=1
                        AND TueAvailable = 1
                    ORDER BY LastDay
                ) as Tuesday
           )
     ORDER BY LastDay
  ) as Wednesday

显然,这是资源密集型的,令人难以置信的低效率和边界痛苦。最好的方法是什么?我在这里错在哪里?

1 个答案:

答案 0 :(得分:0)

这有点笨拙,但是,我认为您可以尝试下面的代码。它使用CTE来存储empid的工作列表。在处理数据时,最好将它们添加并存储在临时表中。

with availability (empid,name,mon,tue,wed,thu,fri,lastday) as 
(
select empid,name,mon,tue,wed,thu,fri,lastday from availability
)
, monday (norder,empid,name,mon,tue,wed,thu,fri,lastday) as 
(select top 2 1,empid,name,mon,0,0,0,0,lastday from availability where mon=1 order by lastday desc)
,   list1 as (select * from monday)
, tuesday (norder,empid,name,mon,tue,wed,thu,fri,lastday) as 
(select top 2 2,empid,name,0,tue,0,0,0,lastday from availability where tue=1 and empid not in (select empid from list1) order by lastday desc)
,   list2 as (select * from monday union select * from tuesday)
, wednesday (norder,empid,name,mon,tue,wed,thu,fri,lastday) as 
(select top 2 3,empid,name,0,0,wed,0,0,lastday from availability where wed=1 and empid not in (select empid from list2) order by lastday desc)
,   list3 as (select * from monday union select * from tuesday union select * from wednesday)
, thursday (norder,empid,name,mon,tue,wed,thu,fri,lastday) as 
(select top 2 4,empid,name,0,0,0,thu,0,lastday from availability where thu=1 and empid not in (select empid from list3) order by lastday desc)
,   list4 as (select * from monday union select * from tuesday union select * from wednesday union select * from thursday)
, friday (norder,empid,name,mon,tue,wed,thu,fri,lastday) as 
(select top 2 5,empid,name,0,0,0,0,fri,lastday from availability where fri=1 and empid not in (select empid from list4) order by lastday desc)
,   list5 as (select * from monday union select * from tuesday union select * from wednesday union select * from thursday union select * from friday)
select * from list5 order by norder asc, lastday desc