假设我有以下架构:
学生(姓名、兄弟姐妹)
相关表有名称和同级表。请注意,同名的行数出现的次数与一个人拥有的兄弟姐妹的数量相同。例如,一个表可以如下所示:
杰克、露西
杰克、蒂姆
这意味着杰克有露西和蒂姆作为他的兄弟姐妹。我想要确定一个 SQL 查询,该查询报告有 2 个或更多兄弟姐妹的所有学生的姓名。我的尝试如下:
select name
from student
where count(name) >= 1;
Run Code Online (Sandbox Code Playgroud)
我不确定我在此 SQL 查询中是否正确使用了 count。有人可以帮忙确定正确的 SQL 查询吗?
你快到了:
select name
from student
group by name
having count(*) > 1;
Run Code Online (Sandbox Code Playgroud)
HAVING 是分组完成后运行的 where 子句。在其中,您可以使用分组提供的功能(例如计数和聚合)。通过对名称进行分组并计数(过滤>1,如果您想要两个或更多,而不是>=1因为这将包括 1),您将获得您想要的名称。
这只会将“Jack”作为单个结果提供(在问题的示例数据中)。如果您随后需要所有详细信息,例如杰克的兄弟姐妹是谁,您可以将分组、筛选的姓名列表加入回表中:
select *
from
student
INNER JOIN
(
select name
from student
group by name
having count(*) > 1
) morethanone ON morethanone.name = student.name
Run Code Online (Sandbox Code Playgroud)
您无法避免执行此“重新加入”操作,因为分组为了创建组而丢弃了详细信息。取回详细信息的唯一方法是获取小组给您的名单,并用它再次过滤原始详细数据
全面披露; 说“无法避免这样做”有点谎言:SQL Server 支持称为窗口函数的东西,它将在后台有效地执行分组并将其连接回详细信息。这样的查询看起来像:
select student.*, count(*) over(partition by name) n
from student
Run Code Online (Sandbox Code Playgroud)
对于这样的表:
jack, lucy
jack, tim
jane, bill
jane, fred
jane, tom
john, dave
Run Code Online (Sandbox Code Playgroud)
它将产生:
jack, lucy, 2
jack, tim, 2
jane, bill, 3
jane, fred, 3
jane, tom, 3
john, dave, 1
Run Code Online (Sandbox Code Playgroud)
因为有两行,所以的行jack会打开。有 3 个简,有 1 个约翰。然后,您可以将所有内容包装在子查询和过滤器中,以删除2jackn > 1john
select *
from
(
select student.*, count(*) over(partition by name) n
from student
) x
where x.n > 1
Run Code Online (Sandbox Code Playgroud)
如果 SQL Server 没有窗口函数,它看起来更像是:
select *
from
student
INNER JOIN
(
select name, count(*) as n
from student
group by name
) x ON x.name = student.name
Run Code Online (Sandbox Code Playgroud)
这COUNT(*) OVER(PARTITION BY name)就像一个迷你“按名称分组并返回计数,然后使用名称作为键自动连接回主要详细信息”,即后一个查询的简短形式