【问题标题】:Select from another table in WHILE loop based on results根据结果​​从 WHILE 循环中的另一个表中选择
【发布时间】:2021-01-06 20:46:38
【问题描述】:

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

示例: 2020 年 9 月 13 日开始轮班。我知道这是第 1 周,星期日(基于其他计算)。 所以我需要在 ROTA 表中从周日取出 2,然后从第 2 周取出 3、2、1、1、1、2。

总共 7 天: 第 1 周 = 星期日 第 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 |
+------+-----+-----+-----+-----+-----+-----+-----+

以上数字存储在 Shifts 表中。因此,对于我的 7 天,这 7 天的总小时数为 2、3、2、1、1、1、2 = 51.5 小时。

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

我正在执行 WHILE 循环来获取所需的周和列。因此,对于上面的示例,我只需要 ROTA 表中的 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 表中选择小时数。任何帮助将不胜感激。

【问题讨论】:

  • SQL 是一种基于集合的语言。你不需要循环。在最坏的情况下,您可以将所有这些检查转换为查询的 SELECT 部分中的单个 CASE 子句
  • 感谢您的回复。如果我只是使用 SELECT,当我可能需要多次使用同一个 WEEK 行时它会如何工作?
  • 你什么时候需要考虑一个星期多次?你的意思是第 5 周之后又是第 1 周,@totalDays 可以超过 35 天?
  • 是的,正确。可能超过 35 天,在第 5 周之后会回到第 1 周
  • 好的,我不清楚。我已经用递归查询更新了我的答案。

标签: sql-server tsql dynamic-sql sp-executesql


【解决方案1】:

ROTA 表对我们人类来说非常易读,但对于不知道一周的星期日之后是下一周的星期一的 DBMS(或者我们认为表中的值与周一-周二-周三-周四-周五-周六-周日订购)。

您可以将表格转换为机器可读的形式,给第 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;

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

更新:

在请求 cmets 中,您说您想继续第 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 Server 要求在递归 CTE 中进行强制转换以匹配列的确切数据类型。)

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

【讨论】:

  • 谢谢你。我将尝试将其合并到我的原始查询中,以获取确切的轮班时间。
猜你喜欢
  • 1970-01-01
  • 2015-02-15
  • 1970-01-01
  • 2017-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多