T-SQL中的row_number()

时间:2018-06-22 09:08:58

标签: sql sql-server tsql

我有一张有这种情况的桌子:

Table

使用我的代码

select
    ID_A,
    ID_B,
    Position,
    row_number() over (partition by ID_A, ID_B order by position) as row
from 
    TB_EXAMPLE
order by 
    ID_A, Position

我添加了一个row_number_a列,但是我想要row_number_b的情况。你有什么提示吗?

2 个答案:

答案 0 :(得分:3)

这是一个缺口和孤岛问题,您需要在Row_number之前为每行分配一个组号。有几种方法,以下是基于这样的事实,即两个序号之间的差异是相同的,只要其中一个序列没有间隔即可:

position rownum diff 
       1      1    0
       2      1    1
       3      1    2
       4      1    3
       5      2    3
       3      3    3
...
      22      1    21
      23      2    21
      24      2    22

现在,如果位置是连续的,则具有相同ID_A和ID_B的所有行将获得相同的差异,并且您可以在PARTITION BY中使用该值:

with cte as 
 (

    select
         ID_A
        ,ID_B
        ,Position
        ,position -- if position is not sequential: ROW_NUMBER()over(partition by ID_A order by position)
         - ROW_NUMBER()over(partition by ID_A,ID_B order by position) as grp
    from TB_EXAMPLE
 )
select 
    ID_A
   ,ID_B
   ,Position
   ,ROW_NUMBER()over(partition by ID_A, ID_B, grp order by position) as rn
from cte
order by ID_A,Position

根据您实际上要删除行的注释,可以简化为检查上一行的值是否与当前行的值相同。

with cte as 
 (

    select
         ID_A
        ,ID_B
        ,Position
        ,case when LAG(ID_A)over(order by position) = ID_A
               and LAG(ID_B)over(order by position) = ID_B
              then 'delete'
              else 'keep'
         end as flag
    from TB_EXAMPLE
 )
select *
from cte
where flag = 'delete'

看起来这仅基于ID_B的更改:

with cte as 
 (

    select
         ID_A
        ,ID_B
        ,Position
        ,case when LAG(ID_B)over(partition by ID_A order by position) = ID_B
              then 'delete'
              else 'keep'
         end as flag
    from TB_EXAMPLE
 )
select *
from cte
where flag = 'delete'

答案 1 :(得分:0)

您可以在没有CTE或子查询的情况下解决此问题。

在ID_A上进行分区时,如果先前的ID_B(按位置排序)不同,则row_number为1。 否则,是在ID_A和ID_B上分区的row_number。

SELECT 
id_a as id1, 
id_b as id2, 
position,
 (case 
  when id_b = lag(id_b) over (partition by id_a order by position)
  then row_number() over (partition by id_a, id_b order by position)
  else 1
  end) as row_number_b
FROM TB_EXAMPLE t
ORDER BY id_a, position;

但是要选择要删除的重复项吗?
如果职位没有空缺,那么在这种情况下使用EXISTS也可以。

SELECT * 
FROM TB_EXAMPLE t
WHERE exists 
(
    SELECT 1 
    FROM TB_EXAMPLE dup
    WHERE dup.ID_A = t.ID_A
      AND dup.ID_B = t.ID_B
      AND dup.position = t.position - 1
);

示例代码段

declare @TB_EXAMPLE table (id_a uniqueidentifier, id_b uniqueidentifier, position int identity(1,1));

declare @idA uniqueidentifier = newid();
declare @idB_1 uniqueidentifier = newid();
declare @idB_2 uniqueidentifier = newid();
declare @idB_3 uniqueidentifier = newid();
declare @idB_6 uniqueidentifier = newid();

insert into @TB_EXAMPLE (id_a, id_b) values
(@idA,@idB_1),(@idA,@idB_2)
,(@idA,@idB_3),(@idA,@idB_3),(@idA,@idB_3)
,(@idA,newid())
,(@idA,newid())
,(@idA,@idB_6),(@idA,@idB_6)
,(@idA,newid())
,(@idA,@idB_1),(@idA,@idB_2);

SELECT 
id_a as id1, 
id_b as id2, 
position,
 (case 
  when id_b = lag(id_b) over (partition by id_a order by position)
  then row_number() over (partition by id_a, id_b order by position)
  else 1
  end) as row_number_b
FROM @TB_EXAMPLE t
ORDER BY id_a, position;

--
-- The dups, using LAG
--
SELECT TOP 1 WITH TIES *
FROM @TB_EXAMPLE t
ORDER BY IIF(id_b = lag(id_b) over (partition by id_a order by position),1,2);

--
-- The dups, using EXISTS
--
SELECT * 
FROM @TB_EXAMPLE t
WHERE exists 
(
    SELECT 1 
    FROM @TB_EXAMPLE dup
    WHERE dup.ID_A = t.ID_A
      AND dup.ID_B = t.ID_B
      AND dup.position = t.position - 1
);