从表中选择不同的ID列表,在同一个表中具有最早的值

时间:2017-01-18 11:22:13

标签: sql sql-server greatest-n-per-group

我有下表,

 SDate         Id       Balance
 2016-01-01    ABC      3
 2016-01-01    DEF      7
 2016-01-01    GHI      2

 2016-02-01    ABC      6
 2016-02-01    DEF      4
 2016-02-01    GHI      8
 2016-02-01    XYZ      12

我需要编写一个查询,在日期范围内为我提供不同的ID列表(在此示例中为SDate >= '2016-01-01'SDate <= '2016-02-01'),但也为我提供了最早的余额从上表中我想看到的结果是,

 Id       Balance
 ABC      3
 DEF      7
 GHI      2
 XYZ      12

这可能吗?

更新

抱歉,我应该为每个日期指定Id是唯一的。

6 个答案:

答案 0 :(得分:2)

您可以使用派生表执行此操作,该表首先计算每个SDate值的最小Id值。然后使用此join返回到原始表,找到与这些值匹配的行的Balance

declare @t table(SDate date,Id nvarchar(3),Balance int);
insert into @t values ('2016-01-01','ABC',3),('2016-01-01','DEF',7),('2016-01-01','GHI',2),('2016-02-01','ABC',6),('2016-02-01','DEF',4),('2016-02-01','GHI',8),('2016-02-01','XYZ',12);

declare @StartDate date = '20160101';
declare @EndDate date = '20160201';

with d as
(
    select Id
          ,min(SDate) as MinSDate
    from @t
    where SDate between @StartDate and @EndDate
    group by id
)
select d.Id
      ,t.Balance
from d
    inner join @t t
        on(d.Id = t.Id
           and d.MinSDate = t.SDate
          );

输出:

Id  | Balance
----+--------
ABC | 3
DEF | 7
GHI | 2
XYZ | 12

答案 1 :(得分:1)

这应该可以通过窗口功能实现 - 您所要做的就是

  • 按ID分区
  • 分配行号,
  • 为每个ID选择第一行

示例:

select  id,
        balance
from    (
            select  id,
                    balance,
                    row_number() over( partition by id order by SDate ) as row_num
            from    table1
            where   SDate between '2016-01-01' and '2016-02-01'
        ) as a
where   row_num = 1

注意:这种方法的优点是它更灵活。假设你想要最早的2条记录,你可以改为where row_num <= 2

答案 2 :(得分:1)

您可以通过自联接实现此目标,这可能不是最快或最优雅的解决方案:

 CREATE TABLE #SOPostSample
    (
      SDate DATE ,
      Id NVARCHAR(5) ,
      Balance INT
    );

 INSERT INTO #SOPostSample
        ( SDate, Id, Balance )
 VALUES ( '2016-01-01', 'ABC', 3 ),
        ( '2016-01-01', 'DEF', 7 ),
        ( '2016-01-01', 'GHI', 2 ),
        ( '2016-02-01', 'ABC', 6 ),
        ( '2016-02-01', 'DEF', 4 ),
        ( '2016-02-01', 'GHI', 8 ),
        ( '2016-02-01', 'XYZ', 12 );

 SELECT t1.Id ,
        MIN(t2.Balance) Balance
 FROM   #SOPostSample t1
        INNER JOIN #SOPostSample t2 ON t1.Id = t2.Id
 GROUP BY t1.Id ,
        t2.SDate
 HAVING t2.SDate = MIN(t1.SDate);

 DROP TABLE #SOPostSample;

产地:

id   Balance
============
ABC  3
DEF  7
GHI  2
XYZ  12

这适用于样本数据,但请我快速编写更多数据进行测试。

答案 3 :(得分:1)

分析row_number()应该是最快的

select *
from (
    select
        t.*,
        row_number() over (partition by Id order by SDate) rn
    from your_table t
) t where rn = 1;

答案 4 :(得分:0)

您可以使用子查询

SELECT  Id ,
        ( SELECT TOP 1
                    Balance
          FROM      [TableName] AS T1
          WHERE     T1.Id = [TableName].Id
          ORDER BY  SDate
        ) AS Balance
FROM    [TableName]
GROUP BY Id;   

答案 5 :(得分:0)

这应该有效,如果SDate和Id在组合中是唯一的,则不需要为安全插入前1

SELECT  o.Id ,
        ( SELECT TOP 1
                    Balance
          FROM      tbl
          WHERE     Id = o.Id
                    AND SDate = MIN(o.SDate)
        ) Balance
FROM    tbl o
GROUP BY Id
HAVING  sDate BETWEEN '20160101' AND '20160201';