在其他领域的基础上获得最小的列

时间:2016-08-18 16:44:36

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

我有一个场景,我有

Id|rank|   date
1 | 7  |07/08/2015
1 | 7  |09/08/2015
1 | 8  |16/08/2015
1 | 8  |17/08/2015
1 | 7  |19/08/2015
1 | 7  |15/08/2015
2 | 7  |01/08/2015
2 | 7  |02/08/2015
2 | 8  |16/08/2015
2 | 8  |17/08/2015
2 | 7  |26/08/2015
2 | 7  |28/08/2015

我想要的解决方案是

1 | 7  |07/08/2015
1 | 8  |16/08/2015
1 | 7  |15/08/2015
2 | 7  |01/08/2015
2 | 8  |16/08/2015
2 | 7  |26/08/2015

即每个id和rank的块我想要最小的日期。 我已经尝试过使用while循环,因为有数千条记录需要2个小时才能加载。还有其他方法可以做。请建议。

3 个答案:

答案 0 :(得分:1)

对于每一行,使用必要的顺序给出唯一的行号。 (因为我得到Id比日期更重要,日期比排名更重要)。

使用移动了一行(d1.RowNum = d2.RowNum+1)的行号将结果表连接到自身。

仅选择加入“其他块”行(d1.Id <> d2.Id or d1.Rank <> d2.rank)的行。

根据移动方向和选定的表格,将选择最大或最小日期。

不要忘记“边缘情况” - 由于移位导致的行无法连接(这就是为什么不使用inner joind1.RowNum = 1条件。)

;WITH dataWithRowNums as (
    select Id, Rank, Date,
        RowNum = ROW_NUMBER() OVER (ORDER BY Id,date,rank) 
    from YourTable
)
select d1.Id, d1.Rank, d1.Date
from dataWithRowNums d1
left join dataWithRowNums d2 
    on d1.RowNum = d2.RowNum+1 and (d1.Id <> d2.Id or d1.Rank <> d2.rank)
where not d2.Id is null or d1.RowNum = 1

此代码返回与您的结果位不同的结果:

Id  Rank    Date
1   7   2015-08-07
1   8   2015-08-16
1   7   2015-08-19 <-- you've got here 2015-08-15 
2   7   2015-08-01
2   8   2015-08-16
2   7   2015-08-26

由于区块(等级8 Id 1)已于16/08开始,所以排名7的第15/08行与第一个区块(排名7 Id1)相关。

如果您仍然需要排序(因此15/08等级7与第二个块(rank7 id1)相关),那么您应该提供自己的RowSorting数据,然后在这里询问另一个任务的另一个解决方案)

答案 1 :(得分:0)

以下是使用row_number()

的查询
 ;WITH cte_rec
  as (SELECT Id,Rank,Date
                    ,ROW_NUMBER()OVER (partition by Id,Rank ORDER BY date) as RNO
        FROM YourTable)
       SELECT Id,Rank,Date
       FROM cte_rec
       WHERE RNO =1

答案 2 :(得分:0)

This is what I have tried and is running as expected
create table #temp
    (
           iden int identity(1,1),
           ID int,
           [rank] int,
           [date] date,
           dr_id int,
           rownum_id int,
           grouprecord int
    )


    Insert into #temp(id,rank,date)
    select 1 , 7  ,'07/08/2015'
    union all select  1 , 7  ,'09/08/2015'
    union all select  1 , 8  ,'08/16/2015'
    union all select  1 , 8  ,'08/17/2015'
    union all select  1 , 7  ,'08/19/2015'
    union all select  1 , 7  ,'08/15/2015'
    union all select  2 , 7  ,'08/01/2015'
    union all select  2 , 7  ,'08/02/2015'
    union all select  2 , 8  ,'08/16/2015'
    union all select  2 , 8  ,'08/17/2015'
    union all select  2 , 7  ,'08/26/2015'
    union all select  2 , 7  ,'08/28/2015'


    update t1
    set dr_id = t2.rn
    from #temp t1 inner join
    (select  iden, dense_rank() over(order by id) as rn from #temp) t2
    on t1.iden = t2.iden


    update t1
    set rownum_id = t2.rn
    from #temp t1 inner join
    (select  iden, row_number() over(partition by dr_id order by id) as rn from #temp) t2
    on t1.iden = t2.iden




    select *,row_number() over(order by iden)rn into #temp1 from
    (             
                         select t2.* 
                  from #temp t1 inner join #temp t2
                  on (t1.dr_id = t2.dr_id or t2.dr_id = (t1.dr_id +1) ) and ( t1.rank<>t2.rank or t2.dr_id = (t1.dr_id +1) )
                  and t2.iden = t1.iden  + 1
    )a





    declare @id int,@miniden int,@maxiden int,@maxid int
    set @id = 1
    select @maxid = max(iden) from #temp

    while exists(select 1 from #temp1 where rn = @id)
    begin
           Select @miniden = iden from #temp1
           where rn = @id

           Select @maxiden = iden from #temp1
           where rn = @id+1

           update #temp
           set grouprecord = @id +1
           where iden between @miniden and @maxiden

    IF(@maxiden IS NULL)
    BEGIN 
           Update #temp
           set grouprecord = @id +1
           where iden between @miniden and @maxid
    END

           set @id = @id + 1
           SET @miniden =NULL
           SET @maxiden = NULL
    end

    UPDATE #TEMP
    SET GROUPRECORD = 1
    WHERE GROUPRECORD IS NULL



    select min(date) as mindate,grouprecord from #temp
    group by grouprecord

谢谢大家的帮助:)