Select 来自另一个 table 在基于结果的 WHILE 循环中

Select from another table in WHILE loop based on results

本质上,我是在尝试根据用户的班次和输入的天数来计算用户可以拥有的总小时数。

示例: 2020 年 9 月 13 日开始轮班。我知道这是第 1 周,星期日(根据其他计算)。 所以我需要在 ROTA table.

中从周日开始学习 2,然后从第 2 周开始学习 3、2、1、1、1、2

一共7天: 第一周 = 周日 第 2 周 = 周一、周二、周三、周四、周五、周六

 ROTA table
+------+-----+-----+-----+-----+-----+-----+-----+
| WEEK | MON | TUE | WED | THU | FRI | SAT | SUN |
+------+-----+-----+-----+-----+-----+-----+-----+
|    1 |   2 |   2 |   2 |   2 |   2 |   2 |   2 |
|    2 |   3 |   2 |   1 |   1 |   1 |   2 |   1 |
|    3 |   1 |   2 |   1 |   1 |   1 |   1 |   1 |
|    4 |   1 |   1 |   1 |   1 |   2 |   1 |   1 |
|    5 |   1 |   1 |   2 |   1 |   1 |   1 |   1 |
+------+-----+-----+-----+-----+-----+-----+-----+

上面的数字存储在一个班次中table。因此,对于我的 7 天,这 7 天的总小时数为 2, 3, 2, 1, 1, 1, 2 = 51.5 小时。

SHIFTS Table
+-------+-------+
| SHIFT | HOURS |
+-------+-------+
|     1 | 8.5   |
|     2 | 6     |
|     3 | 8     |
+-------+-------+

我正在执行一个 WHILE 循环来获取所需的周和列。因此,对于上面的示例,我只需要来自 ROTA table 的 SUN 列。下一个循环将给我 MON - SAT.

起初我试图将两行合并在一起,然后我可以做一些计数。所以有 3x SHIFT 2、1x SHIFT 3 和 3x SHIFT 1。然后我可以得到总小时数,但不知道该怎么做。

当我的查询完成时,我得到以下两行:

LOOP 1:
+-----+
| SUN |
+-----+
|   2 |
+-----+

LOOP 2:
+-----+-----+-----+-----+-----+-----+
| MON | TUE | WED | THU | FRI | SAT |
+-----+-----+-----+-----+-----+-----+
|   3 |   2 |   1 |   1 |   1 |   2 |
+-----+-----+-----+-----+-----+-----+

我已经略微简化了我的查询,但这是它的要点:

WHILE @cnt <= @totalDays
BEGIN

IF @dayOfWeek = 1 SET @columnList = 'SUN' ELSE 
IF @tempTotalDays >= 7 SET @columnList = 'MON, TUE, WED, THU, FRI, SAT, SUN' ELSE
IF @tempTotalDays = 6  SET @columnList = 'MON, TUE, WED, THU, FRI, SAT' ELSE
IF @tempTotalDays = 5  SET @columnList = 'MON, TUE, WED, THU, FRI' ELSE
IF @tempTotalDays = 4  SET @columnList = 'MON, TUE, WED, THU' ELSE
IF @tempTotalDays = 3  SET @columnList = 'MON, TUE, WED' ELSE
IF @tempTotalDays = 2  SET @columnList = 'MON, TUE' ELSE
IF @tempTotalDays = 1  SET @columnList = 'MON'

SET @sqlCommand = 'select '+ @columnList +' from dbo.ROTA
where WEEK = @rotaWeek'

EXEC sp_executesql @sqlCommand, N'@rotaWeek nvarchar(75), @rotaWeek = @rotaWeek

END;
GO

如您所见,我快到了。我只是不知道如何从 SHIFTS table 中获取我的结果和 select 时间。任何帮助将不胜感激。

ROTA table 对于我们人类来说是非常可读的,但对于不知道一周的星期日之后是下一周的星期一(或者我们考虑值table 顺序中的相邻值完全 mon-tue-wed-thu-fri-sat-sun)。

您可以将 table 转换为机器可读的形式,为第 1 周提供天数 1、2、3、4、5、6、7,然后是 8、9、10 ,11,12,13,14 表示第 2 周,等等。计算天数的公式为:day_number = day_of_week + (7 * (week - 1)).

查询:

with days as
(
  select 1 + (7 * (week - 1)) as daynum, mon as shift from rota
  union all
  select 2 + (7 * (week - 1)) as daynum, tue as shift from rota
  union all
  select 3 + (7 * (week - 1)) as daynum, wed as shift from rota
  union all
  select 4 + (7 * (week - 1)) as daynum, thu as shift from rota
  union all
  select 5 + (7 * (week - 1)) as daynum, fri as shift from rota
  union all
  select 6 + (7 * (week - 1)) as daynum, sat as shift from rota
  union all
  select 7 + (7 * (week - 1)) as daynum, sun as shift from rota
)
select sum(s.hours)
from days d
join shifts s on s.shift = d.shift
where d.daynum between @dayOfWeek + (7 * (@rotaWeek - 1))
                   and @dayOfWeek + (7 * (@rotaWeek - 1)) + @totalDays - 1;

当然,如果您更改数据模型以匹配我的 ad-hoc 天视图,那么查询将减少到上述查询的最后五行。

更新:

在请求评论中,你说你想继续第 5 周和第 1 周。您可以使用取模运算从第 35 天到第 1 天 (next_daynum = daynum % 35 + 1)。但因此这变成了一个迭代过程,一个 ROTA 周甚至可以在计算中多次使用。迭代是通过 SQL:

中的递归查询完成的
with days as
(
  select 1 + (7 * (week - 1)) as daynum, mon as shift from rota
  union all
  select 2 + (7 * (week - 1)) as daynum, tue as shift from rota
  union all
  select 3 + (7 * (week - 1)) as daynum, wed as shift from rota
  union all
  select 4 + (7 * (week - 1)) as daynum, thu as shift from rota
  union all
  select 5 + (7 * (week - 1)) as daynum, fri as shift from rota
  union all
  select 6 + (7 * (week - 1)) as daynum, sat as shift from rota
  union all
  select 7 + (7 * (week - 1)) as daynum, sun as shift from rota
)
, cte (daynum, remaining, hours) as
(
  select d.daynum, @totalDays - 1, s.hours
  from days d
  join shifts s on s.shift = d.shift
  where d.daynum = @dayOfWeek + (7 * (@rotaWeek - 1))
  union all                     
  select d.daynum, cte.remaining - 1, cast(cte.hours + s.hours as decimal(5,1))
  from cte
  join days d on d.daynum = cte.daynum % 35 + 1
  join shifts s on s.shift = d.shift
  where cte.remaining >= 1
)
select max(hours)
from cte;

(不幸的是,SQL 服务器要求在递归 CTE 中进行强制转换以匹配列的确切数据类型。)

演示:https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=351ef091ddb80acf27e209595e2d3f9e