SQL,针对一个“键”间隔找到多个范围的离散重叠时间间隔并计算“最严格”的常见重叠?

时间:2018-11-14 18:55:07

标签: sql-server tsql datetime gaps-and-islands

我正在SQL Server Management Studio中工作,所以我想这是Microsoft SQL Server T-SQL问题。

现实情况是这样的:我有多个员工在多个位置工作,并且每个位置都有“ time in”和“ time out”记录。我已经为每个时间间隔创建了一个唯一的“班次ID”,并根据员工,日期和位置加入了与我的主要员工相匹配的其他员工的班次,或者将我与其他所有人进行比较的班次。

此外,我编写了一个查询,将每个“其他雇员”的特定重叠时间间隔与梯形失真拉在一起。对于一个班次,时间轴如下所示:

 Key Emp. | 9AM------------------------6PM
 Emp. A   | 9AM------------------------6PM
 Emp. B   |         12PM-------4PM        

因此可以进行真正的“受控”比较的离散时间段是:

  • 键,A和B从12PM-4PM
  • 从上午9点至下午12点的密钥和A
  • 密钥和A从4PM-6PM

最终目标是提取在该时间段内发生的每个员工的所有活动(在单独的表中组织为带有日期时间戳的事件),并比较每个相关员工的总计。因此,每个时间范围都将有一个单独的“计数(事件)”总计,仅受如上所述共享时间间隔的员工的影响。

目前,我的数据是这样组织的:

密钥和其他雇员的“输入”和“输出”列存储为TIMESTAMP;在我的示例中,“ 1 / 1,6PM”只是节省空间的糟糕方法。请在这篇文章的结尾查看我的消耗数据。 SSMS似乎并不在乎我是否拥有超过TIMESTAMP列,并将它们都视为DATETIME:

Key_ShiftID| Key In | Key Out | Oth_Emp_ShiftID | Oth_Emp_In | Oth_Emp_Out
  K          1/1,9AM   1/1,6PM     A                1/1,9AM     1/1,6PM 
  K          1/1,9AM   1/1,6PM     B                1/1,12PM    1/1,4PM 

其中Shift ID(Key_ShiftID和Oth_Emp_ShiftID)是唯一的字符串,并且时间间隔由两列(Key_In和Key_Out + Oth_Emp_In和Oth_Emp_Out)定义为日期时间/时间戳。我正在寻找可以比较员工活动的离散时间,该时间在单独的表中,每个事件都有一个独特的日期时间,如前所述。因此,我认为结束数据看起来像这样:

Key_ShiftID| Key_In | Key_Out | Oth_Emp_ShiftID | Oth_Emp_In  | Oth_Emp_Out
  K          1/1,9AM   1/1,6PM     A                1/1,12PM    1/1,4PM 
  K          1/1,9AM   1/1,6PM     B                1/1,12PM    1/1,4PM 
  K          1/1,9AM   1/1,6PM     A                1/1,9AM     1/1,12PM
  K          1/1,9AM   1/1,6PM     A                1/1,4PM     1/1,6PM

这样我就可以通过ShiftID将上面的表加入到我的活动表中,并为每个相关员工引入计数(事件)

where event_datetime >= Oth_Emp_In and event_datetime <= Oth_Emp_Out

此外,如前所述,我已经编写了一个查询,以减少非关键员工的轮班以仅反映他们与关键员工重叠的时间间隔,因此Other_Emp_In将始终大于或等于键入时间,Other_Emp_Out将始终小于或等于键入时间。

先谢谢了。我一直在研究和解决这个问题大约2天。

这里是一键移位的示例数据(不是上面的确切示例):

此外,SQL Server似乎并不在乎我是否拥有超过TIMESTAMP列,并将它们都视为DATETIME。

CREATE TABLE "sample_data" 
(
    "Employee" INT,
    "Key_ShiftID" TEXT,
    "Key_In" TIMESTAMP,
    "Key_Out" TIMESTAMP,
    "Other_Emp_ShiftID" TEXT,
    "Other_Emp_In" TIMESTAMP,
    "Other_Emp_Out" TIMESTAMP,
    "overlap_min" TIMESTAMP,
    "overlap_max" TIMESTAMP
);

