前言废话——sql是程序员的饭碗,繁琐but万能,但能干并不意味着适合干,每当多表关联寻找外键时,我都在经历一种没有选择的痛苦。sql不完美,但长期代码让人无暇顾及完美,再痛苦的呐喊到最后都归于疲倦已极的无奈,就在还愿意记下它们的时候存个档吧。午饭吃饱了,打个盹,做个梦去。

正文:

发明SQL的主要目的是为结构化数据提供一种屏弊数据物理存储方案的访问方法,因此SQL中大量使用了类英语的词汇和语法以降低理解和书写困难。作为SQL基础理论的关系代数是个完备的计算体系,原则上可以计算一切。这样看来,我们理所应当地用SQL完成各种数据计算需求。

但是,尽管关系数据库取得了巨大的成功,SQL却显然没有达到其发明初衷,除了极少数简单的查询可由终端用户采用SQL完成外,绝大多数的SQL使用者仍是技术人员,甚至许多复杂的查询对技术人员也不是件容易的事。

通过一个很简单的例子来考察SQL在计算方面的缺点。

假设有一个由三个字段构成的销售业绩表sales_amount(为了简化问题,省去日期信息):

Sales         销售员姓名,假定无重名

Product    销售的产品

Amount    该销售员在该产品上的销售额

现在我们想知道空调和电视销售额都在前10名的销售员名单。这个问题很简单,人们会很自然地设计出如下计算过程:a、先按空调销售额排序,找出前10名;b、再按电视销售额排序,找出前10名;c、对a、b的结果取交集。

用SQL做:

a、找出空调销售额前10名,这很简单:

select top 10 sales from sales_amount where product='AC' order by amount desc

b、找出电视销售额前10名,动作一样:

select top 10 sales from sales_amount where product='TV' order by amount desc

c、求1、2的交集。这有点麻烦,因为SQL不支持步骤化、上两步的计算结果无法保存,所以只能重抄一遍:

select * from

( select top 10 sales from sales_amount where product='AC' order by amount desc )

intersect

( select top 10 sales from sales_amount where product='TV' order by amount desc )

一个只3步的简单计算得用SQL写成这样,而日常计算多达10几步的比比皆是,这显然超出来许多人的可接受能力。

这样,我们知道SQL的第一个重要缺点:不支持步骤化。把复杂的计算分步可以在很大程度地降低问题的难度,反过来,把多步计算汇成一步则很大程度地提高了问题的难度。

如果老师要求小学生做应用题时只能列一个算式完成,小朋友们会多么苦恼(当然,不乏一些聪明孩子搞得定)。

SQL查询不能分步,但用SQL写出的存储过程可以分步,那么用存储过程是否可以方便地解决这个问题呢?

不提使用存储过程的技术环境有多复杂(这足以令大多数人却步了)和数据库的差异性造成的不兼容,我们只从理论上来看用分步SQL是否能让这个计算更简捷些。

a、计算空调销售额前10名。语句还是那样,但我们需要把结果存起来供第3步用,而SQL中只能用表存储集合数据,这样我们要建一个临时表:

create temporary table x1 as

select top 10 sales from sales_amount where product='AC' order by amount desc

b、计算电视销售额前10名。类似地:

create temporary table x2 as

select top 10 sales from sales_amount where product='TV' order by amount desc

c、求交集,前面麻烦了,这步就简单些:

select * from x1 intersect x2

分步后思路变清晰了,但临时表的使用仍显繁琐。在以批量结构化数据计算中,作为中间结果的临时集合是相当普遍的,如果都建立临时表来存储,不仅运算效率低,同时也不直观。

而且,SQL不允许某个字段取值是集合(即临时表),这样,有些计算即使容忍了繁琐也做不到。

如果我们把问题改为计算所有产品销售额都在前10名的销售员,试想一下应当如何计算,继续延用上述的思路很容易想到:

1.  将数据按产品分组,将每组排序,取出前10名;

2.  将所有的前10名取交集;

由于我们事先不知道会有多个产品,这样需要把分组结果也存储在一个临时表中,而这个表有个字段要存储对应的分组成员,这是SQL不支持的,办法就行不通了。

如果有窗口函数(SQL2003标准)的支持,可以转换思路,按产品分组后,计算每个销售员在所有分组的前10名中出现的次数,若与产品总数相同,则表示该销售员在所有产品销售额中均前在前10名内。

select sales

from ( select sales,

from ( select sales,

rank() over (partition by product order by amount desc ) ranking

from sales_amount)

where ranking <=10 )

group by sales

having count(*)=(select count(distinct product) from sales_amount)

这样的SQL,有多少人会写呢?

况且,窗口函数在许多数据库中还不支持。那么,就只能用存储过程写循环依次计算每个产品的前10名,与上一次结果做交集。这个过程比用高级语言编写程序并不简单多少,而且仍然要面向临时表的繁琐。

