介绍
有时,您可以故意使用标量子查询来检查是否找到了不超过一行,而不是连接。例如,您可能有此查询来查找某些person
行的国籍。
select p.name,
c.iso
from person p
join person_country_map pcm
on p.id = pcm.person
join country c
on pcm.country = c.id
where p.id in (1, 2, 3)
现在,假设person_country_map
不是函数映射。一个给定的人可能会映射到多个国家 - 因此连接可能会找到不止一行。或者实际上,一个人可能根本不在映射表中,至少就任何数据库约束而言。
但是对于这个特定的查询,我碰巧知道我要查询的人将只有一个国家。这是我基于我的代码的假设。但我想在可能的情况下检查这个假设 - 这样如果出现问题并且我最终尝试为一个拥有多个国家或没有国家映射行的人执行此查询,它就会死掉。
最多为一行添加安全检查
要检查多于一行,可以将连接重写为标量子查询:
select p.name,
(
select c.iso
from person_country_map pcm
join country c
on pc.country = c.id
where pcm.person = p.id
) as iso
from person p
where p.id in (1, 2, 3)
现在,如果一个人查询到两个或多个国家的地图,DBMS 将给出错误。它不会像直接连接那样为同一个人返回多个条目。因此,即使在任何行返回到应用程序之前,我也可以更轻松地知道这个错误案例正在被检查。作为一个细心的程序员,我当然也可能会检查应用程序。
是否可以对 未 找到行进行安全检查?
但是如果 person_country_map 中没有一个人的行怎么办?在这种情况下,标量子查询将返回 null,使其大致相当于左连接。
(为了论证,假设从 person_country_map.country 到 country.id 的外键和 country.id 上的唯一索引,这样特定的连接将始终成功并准确找到一个国家行。)
我的问题
有什么方法可以在 SQL 中表达我想要一个且完全是一个结果吗?一个普通的标量子查询是“零或一”。我想能够说
select 42, (select exactly one x from t where id = 55)
如果子查询不返回行,则查询在运行时失败。当然,上面的语法是虚构的,我相信它不会那么容易。
我使用的是 MSSQL 2008 R2,实际上这段代码是在一个存储过程中,所以如果有必要我可以使用 TSQL。(显然,普通的声明性 SQL 更可取,因为它也可以在视图定义中使用。)当然,我可以进行exists
检查,或者我可以在 TSQL 变量中选择一个值,然后显式检查它是否为空,等等。我什至可以将结果选择到一个临时表中,然后在该表上构建唯一索引作为检查。但是,是否没有更易读和更优雅的方式来标记我的假设,即子查询只返回一行,并让 DBMS 检查该假设?