虎虎漫画小说

繁体版 简体版
虎虎漫画小说 > > SQL语言艺术最新章节 > 第 14 章

第 14 章 免费阅读

“动作”放到SQL 查询中,此时聚合函数特别有用。

优秀SQL编程的困难,多半在于解决问题的方式:不要将“一个问题”转换成对数据库的“一系列

查询”,而是要转换成“少数查询”。程序用大量中间变量保存从数据库读出的值,然后根据变量

进行简单判断,最后再把它们作为其他查询的输入……这样做是错误的。糟糕的SQL编程有个

显著特点,就是在SQL 查询之外存在大量代码,以循环的方式对返回数据进行些加、减、乘、

除之类的处理。这样做毫无价值、效率低下,这里工作应该jiāo给SQL的聚合函数。

注意:

聚合函数非常有用,可以解决不少SQL问题(第11章会再次讨论)。然而,我发现开发者通常只

使用最平常的聚合函数count(),它对大多数程序是否真的有用值得怀疑。

第2章说明了使用count(*)判定是否要更新记录(chā入新记录)是很浪费的。你可能在报表中误

用了count(*)。测试存在xìng有时会以模仿布尔值的方式实现:

case count(*)

when 0 then 'N'

else 'Y'

end

对于上述实现,只要存在与条件相符的记录,就会读取其中每条记录。其实,只需找到一条记

录就足以判断要显示Y 还是N,通过测试存在xìng或限制返回记录数可以写出更高效的语句,

一旦发现条件相符就停止处理即可。

当要解决的问题与最多、最少、最大、第一、最后有关时,聚合函数(可能会当成OLAP 函数

使用)很可能是最佳选择。也就是说,不要认为聚合函数仅支持count、sum、max、min、avg

等功能,否则就说明你还没有充分理解聚合函数。

有趣的是,聚合函数在作用范围上非常狭窄。除了计算最大值和最小值,它们唯一能做的就是

简单的算术运算:count()每遇到的一行加1;avg()一方面将字段值累加,另一方面不断加1计

数,最后进行除法运算。

聚合函数有时可取得令人吃惊的效果,比如通过sum就可以做很多事情。喜欢数学的朋友知道,

通过对数和次方函数,要在sum和乘积(product)之间转换有多简单。喜欢逻辑的朋友也会知

道OR 很依赖sum,而AND很依赖乘积。

下面通过简单的例子说明聚合的强大作用。假设要进行装运(shicomnt)处理,一次装运由一

些不同的订单组成,每张订单都必须分别做准备;只有装运涉及的每张订单都完成时装运才准

备就绪。问题就是,如何判断装运涉及的所有订单都已完成。

这样的情况常会发生,有多种方法可以判定装运是否就绪。最糟的方法是逐一判断每批装运,

而每批装运内部进行第二个循环,查看有多少张订单的order_complete字段值为“N”,并返回计

数为0 的装运ID。更好的解决方案是理解“‘N’值的不存在xìng测试”的意图,并用子查询(无论

是关系还是非关系)完成:

select shicomnt_id

from shicomnts

where not exists (select null from orders

where order_complete = 'N'

and orders.shicomnt_id = shicomnts.shicomnt_id)

如果表shicomnts上没有其他条件了,则上述方法很糟糕,当shicomnts表数据量大时(而且未完

成订单占少数),换成以下查询会更高效:

select shicomnt_id

from shicomnts

where shicomnt_id not in (select shicomnt_id

from orders

where order_complete = 'N')

上述查询也可以稍作变形,优化器比较喜欢这个变形,但要求orders表的shicomnt_ id字段上有

索引:

select shicomnts.shicomnt_id

from shicomnts

left outer join orders

on orders.shicomnt_id = shicomnts.shicomnt_id

and orders.order_complete = 'N'

where orders.shicomnt_id is null

另一个替代方案是借助集合cāo作,该集合cāo作会使用shicomnts主键索引,且对orders表进行全表

扫描:

select shicomnt_id

from shicomnts

except

select shicomnt_id

from orders

where order_complete = 'N'

注意,并非所有DBMS 都实现了except cāo作符,有的DBMS称之为minus。

还有一种方法。主要是对装运中所有订单执行逻辑AND cāo作,将order_complete为TRUE的订

单的ID返回。这类cāo作在现实中很常见。如前所述, AND 和乘法、OR 和加法之间关系密切。

关键是把诸如“Y” 和“N” 的flag值转换为0 和1,使用case 结构即可。要把order_complete

转成0 或1 的值可以这样写:

select shicomnt_id,

case when order_complete = 'Y' then 1

else 0

end flag

from orders

到目前为止,一切顺利。如果每批装运包含的订单数固定的话,则很容易对适当字段进行sum

后检查是否为预期订单数。然而,实际上希望每批装运的flag值相乘,并检查结果是0 或是1。

这个方法是可行的,因为只要有一张以0 表示的未完成订单,乘法的最后结果就是0。乘法运

算可由对数运行协助完成(虽然在以对数处理时,0 不是最简单的值),但我们这个例子要做的

甚至更简单。

我们想要的是“第一张订单已完成、且第二张订单已完成……且第n 张订单已完成”。德摩根定

律( laws of de Morgan)( 注4)告诉我们,这等价于“第一张订单未完成、或第二张订单未完成……

或第n 张订单未完成”的情况“不成立”。由于使用聚合时,OR 比AND 更容易处理。检查由

OR 连结的一连串条件是否不成立,比检查由AND 连结的一连串条件是否成立,要容易得多。

我们要考虑的真正“谓词(predicate)”是“订单未完成”,并对order_complete 标志作转换,如

果是N 就转换为1,如果是Y 就转换为0。之后,通过加总flag值,就可检查是否所有订单

的flag值都是0(都已完成)——如果总和是0,所有订单都已完成。

因此,查询可写成:

select shicomnt_id

from (select shicomnt_id,

case when order_complete = 'N' then 1

else 0

end flag

from orders) s

group by shicomnt_id

having sum(flag) =0

甚至可以写得更简洁:

select shicomnt_id

from orders

group by

松语文学免费小说阅读_www.16sy.com

『加入书签,方便阅读』