INSERT INTO "sample_data" 
VALUES (900, '545BD826-0C9A-408B-BE9F-4C3D7D307948', '2016-09-27 14:15:00', '2016-09-27 21:45:00', '035FA1C1-B469-44EB-B5B4-5B6948574464', '2016-09-27 08:45:00', '2016-09-27 16:15:00', '2016-09-27 14:15:00', '2016-09-27 16:15:00'),
       (78, '545BD826-0C9A-408B-BE9F-4C3D7D307948', '2016-09-27 14:15:00', '2016-09-27 21:45:00', '74035838-FD07-4F8D-8AC4-F6407AC786D9', '2016-09-27 18:00:00', '2016-09-27 21:15:00', '2016-09-27 18:00:00', '2016-09-27 21:15:00'),
       (900, '545BD826-0C9A-408B-BE9F-4C3D7D307948', '2016-09-27 14:15:00', '2016-09-27 21:45:00', 'D7E9ADCD-8631-476D-B69F-00626F0E4B06', '2016-09-27 16:45:00', '2016-09-27 21:45:00', '2016-09-27 16:45:00', '2016-09-27 21:45:00');

1 个答案:

答案 0 :(得分:0)

欢迎使用StackOverflow。将来,请尝试包含一些易消耗的示例数据,例如我在下面的解决方案中包含的数据。

这是一个有趣的小问题。对于这种情况,我利用了利用ngrams8K的patExtract8K函数。这是一个如何使用PatExtract的示例;在这里,我从一个字符串中提取钱:

import { validationMixin } from 'vuelidate';
import { required } from 'vuelidate/lib/validators';

var url = "https://bc-only-rates-trimakas.c9users.io";

export default {
  mixins: [validationMixin],
  validations: {
    seller_id: { required }
  },
  props: ["amazonCredsArray"],
  computed:{
    sellerIdErrors () {
      const errors = []
      if (!this.$v.seller_id.$dirty) return errors
      !this.$v.seller_id.checked && errors.push('Please provide us your seller id')
      return errors
    },
  },

结果:

SELECT p.* 
FROM   dbo.patextract8K('Pay me $50.17 now or $1000 later!','[^$0-9.]') AS p;

现在要解决您的问题:

itemNumber  itemIndex  itemLength  item
----------- ---------- ----------- --------
1           8          6           $50.17
2           22         5           $1000

结果:

-- Easily consumable sample data
DECLARE @table TABLE (shiftId VARCHAR(2), empKey VARCHAR(5), workDuration VARCHAR(100));
INSERT @table(shiftId,empKey,workDuration)
VALUES
('K','A','12PM - 4PM'),
('K','B','12PM - 4PM'),
('K','A','9AM - 12PM'),
('K','A','4PM - 6PM');

-- Solution
SELECT 
  shiftId   = f.shiftId, 
  KeyIn     = '1/1,'+REPLACE(CONVERT(VARCHAR(10),
               MIN(CAST(f.c1 AS TIME)) OVER (),100),':00',''),
  KeyOut    = '1/1,'+REPLACE(CONVERT(VARCHAR(10),
               MAX(CAST(f.c2 AS TIME)) OVER (),100),':00',''),
  empShift  = f.empKey,
    othEmpIn  = '1/1,'+f.c1, 
  othEmpOut = '1/1,'+f.c2
FROM
(
  SELECT      t.shiftId, t.empKey, t.workDuration, 
              c1 = MAX(CASE p.itemNumber WHEN 1 THEN p.item END), 
              c2 = MAX(CASE p.itemNumber WHEN 2 THEN p.item END)
  FROM        @table AS t
  CROSS APPLY dbo.patExtract8k(t.workDuration, '[^0-9APM]') AS p
  CROSS APPLY (VALUES(CAST(p.item AS TIME))) AS tm(N)
  GROUP BY    t.shiftId, t.empKey, t.workDuration
) AS f;

请注意,我不知道“ 1/1”来自何处,所以我只是对其进行了硬编码。

这是我的基本功能。所有这些都对以很少的代码有效地解决各种各样的SQL问题很有帮助。

shiftId KeyIn      KeyOut       empShift othEmpIn     othEmpOut
------- ---------- ------------ -------- ------------ ------------
K       1/1,9AM    1/1,6PM      A        1/1,12PM     1/1,4PM
K       1/1,9AM    1/1,6PM      A        1/1,4PM      1/1,6PM
K       1/1,9AM    1/1,6PM      A        1/1,9AM      1/1,12PM
K       1/1,9AM    1/1,6PM      B        1/1,12PM     1/1,4PM