【问题标题】:SQL - How to compare changing column values without multiple sub-selectsSQL - 如何在没有多个子选择的情况下比较更改的列值
【发布时间】:2015-06-24 08:57:39
【问题描述】:

我正在编写一个 TSQL 查询。

我有下表,其中 A 列和 B 列偶尔会发生变化。 我对 A 或 B 与前一行相比发生变化的每一行感兴趣(或者当 previos 行不存在时,即第一行)。每个日期都是独一无二的。

Date                    A   B       SysId
2015-02-01 00:00:00.000 2   1201    949410
2015-01-01 00:00:00.000 3   1201    949410
2014-01-01 00:00:00.000 2   1201    949410
2013-01-01 00:00:00.000 2   1200    949410
2012-01-01 00:00:00.000 2   1200    949410
2011-01-01 00:00:00.000 2   1200    949410
2010-01-01 00:00:00.000 2   1200    949410
2009-01-01 00:00:00.000 2   1200    949410
2008-01-01 00:00:00.000 2   1200    949410
2007-01-01 00:00:00.000 2   1200    949410
2006-01-01 00:00:00.000 2   1200    949410
2005-01-01 00:00:00.000 2   1200    949410
2004-01-01 00:00:00.000 2   1200    949410
2003-01-01 00:00:00.000 2   1200    949410
2002-01-01 00:00:00.000 3   1200    949410
2001-01-01 00:00:00.000 2   1200    949410
2000-01-01 00:00:00.000 3   1200    949410
1999-01-01 00:00:00.000 3   1200    949410
1998-01-01 00:00:00.000 3   1200    949410
1997-01-01 00:00:00.000 3   1200    949410
1996-01-01 00:00:00.000 3   1200    949410
1995-01-01 00:00:00.000 3   1200    949410
1994-01-01 00:00:00.000 3   1200    949410
1993-01-01 00:00:00.000 3   1200    949410
1992-01-01 00:00:00.000 3   1200    949410
1991-01-01 00:00:00.000 3   1200    949410
1990-01-01 00:00:00.000 3   1200    949410
1989-01-01 00:00:00.000 3   1200    949410
1988-01-01 00:00:00.000 3   1200    949410
1987-01-01 00:00:00.000 3   1200    949410
1986-01-01 00:00:00.000 3   1200    949410
1985-01-01 00:00:00.000 3   1200    949410
1984-01-01 00:00:00.000 2   1200    949410

在这种情况下,结果应该是:

Date                    A   B       SysId
2015-02-01 00:00:00.000 2   1201    949410
2015-01-01 00:00:00.000 3   1201    949410
2014-01-01 00:00:00.000 2   1201    949410
2003-01-01 00:00:00.000 2   1200    949410
2002-01-01 00:00:00.000 3   1200    949410
2001-01-01 00:00:00.000 2   1200    949410
1985-01-01 00:00:00.000 3   1200    949410
1984-01-01 00:00:00.000 2   1200    949410

因为我们对 A 或 B 发生变化的第一行感兴趣。

我有一个极其丑陋和昂贵的选择,可以为我做到这一点:

SELECT Date, A, B, SysId
FROM SysHistory fb1
WHERE fb1.SysId = 949410
AND 
(
    (
        ((
            SELECT TOP 1 fb2b.A
            FROM SysHistory fb2b
            WHERE fb2b.Date < fb1.Date 
            AND fb2b.SysId = 949410
            order by Date DESC
        )) <> fb1.StatusId
        OR 
        ((
            SELECT TOP 1 fb2a.A
            FROM SysHistory fb2a
            WHERE fb2a.Date < fb1.Date 
            AND fb2a.SysId= 949410
            order by Date  DESC
        )) IS NULL
    )
    OR
    (
        ((
            SELECT TOP 1 fb3b.B
            FROM SysHistory fb3b
            WHERE fb3b.Date < fb3b.Date 
            AND fb3b.SysId= 949410
            order by Date DESC
        )) <> fb1.StatusId
        OR 
        ((
            SELECT TOP 1 fb3a.B
            FROM SysHistory fb3a
            WHERE fb3a.Date < fb1.Date 
            AND fb3a.SysId = 949410
            order by Date DESC
        )) IS NULL
    )
)
order by Date DESC

