SQL Server:根据另一个表中条目的频率选择条目
Posted
技术标签:
【中文标题】SQL Server:根据另一个表中条目的频率选择条目【英文标题】:SQL Server: Select entry based on frequency of entries in another table 【发布时间】:2017-11-03 16:35:16 【问题描述】:所以我使用的是 SQL Server,并且我有两个类似于下面的表: 人 个人 ID 姓名 1 鲍勃 2 比尔 3 芭芭拉 4 邦妮
类 个人 ID 班级 1 数学 1 科学 2 数学 2 英语 3 科学 3 英语 4 英语 4 数学 4 科学
我需要编写一个查询来返回参加最多课程的人的姓名,并且只返回姓名。因此,对上述案例运行查询后的唯一结果应该是名称“Bonnie”。
在平局的情况下,应返回多个名称。
我的尝试如下:
`Select People.Name
from People inner join Classes
On People.PersonID = Classes.PersonID
Group by People.Name
Having max(Classes.PersonID)`
最后一行在 SQL Server 中不起作用,我终其一生都无法弄清楚如何重新编写代码以使其正常运行。
有什么想法吗?
【问题讨论】:
你的 HAVING 是不完整的。您需要添加类似 = 4 的内容,但它也存在逻辑错误,因为您不知道 PersonID。因此,您不想查看该列。你想看看计数。 【参考方案1】:SELECT TOP 1
name
FROM (
SELECT name,
COUNT(p.PersonID) as cnt
FROM People p
JOIN Classes c
ON p.PersonID = c.PersonID
GROUP BY name
) a
ORDER BY cnt DESC
【讨论】:
是的,这非常有助于澄清事情。【参考方案2】:试试这个
WITH CTE
AS
(
SELECT
SeqNo = ROW_NUMBER() OVER(ORDER BY COUNT(1) DESC),
P.PersonId,
Cnt = COUNT(1)
FROM Person p
INNER JOIN Classes C
ON P.PersonId = C.PersonId
GROUP BY p.PersonId
)
SELECT
*
FROM Person P2
WHERE EXISTS
(
SELECT 1 FROM CTE WHERE PersonId = p2.PersonId AND SeqNo = 1
)
【讨论】:
【参考方案3】:T-SQL 提供ranking functions,允许您根据字段或聚合计算排名。
鉴于这些表格:
declare @people table (personid int primary key,name nvarchar(20))
declare @classes table (personid int ,class nvarchar(20))
insert into @people(personid,name)
values
(1,'Bob'),
(2,'Bill'),
(3,'Barbara'),
(4,'Bonnie'),
(5,'Joe')
insert into @classes (personid,class)
values
(1,'Math'),
(1,'Science'),
(2,'Math'),
(2,'English'),
(3,'Science'),
(3,'English'),
(4,'English'),
(4,'Math'),
(4,'Science')
(5,'Science')
下面的查询会根据所学的课数计算个人排名:
select p.name,count(*) As Classes,rank() over (order by count(*) desc) as Rank
from @classes c
inner join @people p on p.personid=c.personid
group by p.name
返回 Bonnie 有 3 个类。所有其他学生在 2 个班级中排名第二:
name Classes Rank
------- ------- ----
Bonnie 3 1
Barbara 2 2
Bill 2 2
Bob 2 2
Joe 1 5
RANK
如果有平局,将跳过位置。这就是Joe
排名为 5 的原因。
您不能在 WHERE 或 HAVING 子句中使用排名函数。要仅返回第一个学生,您需要使用子查询或 CTE,例如:
select name,classes
from (
select p.name,count(*) As Classes,rank() over (order by count(*) desc) as Rank
from @classes c
inner join @people p on p.personid=c.personid
group by p.name
) r
where rank=1
或者
;with r as (
select p.name,count(*) As Classes,rank() over (order by count(*) desc) as Rank
from @classes c
inner join @people p on p.personid=c.personid
group by p.name
)
select name,classes
from r
where rank=1
两个查询都会返回:
name Classes
------- -------
Bonnie 3
如果您想找到 N 个最好的学生,您应该使用 DENSE_RANK 并返回排名小于或等于 N 的行。DENSE_RANK
Joe 的排名将为 3。
以下查询将返回前两个位置的学生:
with r as (
select p.name,count(*) As Classes,dense_rank() over (order by count(*) desc) as Rank
from @classes c
inner join @people p on p.personid=c.personid
group by p.name
)
select name,classes
from r
where rank<=2
【讨论】:
【参考方案4】:使用 TOP 查询获取大多数类的人员 ID。然后从人员表中选择人员的姓名:
select name
from people
where people_id in
(
select top(1) with ties person_id
from classes
group by person_id
order by count(*) desc
);
【讨论】:
【参考方案5】:好的,我通过Having COUNT(*) >= ALL (Select..)
命令行找到了最有效的方法。
代码如下:Select People.Name as 'Most Classes Taken:'
from People inner join Classes
On People.PersonID = Classes.PersonID
Group by People.Name
Having count(*)>=ALL (Select count(*) from Classes group by Classes.PersonID)
【讨论】:
以上是关于SQL Server:根据另一个表中条目的频率选择条目的主要内容,如果未能解决你的问题,请参考以下文章
添加列通过从sql Server中的另一个表中选择所有项目来选择表