如何从表中选择多个列时,只将TOP语句应用于1列?

时间:2018-05-31 10:03:01

标签: sql sql-server tsql

我正在尝试从表中选择多个列,但我想根据一列选择最多一定数量的记录。我试过这个:

select roll_no ,marks as Percentage
from database
where marks in (select top (3) *
                from database
                where subject = ''
                order by marks desc) order by percentage desc

我收到错误:

  

当选择列表中只能指定一个表达式时   子查询不是用EXISTS引入的,也不是超过指定的数字   记录。

我也尝试过:

select roll_no ,marks as Percentage
from database
where marks in (select top (3) marks
                from database
                where subject = ''
                order by marks desc) order by percentage desc

为某些科目返回正确的结果,但对于其他科目,则会显示其他科目的最高分。

例如:

+---------+-------+
| roll_no | marks |
+---------+-------+
|10003    |  87   | 
|10006    |  72   | 
|10003    |  72   |    
|10002    |  67   |     
|10004    |  67   |  
+---------+-------+

如何正确构建查询?

示例数据:

+---------+-------+---------+
| roll_no | marks |subject  |
+---------+-------+---------+
|10001    |  45   | Maths   |
|10001    |  72   | Science |
|10001    |  64   | English |     
|10002    |  52   | Maths   |        
|10002    |  35   | Science |   
|10002    |  75   | English |      
|10003    |  52   | Maths   |        
|10003    |  35   | Science |   
|10003    |  75   | English | 
|10004    |  52   | Maths   |        
|10004    |  35   | Science |   
|10004    |  75   | English |  
+---------+-------+---------+

2 个答案:

答案 0 :(得分:0)

您可以使用窗口函数对marks列进行排名(具体为dense_rank,允许重复排名,同时保留顺序编号),然后返回排名为3或更少的所有行:

declare @t table(roll_no int identity(1,1),marks int);
insert into @t(marks) values(2),(4),(5),(8),(6),(1),(3),(2),(1),(8);

with t as
(
    select roll_no
            ,marks
            ,dense_rank() over (order by marks desc) as r
    from @t
)
select *
from t
where r <= 3;

输出:

+---------+-------+---+
| roll_no | marks | r |
+---------+-------+---+
|       4 |     8 | 1 |
|      10 |     6 | 1 |
|       5 |     6 | 2 |
|       3 |     5 | 3 |
+---------+-------+---+

答案 1 :(得分:0)

如果我是对的并且您正在为每个主题寻找最佳的3分,那么您可以通过以下方式获得它:

DECLARE @SelectedSubject VARCHAR(50) = 'Maths'

;WITH FilteredSubjectMarks AS
(
    SELECT
        D.Subject,
        D.Roll_no,
        D.Marks,
        MarksRanking = DENSE_RANK() OVER (ORDER BY D.Marks DESC)
    FROM
        [Database] AS D
    WHERE
        D.Subject = @SelectedSubject
)
SELECT
    F.*
FROM
    FilteredSubjectMarks AS F
WHERE
    F.MarksRanking <= 3