T-SQL :找出之前出现的乱序值

T-SQL : find out of sequence values that have occurred before

这道题是关于找出乱序事件的,但仅限于之前发生过的事件。在下面的table个机器人动作数据中,每个动作都应该按顺序到相同或更高的点号,如果是这样,DIR(方向)= 1。正确的序列已预先编程 (SeqCorr) 并记录实际序列 (SeqAct) 以供比较。

Spot Dir SeqCorr SeqAct moveID
------------------------------
 9    1    113    117    1085
 9    1    114    118    1086
 10   1    115    119    1087
 10   1    116    120    1088
 2    0    1      121    1089
 2    1    2      122    1090
 2    1    3      123    1091
 6    1    5      124    1092
 6    1    6      125    1093
 2    0    4      126    1094
 6    1    7      127    1095
 6    1    8      128    1096

问题是我将如何查询数据以检测 Spot 的 Dir = 0(例如 Spot2 的 moveID = 1094)并且该 Spot 之前至少访问过一次(moveID 1089 到 1091)的情况。

输出将是一个 moveID 列表和另一个 Status 列,显示 return 到先前访问过的 Spot 的移动。

Status moveID
-------------
 0    1085
 0    1086
 0    1087
 0    1088
 0    1089
 0    1090
 0    1091
 0    1092
 0    1093
 1    1094
 0    1095
 0    1096

这将为您提供给定 ID 之前访问过某个地点的所有移动

select m1.*
from moves m1
join moves m2 ON m1.spot = m2.spot and m1.moveID > m2.moveID
where m2.dir = 0

这将为您提供连续发生的动作

select distinct m1.*
from moves m1
join moves m2 ON m1.spot = m2.spot and m1.dir = m2.dir and m1.moveID > m2.moveID
where m1.dir = 1

我怀疑机器人坐在一个点上进行相邻动作是可以的。该问题稍后返回到较早的位置。

如果是这样,这就是间隙和孤岛问题的一个例子。这可以通过不同的行号来解决,以获得机器人在给定位置时的相邻移动:

select spot, min(moveid) as min_moveid, max(moveid) as max_moveid
from (select m.*,
             row_number() over (order by moveid) as seqnum,
             row_number() over (partition by spot order by moveid) as seqnum_s
      from moves m
     ) m
group by (seqnum - seqnum_s), spot;

为什么这行得通有点难以解释,但很容易看出。如果您 运行 子查询,您将看到行号的差异如何定义每个点。

现在,问题是给出每个点的移动范围 -- 一个点是否出现不止一次。为此,我们现在可以将 count(*) 用作 window 函数。因此,要获得重复移动的出现:

with s as (
      select spot, min(moveid), max(moveid)
      from (select m.*,
                   row_number() over (order by moveid) as seqnum,
                   row_number() over (partition by spot order by moveid) as seqnum_s
            from moves m
           ) m
      group by (seqnum - seqnum_s), spot
     )
select s.*
from (select s.*,
             count(*) over (partition by spot) as cnt
      from s
     ) s
where cnt > 1;

我相信这回答了你的问题。

这是问题的最终解决方案。这次我添加了基于更大数据集的分区和分组,这些数据集涵盖了不同生产线和制造工厂不同部分的机器人。

SELECT line, robot, min(moveid) moveIDmin, max(moveid) moveIDmax
        from (select m.*,
                     row_number() over (partition by line, robot order by moveid) as seqnum,
                     row_number() over (partition by line, robot, spot order by moveid) as seqnum_s
              from #Temp2 m
             ) m
        group by line, robot, (seqnum - seqnum_s), spot
        order by line, robot, moveIDmin

由此产生的输出可以轻松定位低效的移动,例如 moveIDmin 298,机器人已移回之前访问过的位置。

   line  robot spot moveIDmin moveIDmax
  AER389X  G    26       1        72
  AER389X  G    30      73       137
  AER389X  J     2     138       139
  AER389X  J     6     140       224
  AER389X  J    10     225       297
  AER389X  J     6     298       301
  AER389X  J    14     302       319
  AER389X  K    14     320       380
  AER389X  K    18     381       468