比较 SQL 中的两个关系

Posted

技术标签:

【中文标题】比较 SQL 中的两个关系【英文标题】:Compare Two Relations in SQL 【发布时间】:2020-05-04 06:39:44 【问题描述】:

我刚开始学习SQL,这是老师在在线课程中给出的演示,效果很好。该声明正在寻找“具有相同 GPA 的其他学生数量等于具有相同 sizeHS 的其他学生数量的学生”:

select *
from Student S1
where (
    select count(*)
    from Student S2
    where S2.sID <> S1.sID and S2.GPA = S1.GPA
) = (
    select count(*)
    from Student S2
    where S2.sID <> S1.sID and S2.sizeHS = S1.sizeHS
);

似乎在这个where 子句中,我们比较两个关系(因为子查询的结果是一个关系),但大多数时候我们是在比较属性(到目前为止正如我所见)。

所以我在考虑比较两个RELATION时,RELATION应该包含多少个属性,多少个元组有要求。如果不是,当有多个属性或多个元组时,我们如何比较两个 RELATIONS 以及我们得到的结果是什么注意

学生关系有4个属性:sID、sName、GPA、sizeHS。以下是数据

+-----+--------+-----+--------+ |标识 |姓名 |平均绩点 |尺寸HS | +-----+--------+-----+--------+ | 123 |艾米 | 3.9 | 1000 | | 234 |鲍勃 | 3.6 | 1500 | | 345 |克雷格 | 3.5 | 500 | |第456章多丽丝 | 3.9 | 1000 | |第567章爱德华 | 2.9 | 2000 | |第678章费 | 3.8 | 200 | |第789章加里 | 3.4 | 800 | | 987 |海伦 | 3.7 | 800 | |第876章艾琳 | 3.9 | 400 | | 765 |周杰伦 | 2.9 | 1500 | |第654章艾米 | 3.9 | 1000 | |第543章克雷格 | 3.4 | 2000 | +-----+--------+-----+--------+

这个查询的结果是:

+-----+--------+-----+---------+ |标识 |姓名 |平均绩点 |尺寸HS | +-----+--------+-----+---------+ | 345 |克雷格 | 3.5 | 500 | |第567章爱德华 | 2.9 | 2000 | |第678章费 | 3.8 | 200 | |第789章加里 | 3.4 | 800 | | 765 |周杰伦 | 2.9 | 1500 | |第543章克雷格 | 3.4 | 2000 | +-----+--------+-----+---------+

【问题讨论】:

请限制为一个数据库。什么是样本数据?你期待什么输出?对于一致的术语,数据库具有行和列以及sub查询。 mysql SQL Server Why should I tag my DBMS 现在可以了吗?对不起,我是学生,刚开始学习数据库。 【参考方案1】:

因为子查询的结果是一个关系

关系是我们在数据库中称为表的科学名称,我更喜欢“表”这个名称而不是“关系”。一张桌子很容易想象。例如,我们从学校的时间表中了解它们。是的,我们将这里内部的东西关联到一个表格(日期和时间以及学校教授的科目),但我们也可以将表格与表格联系起来(学生的时间表与表格教室、整体科目时间表和老师的时间表)。因此,RDBMS 中的表也相关彼此(因此名称​​关系数据库管理系统)。我发现表的名称关系非常混乱(许多人使用“关系”一词来描述 表之间的关系)。

所以,是的,查询结果本身又是一个表(“关系”)。我们当然可以从表格中选择:

select * from (select * from b) as subq;

还有一些标量查询只返回一行一列。 select count(*) from b 就是这样一个查询。虽然这仍然是一张我们可以从中选择的表格

select * from (select count(*) as cnt from b) as subq;

我们甚至可以在我们通常有单个值的地方使用它们,例如在 select 子句中:

select a.*, (select count(*) from b) as cnt from a;

在您的查询中,您的 where 子句中有两个标量子查询。

对于子查询,还有另一个区别:我们有相关和不相关的子查询。我刚刚展示的最后一个查询包含一个不相关的子查询。它为每个结果行选择 b 行的计数,无论该行包含什么,否则。另一方面,相关子查询可能如下所示:

select a.*, (select count(*) from b where b.x = a.y) as cnt from a;

这里,子查询与主表相关。对于每个结果行,我们查找与通过where b.x = a.y 显示的a 行匹配的b 行计数,因此计数因行而异(但对于共享相同y 值的行,我们会得到相同的计数)。

您的子查询也是相关的。与 select 子句一样,where 子句一次处理一行(为了保留或关闭它)。所以我们一次只看一个学生 S1。对于这个学生,我们计算具有相同 GPA (and S2.GPA = S1.GPA) 的其他学生 (S2, where S2.sID &lt;&gt; S1.sID) 并计算具有相同 sizeHS 的其他学生。我们只保留具有相同 GPA 的其他学生与具有相同 sizeHS 的学生数量完全相同的学生 (S1)。


