select 来自 table 的不同 ID 列表,其中最早的值在同一 table 中
select distinct list of ids from table with earliest value in same table
我有以下 table,
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'
),但也要给我最早的余额,以便结果从上面的table我想看到的是,
Id Balance
ABC 3
DEF 7
GHI 2
XYZ 12
这可能吗?
更新
抱歉,我应该指定每个日期的 ID 都是唯一的。
您可以使用派生的 table 来执行此操作,它首先计算出每个 Id
值的最小 SDate
值。然后使用它 join
回到原来的 table 以找到与这些值匹配的行的 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
这应该可以通过 window 函数实现 - 您所要做的就是
- 按id分区
- 指定行号,
- select 每个 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
.
您可以使用sub-query
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;
您可以通过自联接实现此目的,这可能不是最快或最优雅的解决方案:
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
这适用于示例数据,但请使用更多数据进行测试,因为我刚刚快速编写了它。
这应该有效,为安全起见刚刚插入 Top 1,如果 SDate 和 Id 的组合是唯一的,则不需要
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';
解析row_number()
应该是最快的
select *
from (
select
t.*,
row_number() over (partition by Id order by SDate) rn
from your_table t
) t where rn = 1;
我有以下 table,
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'
),但也要给我最早的余额,以便结果从上面的table我想看到的是,
Id Balance
ABC 3
DEF 7
GHI 2
XYZ 12
这可能吗?
更新
抱歉,我应该指定每个日期的 ID 都是唯一的。
您可以使用派生的 table 来执行此操作,它首先计算出每个 Id
值的最小 SDate
值。然后使用它 join
回到原来的 table 以找到与这些值匹配的行的 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
这应该可以通过 window 函数实现 - 您所要做的就是
- 按id分区
- 指定行号,
- select 每个 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
.
您可以使用sub-query
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;
您可以通过自联接实现此目的,这可能不是最快或最优雅的解决方案:
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
这适用于示例数据,但请使用更多数据进行测试,因为我刚刚快速编写了它。
这应该有效,为安全起见刚刚插入 Top 1,如果 SDate 和 Id 的组合是唯一的,则不需要
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';
解析row_number()
应该是最快的
select *
from (
select
t.*,
row_number() over (partition by Id order by SDate) rn
from your_table t
) t where rn = 1;