请注意,我从前一行获取顶部的 A 或 B 属性。由于前一行可能为空(在我们位于表的第一行的情况下),我还有一个用于 A 和 B 的 OR 语句来检查是否为空。

我觉得必须有更好的方法来做到这一点。

是否可以在 TSQL 中比较同一个子选择中的多个列?或者只是一般来说,您将如何改进此查询?有没有办法让它更紧凑或可能更快?

我想我的问题接近于最佳实践,但我觉得这在技术上是一个语法问题。

导入更新 我现在注意到我的查询实际上并没有给我想要的结果。所以上面的 SQL 查询似乎不起作用。这种情况下的结果应该是

Date                    A   B       SysId
2015-02-01 00:00:00.000 2   1201    949410
2015-01-01 00:00:00.000 3   1201    949410
2014-01-01 00:00:00.000 2   1201    949410
2003-01-01 00:00:00.000 2   1200    949410
2002-01-01 00:00:00.000 3   1200    949410
2001-01-01 00:00:00.000 2   1200    949410
1985-01-01 00:00:00.000 3   1200    949410
1984-01-01 00:00:00.000 2   1200    949410

结果却是:

Date                    A   B       SysId
2015-02-01 00:00:00.000 2   1201    949410
2015-01-01 00:00:00.000 3   1201    949410
2003-01-01 00:00:00.000 2   1200    949410
2002-01-01 00:00:00.000 3   1200    949410
2001-01-01 00:00:00.000 2   1200    949410
1985-01-01 00:00:00.000 3   1200    949410
1984-01-01 00:00:00.000 2   1200    949410

【问题讨论】:

  • 2013-01-01 00:00:00.000 为什么不包括这个日期。它的 B 与上一行不同
  • @GiorgiNakeuri - 数据显示在顶部,最新数据显示在顶部,但“以前的”按时间顺序定义。上一行是当前行下方显示的行。
  • GiorgiNakeuri,是的,我注意到了我的错误。现在应该更正了。

标签: sql .net sql-server tsql select


【解决方案1】:

您可以对数据应用ROW_NUMBER(),以便执行自联接以查找先前的行:

;WITH Numbered as (
  SELECT Date, A, B, SysId,
    ROW_NUMBER() OVER (ORDER BY Date desc) as rn
  FROM SysHistory fb1
  WHERE fb1.SysId = 949410
)
select n1.*
from Numbered n1
   left join
     Numbered n2
        on n1.rn = n2.rn - 1
where
  n2.Date is null or --If you want to include the earliest row
  n1.A <> n2.A or
  n1.B <> n2.B

结果(已将您的示例数据放入名为 @SysHistory 的表变量中,更改了上述查询以引用它,并将 Date 列转义为 [Date],因为使用类型名称作为列名称通常是个坏主意) :

Date                    A           B           SysId       rn
----------------------- ----------- ----------- ----------- --------------------
2015-02-01 00:00:00.000 2           1201        949410      1
2015-01-01 00:00:00.000 3           1201        949410      2
2014-01-01 00:00:00.000 2           1201        949410      3
2003-01-01 00:00:00.000 2           1200        949410      14
2002-01-01 00:00:00.000 3           1200        949410      15
2001-01-01 00:00:00.000 2           1200        949410      16
1985-01-01 00:00:00.000 3           1200        949410      32
1984-01-01 00:00:00.000 2           1200        949410      33

这似乎符合您的预期结果(除了我的额外专栏)

【讨论】:

  • 谢谢!但是您的回答是否考虑到我在更新中写的内容?无论哪种方式,我都会尝试您的查询,如果可行,请选择您的答案。
  • 是的,我已经尝试过您的解决方案并且它有效!谢谢你,这太天才了! PS:所有列名都是任意的。如果你愿意,你可以写 SysDate 而不是 date 以避免语法冲突。
猜你喜欢
  • 2019-05-09
  • 1970-01-01
  • 1970-01-01
  • 2016-09-07
  • 2021-12-19
  • 2021-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多