SQL查询,在一个ID范围内返回每个ID的前N行
假设我有一个包含数亿行的表,看起来像这样:SQL查询,在一个ID范围内返回每个ID的前N行,sql,sql-server,database,optimization,Sql,Sql Server,Database,Optimization,假设我有一个包含数亿行的表,看起来像这样: memID | foo | bar | foobar 1 | blah | blah | blah 1 | blah | blah | blah 1 | blah | blah | blah 1 | blah | blah | blah 1 | blah | blah | blah 1 | blah | blah | blah 1 | blah | blah | blah 2 | bl
memID | foo | bar | foobar
1 | blah | blah | blah
1 | blah | blah | blah
1 | blah | blah | blah
1 | blah | blah | blah
1 | blah | blah | blah
1 | blah | blah | blah
1 | blah | blah | blah
2 | blah | blah | blah
2 | blah | blah | blah
2 | blah | blah | blah
2 | blah | blah | blah
2 | blah | blah | blah
.
.
.
10001 | blah | blah | blah
10001 | blah | blah | blah
我需要一个查询,该查询将返回成员ID范围内每个memID的前N行。
例如,如果N=3且范围为0-2,则应返回
memID | foo | bar | foobar
1 | blah | blah | blah
1 | blah | blah | blah
1 | blah | blah | blah
2 | blah | blah | blah
2 | blah | blah | blah
2 | blah | blah | blah
我考虑了几种方法,首先创建一个大规模的
SELECT TOP (3) *
FROM table
WHERE memID = 0
UNION ALL
SELECT TOP (3) *
FROM table
WHERE memID = 1
.
.
.
用代码查询。这并不是一个现实的选择,原因可能很明显
第二种方法是创建一个临时表,并在memID范围内循环,将每个memID的前3个插入到临时表中
WHILE @MemID < 10000 AND @MemID > 0
BEGIN
INSERT INTO tmp_Table
SELECT TOP (3) *
FROM table
WHERE memID = @MemID
SET @MemID = @MemID + 1
END
这是可行的,但我想知道是否有一个更优雅的单一查询解决方案,我错过了
Cadaeic给了我一个不需要修改的答案,但感谢所有建议分析的人,看起来我有一些严肃的阅读要做。如果您使用的是SQL Server 2005或2008,您可能希望调查使用分析的原因。我没有测试这个,但应该很接近:
SELECT memID, foo, bar, foobar
FROM (
SELECT memID, foo, bar, foobar,
RANK() OVER (PARTITION BY memID ORDER BY memID) AS 'nRank'
FROM table
WHERE memID BETWEEN 0 AND 2)
WHERE nRank <= 3
我们应该这样做
SQL> select ename,sal,
2 row_number()
3 over (order by sal desc)rn,
4 rank()
5 over (order by sal desc)rnk,
6 dense_rank()
7 over (order by sal desc)drnk
8 from emp
9 order by sal desc
10 /
ENAME SAL RN RNK DRNK
----- ---- -- --- ----
KING 5000 1 1 1
FORD 3000 2 2 2
SCOTT 3000 3 2 2
JONES 2975 4 4 3
BLAKE 2850 5 5 4
CLARK 2450 6 6 5
如果您想在本地进行测试,以下是设置代码:
create table #test
(
memID int not null
, foo char(4) not null
, bar char(4) not null
, foobar char(4) not null
)
insert into #test (memID, foo, bar, foobar)
select 1, 'blah', 'blah', 'blah'
union all
select 1, 'blah', 'blah', 'blah'
union all
select 1, 'blah', 'blah', 'blah'
union all
select 1, 'blah', 'blah', 'blah'
union all
select 1, 'blah', 'blah', 'blah'
union all
select 1, 'blah', 'blah', 'blah'
union all
select 1, 'blah', 'blah', 'blah'
union all
select 2, 'blah', 'blah', 'blah'
union all
select 2, 'blah', 'blah', 'blah'
union all
select 2, 'blah', 'blah', 'blah'
union all
select 2, 'blah', 'blah', 'blah'
union all
select 10001, 'blah', 'blah', 'blah'
union all
select 10001, 'blah', 'blah', 'blah'
union all
select 10001, 'blah', 'blah', 'blah'
定义“顶部”。您需要指定一些条件,为这些条件选择具有给定id的行。编辑以使TOP是一个函数更清楚。是否有主键?这会让事情变得容易得多。@blogsdon:我不认为乔尔是这个意思。你根据什么标准判断哪一行在前三名,哪一行不在前三名?Top通常伴随着一个命令来定义顶部的内容。或者它更像是最多三个任意样本行?哦,我明白了,除了它们都需要相同的memID之外,没有其他标准。因此,是的,您对最多三个任意样本行的描述是正确的。这是有效的。感谢所有建议分析的人,我有很多阅读要做!为什么在每个内部查询中保留select子句?为什么不使用IN语句执行外部选择,在IN语句中运行只返回主键的内部查询呢?您也可以在主键上进行类似的连接。。。
declare @startID int, @endID int, @rowsEach int
select @startID = 0, @endID = 2, @rowsEach = 3
select *
from
(
select memID, foo, bar, foobar, row_number() over (partition by dense_rank order by dense_rank) [rank_row]
from
(
select memID, foo, bar, foobar, dense_rank() over (order by memID) [dense_rank]
from #test
where memID between @startID and @endID
) a
) b
where rank_row <= @rowsEach
memID foo bar foobar rank_row
----------- ---- ---- ------ --------------------
1 blah blah blah 1
1 blah blah blah 2
1 blah blah blah 3
2 blah blah blah 1
2 blah blah blah 2
2 blah blah blah 3
create table #test
(
memID int not null
, foo char(4) not null
, bar char(4) not null
, foobar char(4) not null
)
insert into #test (memID, foo, bar, foobar)
select 1, 'blah', 'blah', 'blah'
union all
select 1, 'blah', 'blah', 'blah'
union all
select 1, 'blah', 'blah', 'blah'
union all
select 1, 'blah', 'blah', 'blah'
union all
select 1, 'blah', 'blah', 'blah'
union all
select 1, 'blah', 'blah', 'blah'
union all
select 1, 'blah', 'blah', 'blah'
union all
select 2, 'blah', 'blah', 'blah'
union all
select 2, 'blah', 'blah', 'blah'
union all
select 2, 'blah', 'blah', 'blah'
union all
select 2, 'blah', 'blah', 'blah'
union all
select 10001, 'blah', 'blah', 'blah'
union all
select 10001, 'blah', 'blah', 'blah'
union all
select 10001, 'blah', 'blah', 'blah'