sql:选择列值与前一行不同的行

kjthegm6  于 2021-07-24  发布在  Java
关注(0)|答案(6)|浏览(431)

假设我有一个(mysql)数据库,按时间戳的增加排序:

Timestamp   System StatusA StatusB 
2011-01-01     A      Ok     Ok      
2011-01-02     B      Ok     Ok     
2011-01-03     A     Fail   Fail     
2011-01-04     B      Ok    Fail     
2011-01-05     A     Fail    Ok      
2011-01-06     A      Ok     Ok      
2011-01-07     B     Fail   Fail

如何为该系统选择statusa从上一行更改的行?statusb并不重要(我在这个问题中展示它只是为了说明对于每个系统可能有许多连续的行,其中statusa没有改变)。在上面的示例中,查询应该返回行2011-01-03(对于systema,statusa在2011-01-01和2011-01-03之间发生了变化)、2011-01-06、2011-01-07。
查询应该在表有成千上万条记录的情况下快速执行。
谢谢

wtlkbnrh

wtlkbnrh1#

SELECT a.*
FROM tableX AS a
WHERE a.StatusA <>
      ( SELECT b.StatusA
        FROM tableX AS b
        WHERE a.System = b.System
          AND a.Timestamp > b.Timestamp
        ORDER BY b.Timestamp DESC
        LIMIT 1
      )

但是你也可以试试这个(带索引) (System,Timestamp) :

SELECT System, Timestamp, StatusA, StatusB
FROM
  ( SELECT (@statusPre <> statusA AND @systemPre=System) AS statusChanged
         , System, Timestamp, StatusA, StatusB
         , @statusPre := StatusA
         , @systemPre := System
    FROM tableX
       , (SELECT @statusPre:=NULL, @systemPre:=NULL) AS d
    ORDER BY System
           , Timestamp
  ) AS good
WHERE statusChanged ;
dnph8jn4

dnph8jn42#

select a.Timestamp, a.System, a.StatusA, a.StatusB
from tableX as a
cross join tableX as b
where a.System = b.System
and a.Timestamp > b.Timestamp
and not exists (select * 
    from tableX as c
    where a.System = c.System
    and a.Timestamp > c.Timestamp
    and c.Timestamp > b.Timestamp
)
and a.StatusA <> b.StatusA;

更新注解:为什么不使用内部连接而不是交叉连接?
这个问题需要一个mysql解决方案。根据文件:
在mysql中,交叉连接在语法上等同于内部连接(它们可以互相替换)。在标准sql中,它们不是等价的。内部联接与on子句一起使用,否则使用交叉联接。
这意味着这些连接中的任何一个都可以工作。
与on一起使用的条件表达式是可以在where子句中使用的任何形式的条件表达式。通常,应该使用on子句作为条件来指定如何联接表,使用where子句来限制结果集中需要的行。
条件 a.System = b.System 可能属于“how to join tables”类别,因此在这种情况下使用内部联接会更好。
由于两者产生相同的结果,不同之处可能在于性能。要说哪个更快,我需要知道连接是如何在内部实现的——它们是使用索引还是散列来实现连接。

fwzugrvs

fwzugrvs3#

使用rownum
我在20000排有0.05秒

select a1.*
  from (select rownum R_NUM, TIMESTAMP, System, StatusA from TableX) a1 
  join (select rownum R_NUM, TIMESTAMP, SYSTEM, STATUSA from TABLEX) a2 
    on a1.R_NUM = a2.R_NUM+1 
 where a1.system = a2.system 
   and a1.StatusA != a2.StatusA
1dkrff03

1dkrff034#

这是一个略短的版本,逻辑相似。我经常测试这个,我确信它是有效的;主要是因为它消除了相关子查询(不存在的地方)。
“c”在那里是为了确保b在a的正下方-它表示c(在它们之间)不能被找到(通过空测试)。

SELECT a.Timestamp, a.System, a.StatusA, a.StatusB
FROM tableX AS a
JOIN tableX AS b
    ON a.System = b.System
    AND a.Timestamp > b.Timestamp
LEFT JOIN tableX AS c
    ON a.System = b.System
    AND a.Timestamp > c.Timestamp
    AND b.Timestamp < c.Timestamp
WHERE c.System IS NULL
    AND a.StatusA <> b.StatusA;
vm0i2vca

vm0i2vca5#

在mssql中,egor的答案对我很有用,只是做了一个小小的改动。必须将rownum语句替换为:

select row_number () over (order by TIMESTAMP) as R_NUM, ...
txu3uszq

txu3uszq6#

SELECT   a.*
FROM    (select row_number() over (partition by System order by Timestamp asc) as aRow, Timestamp, System, StatusA, StatusB from tableX) as a
left join (select row_number() over (partition by System order by Timestamp asc) as bRow, Timestamp, System, StatusA, StatusB from tableX) as b on a.aRow = b.bRow + 1 and a.System = b.System 
where (a.StatusA != b.StatusA or b.StatusA is null)

它将返回第一行和值不同的行。

相关问题