【发布时间】:2015-05-07 03:32:49
【问题描述】:
简介
有时,您可以故意使用标量子查询来检查是否找到了不超过一行,而不是连接。例如,您可能有此查询来查找某些 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 检查该假设?
【问题讨论】:
-
你有没有考虑过一个用户定义的函数,如果没有一个结果就抛出异常?
-
@HABO,你的意思是一个通用的 check_not_null(x) 函数,如果 x 不为 null,则返回 x,否则返回 barfs(比如触发除以零)?
-
我在想
GetCountryForUser( UserId )。它要么返回指定用户的唯一CountryId,要么抱怨。不幸的是,UDF 中不允许使用RaIsError和Throw,因此您会得到类似 this 的内容。
标签: sql-server-2008 tsql join cardinality scalar-subquery