自联接表并仅使用一次行-DENSE_RANK()的正确用法

时间:2018-07-16 09:22:24

标签: sql sql-server sql-server-2008

首先,一些虚构数据:

方案: 我有一张桌子,上面记录着我所有的交易,例如库存变动和其他交易。我需要生成一个报告,该报告将显示进入特定create table #testm ( transdate date, item nvarchar(20), qty int, whse nvarchar(10), loc nvarchar(10) ) insert into #testm (transdate, item, qty, whse, loc) values ('20180601', '123', 100, 'main', 'qc'), ('20180602', '123', -100, 'main', 'qc'), ('20180603', '123', 100, 'main', 'qc'), ('20180604', '123', -100, 'main', 'qc'), ('20180602', '1234', 100, 'main', 'qc'), ('20180602', '1234', -100, 'main', 'notqc') 并退出该项目的数据(#testm表字段)。但是,我只需要对每个“出口”计数一次,因此它不会将自己与匹配项目和数量的其他“入口”联接起来。然后,我需要查看该项目在该loc中花费了多少时间。特殊情况是项目尚未离开loc时,它应该在loc中的datediff()中在查询的第二部分显示空值。

在上面的getdate()语句中,预期结果是将第1行与2、3、4、5完全匹配(因为还没有退出,而第6行的insert错误),并且跳过第6行,因为loc不是我要找的那个。

结果应该像这样

loc

这是我的尝试:

     transdate  item qty whse loc td2        it2  qt2   wh2     lo2 datediffhour
     2018-06-01 123  100 main qc  2018-06-02 123  -100  main    qc   24
     2018-06-02 1234 100 main qc  NULL       NULL NULL  NULL    NULL 1056
     2018-06-03 123  100 main qc  2018-06-04 123  -100  main    qc   24

2个问题:

它跳过了第5行

select * from ( select tin.transdate, tin.item, tin.qty, tin.whse, tin.loc, DENSE_RANK() over(partition by tin.transdate order by tout.transdate) as firstrank, DENSE_RANK() over (partition by tout.transdate order by tin.item) as secondrank, tout.transdate as td2, tout.item as it2, tout.qty as qt2, tout.whse as wh2, tout.loc as lo2, datediff(hour, tin.transdate, isnull(tout.transdate, getdate())) as datediffhour from #testm as tin left join #testm as tout on tin.item = tout.item and tin.whse = tout.whse and tin.transdate <= tout.transdate and tin.qty = -1*tout.qty where tin.loc = 'qc' and tout.loc = 'qc' ) as t where (firstrank = secondrank or t.td2 is null) and t.qty > 0 order by t.transdate secondrank只是猜测,因为我并不真正熟悉firstrank-我什至使用得还对吗?

3 个答案:

答案 0 :(得分:1)

我使用dense_rank()代替了t_in.transdate < t_out.transdate。这里只需要一个。请注意,我们有一个<=条件,而不是t_out.loc = 'qc'

显示第5行没有退出匹配项的关键是将条件WHERE设置为左联接条件而不是 INNER JOIN子句,因为在这里它的作用就像您正在应用{{1 }}

select 
  transdate, item, qty, whse, loc,
  td2, it2, qt2, wh2, lo2, datediffhour
from (
    select 
      t_in.transdate, t_in.item, t_in.qty, t_in.whse, t_in.loc,
      t_out.transdate as td2, t_out.item as it2, t_out.qty as qt2, t_out.whse as wh2, t_out.loc as lo2,
      datediff(hour, t_in.transdate, isnull(t_out.transdate, getdate())) as datediffhour,
      row_number() over (partition by t_in.item, t_in.transdate order by t_in.transdate) as rn
    from #testm t_in
    left join #testm t_out on
      t_in.item = t_out.item
      and t_in.whse = t_out.whse
      and t_in.qty = -1 * t_out.qty
      and t_in.transdate < t_out.transdate
      and t_out.loc = 'qc' 
    where 
      t_in.qty > 0
      and t_in.loc = 'qc'     
) t 
where rn = 1 -- pick up only first matching "exit"
order by transdate

答案 1 :(得分:0)

使用Row_Number()。当您连续在同一地点购买相同物品并随后出售(输入/退出)时,此功能将无效。

Select * from 
  (Select test1.*,      
      datediff(hour, test1.transdate, Coalesce(test2.transdate, getdate())) as datediffhour,
      row_number() over (partition by test1.item, test1.loc, test1.transdate order by test1.transdate) as ranking
    from #testm test1
    left join #testm test2 
    on test1.item = test2.item
   and test1.loc = test2.loc 
   and test1.whse = test2.whse
   and test1.qty + test2.qty = 0
   and test1.transdate < test2.transdate
    where test1.qty > 0)c
where ranking=1

答案 2 :(得分:0)

使用outer apply。像这样:

select t.*, tnext.*
from #testm t outer apply
     (select top (1) tnext.*
      from #testm tnext
      where tnext.loc = t.loc and tnext.item = t.item and
            tnext.transdate > t.transdate
      order by tnext.transdate asc
     ) tnext;