【问题标题】:SQL query based on multiple rows基于多行的 SQL 查询
【发布时间】:2015-07-31 21:07:10
【问题描述】:

我需要弄清楚如何在 SQL 中完成这项工作:

如果 C=1,则在 C=1 的行中找到 B 为 B 的负数的下一行,并且两行的 A 值相同。如果这 2 行之间的 D 差异大于 1 天,则返回 C=1 的那一行

样本数据

A       B        C        D
1       10.00    0        2015-01-01
1       15.00    1        2015-01-02
1      -15.00    0        2015-01-03
2        5.00    1        2015-01-03
2       -5.00    0        2015-01-05
3        1.00    1        2015-01-03
3        2.00    0        2015-01-04
3       -1.00    0        2015-01-05

预期输出:

2        5.00    1        2015-01-03
3        1.00    1        2015-01-03

【问题讨论】:

  • 如何定义“下一行”? SQL 表是无序的,因此任何有序的都由特定的列定义。
  • 好的,如果我们在 D(日期)ASC 之前订购,我应该说“下一行”

标签: sql sql-server rows


【解决方案1】:

如果“下一个”是指 d 列中的后一个值,则可以将条件转换为 exists 子句:

select t.*
from table t
where t.c = 1 and
      exists (select 1
              from table t2
              where t2.d >= datedadd(day, 1, t.d) and t2.a = t.a and
                    t2.b = - t.b
             );

注意:如果b 是浮点数,您可能需要一个容差,例如abs(t2.b + t.b) < 0.001

【讨论】:

  • 是的,我应该注意到“next”是在“order by D asc”的上下文中
【解决方案2】:

正如一位评论者所暗示的,你必须有某种“排序”......我已经介绍了一个表键,你可以将 Over/OrderBY 更改为已经存在的东西......但你必须有某种除了“here they are”之外的排序机制。

我并不是说我的查询是最短的查询...但是,当我有“时髦的业务规则”时,我会选择稍微冗长而不是简洁,所以如果我必须回去维护它,我可以以某种有意义的方式分解“部分”。

这是一个能让你大部分时间到达那里的答案。 基本上,我会根据您的标准去寻找“MagicNewRow”。 但它之所以有效,是因为我能够根据某种排序机制创建一个 ComputedRowId。 (正如我之前解释的)

declare @holder table (TableKey int identity(1,1) , 
Col1 int, Col2 decimal, Col3 bit , Col4 smalldatetime , ComputedRowID int )
insert into @holder (Col1, Col2, Col3, Col4)
select 1  ,     10.00 ,   0,        '2015-01-01'
union all select 1  ,     15.00,1,        '2015-01-02'
union all select 1  ,    -15.00,    0,        '2015-01-03'
union all select 2  ,      5.00,    1,        '2015-01-03'
union all select 2  ,     -5.00,    0,        '2015-01-05'
union all select 3  ,      1.00,    1,        '2015-01-03'
union all select 3  ,      2.00,    0,        '2015-01-04'
union all select 3  ,     -1.00,    0,        '2015-01-05'

--select * from @holder

Update @holder Set ComputedRowID
= derived1.ROWID
from
@holder hold join

--Select * from
(
select TableKey, Col1, Col2, Col3, Col4 , ROW_NUMBER() over (order by TableKey) as ROWID
from @holder) as derived1
on hold.TableKey = derived1.TableKey
/* 
f C=1 then find the next row where B is the negative of B in the row
 where C=1 and the value of A is the same for both rows. 
 If the difference in D between those 2 rows is greater than 1 day, 
then return that row where C=1*/

;WITH
  cteCIsOne /*Col1, Col2, Col3, Col4, ROWID )*/
  AS
  (
    Select holderAlias.Col1, holderAlias.Col2, holderAlias.Col3, holderAlias.Col4, holderAlias.ComputedRowID
    from @holder holderAlias
    where Col3 = 1
  )
  ,
    cteRowsGreaterThanCurrentRowIdAndNegRuleApplies /*(Col1, Col2, Col3, Col4, ROWID )*/
  AS
  (
    Select holderAlias.Col1, holderAlias.Col2, holderAlias.Col3, holderAlias.Col4,
     holderAlias.ComputedRowID, 
     MagicNextRowComputedRowID = (select top 1 ComputedRowID from cteCIsOne cte1 
        where holderAlias.ComputedRowID > cte1.ComputedRowID and holderAlias.col2 = (-1 * cte1.col2) )
    from @holder holderAlias
  )

Select Col1, Col2, Col3, Col4 , ComputedRowID , MagicNextRowComputedRowID, MagicNextRowDate
, MyDateDiff = datediff(d, MagicNextRowDate, Col4)
from
(
SELECT Col1, Col2, Col3, Col4 , ComputedRowID , MagicNextRowComputedRowID
,
MagicNextRowDate = (select top 1 Col4 from @holder hold where hold.ComputedRowID = cteAlias2.MagicNextRowComputedRowID)
from cteRowsGreaterThanCurrentRowIdAndNegRuleApplies cteAlias2
) as derived
where derived.MagicNextRowComputedRowID IS NOT NULL 
and
datediff(d, MagicNextRowDate, Col4) > 1

【讨论】:

    【解决方案3】:
    select m1.*
    from myTable m1
    inner join myTable m2
    on m1.a = m2.a                     -- same a
    and m1.b = m2.b * -1               -- b is the negative of b
    and m1.d < m2.d                    -- the next row
    and dateadd(day, 1, m1.d ) > m2.d  -- difference is greater than 1 day
    where m1.c = 1
    
    union all 
    
    select m2.*
    from myTable m1
    inner join myTable m2
    on m1.a = m2.a
    and m1.b = m2.b * -1
    and m1.d < m2.d
    and dateadd(day, 1, m1.d ) <= m2.d
    where m1.c = 1
    

    【讨论】:

    • "where c = 1" 在上面的每个选择中都是模棱两可的。它需要一个表名,但我不确定它应该是 m1.c 和 m1.c 还是 m1.c 和 m2.c
    • 查看编辑,两者都是 m1.c。另请注意,在第二部分我选择 m2.* 而不是 m1.*
    猜你喜欢
    • 2020-01-27
    • 2023-01-03
    • 1970-01-01
    • 2014-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-24
    相关资源
    最近更新 更多