现在,我们知道了SQL的第二个重要缺点:集合化不彻底。虽然SQL有集合概念,但并未把集合作为一种基础数据类型提供,这使得大量集合运算在思维和书写时都需要转换翻译。

SQL还有多少"理所当然";还有那些"就是这样"的更多相关文章

  1. mybatis中#{}与${}的差别(如何防止sql注入)

    默认情况下,使用#{}语法,MyBatis会产生PreparedStatement语句中,并且安全的设置PreparedStatement参数,这个过程中MyBatis会进行必要的安全检查和转义. # ...

  2. 转!! PreparedStatement是如何防止SQL注入的

    SQL注入最简单也是最常见的例子就是用户登陆这一模块,如果用户对SQL有一定的了解,同时系统并没有做防止SQL注入处理,用户可以在输入的时候加上'两个冒号作为特殊字符,这样的话会让计算机认为他输入的是 ...

  3. MyBatis怎么防止SQL注入

    SQL注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中(例如,为了转储数据库内容给攻击者).[摘自] SQL injection - Wikipedia SQL ...

  4. java持久层框架mybatis如何防止sql注入

    看到一篇很好的文章:http://www.jfox.info/ava-persistence-framework-mybatis-how-to-prevent-sql-injection sql注入大 ...

  5. 误报的java.sql.SQLException: Parameter number 21 is not an OUT parameter

    今天为了模拟一个mysql内存不释放问题,要测试一个存储过程,同时具有出参和入参,启动时报了上述错误. <select id="funcl_trd_secu_execution_que ...

  6. SQL语句执行顺寻

    SQL语句执行的时候是有一定顺序的.理解这个顺序对SQL的使用和学习有很大的帮助. 1.from 先选择一个表,或者说源头,构成一个结果集. 2.where 然后用where对结果集进行筛选.筛选出需 ...

  7. SQL Server Profiler监控SQL Server性能

    全面掌握SQL Server Profiler 1.       原理与相关概念介绍 SQL Server Profiler,大家已经非常熟悉.常常在性能优化中使用,本文档详细介绍SQL Server ...

  8. MyBatis如何防止SQL注入

    转自:http://www.myexception.cn/sql/1938757.html SQL注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中(例如,为了转 ...

  9. mybatis防止sql注入

         SQL注入是一种代码注入技术,用于攻击数据驱动的应用,恶意的SQL语句被插入到执行的实体字段中(例如,为了转储数据库内容给攻击者).[摘自] SQL injection - Wikipedi ...

随机推荐

  1. 当axios需要传送application/x-www-form-urlencoded格式参数的问题

    我发送出去的数据发现后台接收不到,查找了一下原因,发现需要form-data的数据后台才可以获取到.于是改成了form-data格式,成功了,后台获取到数据了,有点小激动,但是随即发现发送的数据格式出 ...

  2. Pushlets的初始化陷阱

    Pushlets是在类名为Pushlet的servlet的init方法中进行初始化的.一般我们会在web.xml配置pushlet的时候,指定其servlet在Web应用启动时就进行初始化,即便这样, ...

  3. 转 SPOOLING技术——操作系统

    传送门 SPOOLING技术——操作系统 SPOOLING技术(Simultaneous Peripheral Operating On Line) 同时联机外围操作技术,它是关于慢速字符设备如何与计 ...

  4. 如何让div中的文字只显示一行,多余的文字隐藏并加上省略号(超链接形式)

    写页面的时候遇到了一个小小的问题,如何让div中一行超链接文字只显示一行,多余的文字隐藏并加上省略号,悬浮时隐藏的文字显示出来?解决问题时发现了css3的一个新标签  text-overflow  , ...

  5. loading上下左右居中

    .loading { width: 50px; height: 60px; text-align: center; font-size: 10px; position:fixed; left:50%; ...

  6. python笔记4:高级特性

    4 高级特性 4.1  切片 [:] *注:-- list和tuple,字符串都支持切片 4.2 迭代 Iteration for ... in 及 for ... in if 两个变量迭代时, 例1 ...

  7. Codeforces 919 C. Seat Arrangements

    C. Seat Arrangements   time limit per test 1 second memory limit per test 256 megabytes input standa ...

  8. RSA 公钥加密算法

    RSA公钥加密算法是1977年由Ron Rivest.Adi Shamirh和LenAdleman在(美国麻省理工学院)开发的. 这个算法的名字也是他们三个人名字首字母,RSA算法基于一个十分简单的数 ...

  9. ML| EM

    What's xxx The EM algorithm is used to find the maximum likelihood parameters of a statistical model ...

  10. Network | DHCP

    动态主机设置协议(Dynamic Host Configuration Protocol, DHCP)是一个局域网的网络协议,使用UDP协议工作,主要有两个用途: 给内部网络或网络服务供应商自动分配I ...