找到正在检查行的SQL标量子查询



简介

有时,您可以故意使用标量子查询来检查是否只找到一行,而不是联接。例如,您可以使用此查询来查找某些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.id到country.id,并在country.id上有一个唯一索引,这样特定的联接总是成功的,并且只找到一个国家行。)

我的问题

有没有什么方法可以在SQL中表达我想要一个确切的结果?纯标量子查询是"零或一"。我想说

select 42, (select exactly one x from t where id = 55)

如果子查询不返回行,则在运行时使查询失败。当然,上面的语法是虚构的,我相信它不会那么容易。

我使用的是MSSQL2008R2,实际上此代码位于存储过程中,因此如果需要,我可以使用TSQL。(显然,普通的声明性SQL更可取,因为它也可以用于视图定义。)当然,我可以进行exists检查,或者我可以在TSQL变量中选择一个值,然后显式检查它是否为null,等等。我甚至可以在临时表中选择结果,然后在该表上构建唯一索引作为检查。但是,没有比这更可读、更优雅的方法来标记我的假设,即子查询只返回一行,并由DBMS检查该假设吗?

你让这比需要的更难

当然,你需要在person.id到person_country_map.erson 上建立FK关系

您对person_country_map.erson有唯一约束,还是没有
如果您没有唯一的约束,那么是的,您可以为同一个人拥有多个记录_country_map.persons.

如果你想知道你是否有任何重复,那么

select pcm.person 
from person_country_map pcm
group by  pcm.person  
having count(*) > 1

如果有不止一个,那么你只需要确定哪一个

select p.name,
       min(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)
group by p.name

在MSSQL中,isnull似乎只在第一个参数为null时计算第二个参数。所以一般来说,你可以说

select isnull(x, 0/0)

以给出一个查询,该查询在非null的情况下返回CCD_。将其应用于标量子查询

select 42, isnull((select x from t where id = 55), 0/0)

将保证CCD_ 11子查询正好找到一行。如果不止一个,DBMS本身就会产生错误;如果没有行,则触发除以零。

将此应用于原始示例会产生代码

select p.name, -- Get the unique country code of this person. -- Although the database constraints do not guarantee it in general, -- for this particular query we expect exactly one row. Check that. -- isnull(( select c.iso from person_country_map pcm join country c on pc.country = c.id where pcm.person = p.id ), 0/0) as iso from person p where p.id in (1, 2, 3)

为了获得更好的错误消息,您可以使用转换失败而不是除以零:

select 42, isnull((select x from t where id = 55), convert(int, 'No row found'))

尽管如果从子查询中获取的值本身不是CCD_ 15,则需要进一步的CCD_。

最新更新