更新

就像处理多个元组一样

select *
from Student S1
where (
    select count(*), avg(grade)
    from Student S2
    where S2.sID <> S1.sID and S2.GPA = S1.GPA
) = (
    select count(*), avg(grade)
    from Student S2
    where S2.sID <> S1.sID and S2.sizeHS = S1.sizeHS
);

这在某些 DBMS 中是可能的,但在 SQL Server 中是不可能的。 SQL Server 不知道元组。

但是还有其他方法可以达到同样的效果。您可以只添加两个子查询:

select * from student s1
where (...) = (...) -- compare counts here
and (...) = (...) -- compare averages here

或者获取FROM子句中的数据,然后进行处理。例如:

select *
from Student S1
cross apply
(
    select count(*) as cnt, avg(grade) as avg_grade
    from Student S2
    where S2.sID <> S1.sID and S2.GPA = S1.GPA
) sx
cross apply
(
    select count(*) as cnt, avg(grade) as avg_grade
    from Student S2
    where S2.sID <> S1.sID and S2.sizeHS = S1.sizeHS
) sy
where sx.cnt = sy.cnt and sx.avg_grade = sy.avg_grade;

【讨论】:

哇,谢谢。但是,我确实理解这个语句在做什么,我的问题是两个表之间的“=”(如你所说)。我了解我们如何比较属性而不是表格。我正在考虑在比较两个表时,表应该包含多少个属性和多少个元组。如果没有,当有多个属性或多个元组时,我们如何比较两个表,我们得到什么结果? 好的,这很简单,因为我们将一个数字与另一个数字进行比较(GPA 计数与 sizeHS 计数)。至于比较两个查询的元组(例如(year, month))而不是单个值,这取决于您的 DBMS。有些可以很好地处理元组,有些则不能。例如:select 'hello' where (1,2,3) = (1,2,3) 在 PostgreSQL 和 MySQL 中可以正常工作,但在 SQL Server 中根本不行,因为 SQL Server 不知道元组。 因此,如果您只想在不仅计数相等,而且平均成绩也相等时显示学生,则必须在 SQL Server 中找到其他方法。这包括使用集合(例如INTERSECT)和查找(通过[NOT] IN[NOT] EXISTS),或者您只需在from 子句中选择聚合以便随后处理它们。好吧,我想我最好用一个例子来更新我的答案...... 再次感谢您的更新!只是好奇,如果“=”左右两边的表都有多个元组(你刚才解释的是“多个属性”)怎么办?例如:select * from Student S1 where (select * from Student S2 where S2.sID &lt;&gt; S1.sID and S2.GPA = S1.GPA) = (select * from Student S2 where S2.sID &lt;&gt; S1.sID and S2.sizeHS = S1.sizeHS);,结果为 123|Amy|3.9|1000 456|Doris|3.9|1000 654|Amy|3.9|1000 当数据集有多行时,您不能使用=(即使可以,也只能意味着一个集合应该等于另一个集合,即所有行都相等)。如果我们想知道是否存在另一个 GPA 和 sizeHS 相同的学生,我们可以使用EXISTS【参考方案2】:

有关系operations:

交集运算符产生两个元组的集合 关系有共同点。交集是在 SQL 中实现的 INTERSECT 运算符的形式。 差分运算符作用于两个关系,并根据第一个关系生成第二个关系中不存在的元组集。差异在 SQL 中以 EXCEPT 或 MINUS 运算符的形式实现。

因此,例如,在 SQL Server 的上下文中,您可以这样做:

SELECT *
FROM R1 
EXCEPT
SELECT *
FROM R2 

获取R1 中不包含在R2 中的行,反之 - 获取所有差异。

当然,属性必须相同——如果不是,则需要在SELECT中显式设置属性。

【讨论】:

感谢您的回答。但这是老师给的demo,效果很好。我感到困惑的是两个关系之间的“=”,而不是两个属性之间的“=”。我正在考虑在比较两个关系时,关系应该包含​​多少个属性和多少个元组。如果没有,当有多个属性或多个元组时,我们如何比较两个关系,我们得到什么结果?

以上是关于比较 SQL 中的两个关系的主要内容,如果未能解决你的问题,请参考以下文章

比较过多关系中的两个属性的 CoreData 谓词

使用Red Gate Sql Compare 数据库同步工具进行SQL Server的两个数据库的结构比较同步

如何比较标准 SQL(BigQuery)中的两个数组?

比较 Oracle SQL 中的两个不同字段

跨数据库和层比较 SQL Server 中的两个表

比较 SQL 中的 Oracle 表列