10个简单步骤,完全理解SQL

转载自:http://blog.jobbole.com/55086/

1 sql是一种声明式语言 
sql语言是为计算机声明了一个你想从原始数据中获得什么样的结果的一个范例,而不是告诉计算机如何能够得到结果。 
2sql的语法并不是按照语法顺序执行 
select [distinct] 
from 
where 
group by 
having 
union 
order by 
a, T he first thing that happens is loading data from the disk into memory ,in order to operate on such data. 
b,select 是在大部分语句执行了之后才执行的,严格的说是在from 和group by之后执行的。理解这一点是很重要的,这就是你不能再where 中使用在select 中设定别名的字段作为判断条件的原因 
select是最后执行的语句 
重写别名: 
select a.x +a.y as z from a where(a.x+ a.y) = 10

c 无论在语法上还是执行顺序上,union总是排在order by之前,很多人认为每个union段都能使用order by 排序,但是根据sql语言标准和各个数据库sql的执行差异来看,这并不是真的,尽管某些数据允许sql语句对子查询subqueries 或者派生表derived tables 进行排序,但是这并不说明这个排序在union操作过后仍保持排序后的顺序

3sql语言的核心是对表的引用 tablereferences 
from a,b 
上面这句table语句的输出是一张联合表,联合了表a和表b,如果a表有三个字段,b表有5个字段,输出是8个字段。一张联合表,来自于所有引用的表在某一个维度上的联合, 
这个联合表中的数据时a*b,即a和b的笛卡尔积,换句话说,也就是a表中的每一条数据都要跟b中的每一条数据配对,如果a表有3条数据,b表有5条数据,那摩联合表就会有15条数据。

from 输出的结果被where语句筛选后要经过group by语句处理,从而形成新的输出结果。 
如果我们从集合论关系代数的角度来看,一张数据库的表就是一组数据元的关系,而每个sql语句会改变一种或者数种关系,从而产生新的数据元的关系(即产生新的表)

4灵活引用表能使sql语句变的更强大 
灵活引用表能使sql语句标的更加强大,一个简单的例子就是join的使用,join语句并非是select中的一部分,而是一种特殊的表引用语句,sql语言标准中表的连接定义如下: 
::= 


| joined table>
from a,b 
a1 join a2 on a1.id = a2.id 
将它放到之前的例子中就变成了: 
from a1 join a2 on a1.id =a2.id,b 
尽管讲一个连接表用逗号跟另一张表联合在一起并不是常用做法,但是你的确可以这麽做。结果就是,最终输出的表就有了a1 +a2 +b个字段了

思考问题时,要从表引用的角度出发,这样就很容易理解数据时怎么样被sql语句处理的,并且能帮助你理解哪些复杂的表引用是做什么的 
更重要的是,要理解join是构建连接表的关键词,并不是select语句的一部分,有些数据库允许在insert,update,delete中使用join

5sql语句中推荐使用表连接

from a,b 
高级sql程序员也许会给你忠告,尽量不要使用都好来代替join进行表的连接,这样会提高你的sql语句的可读性,并且可以避免一些错误 
记着要尽量的使用join进行表的连接,永远不要在from后面使用逗号连接表

6sql语句中不同的连接操作 
sql语句中,表连接的方式从根本上分为5种: 
equi join 
semi join 
anti join 
cross join 
division 
EQUI JOIN 
这是一种最普通的join操作,它包含两种连接方式: 
inner join (join) 
outer join(left,right,full outer join) 
this table reference contains authers and their books 
there is one record for each book and its author 
authors without books are NOT included 
authors JOIN book ON author.id =book.author.id

this table reference contains authors and their books 
there is one record for each book and its author 
or there is an “empty” record for authors without book 
(“empty” meaning that all book columns are null) 
author LEFT OUTER JOIN book ON author.id =book.author.id

SEMI JOIN 
这种连接关系在sql中有两种表现方式:使用IN,或者使用EXISTS. 
“SEMI”在拉丁文中是”半“的意思,这种连接方式是只连接目标表的一部分。我们不需要作者和书名的这样的组合,只是需要哪些在书名表中的书的作者信息。那我们 
就能这么写: 
using IN 
from author 
where author.id IN (select book.author_id from book) 
using EXISTS 
FROM AUTHOR 
WHERE EXISTS (SELECT 1 FROM BOOK WHERE BOOK.AUTHOR_ID = AUTHOR.ID

尽管没有严格的规定说明你何时应该使用IN,何时应该使用EXISTS,但是这些事情你还是应该知道的 
IN比EXISTS 的可读性更好 
EXISTS 比IN 的表达性更好(更适合复杂的语句) 
二者之间性能没有差异(但对于某些数据库来说性能差异会非常大)

因为使用INNER JOIN也能得到书名中书对应的作者信息,所以很多初学者认为可以通过DISTINCT进行去重,然后将SEMI JOIN IN 语句写成这样 
—-find only those authors who also have books 
select distinct frist_name,last_name 
from author 
JOIN book ON author.id =book.author_id 
这是一种很糟糕的写法,原因如下: 
sql语句性能底下:因为去重操作(distinct)需要数据库重复从硬盘中读取数据到内存中。(distinct的确是一种很耗费资源的操作,但是每种数据库对于distinct的操作方式可能不同) 
这么写并非完全正确:尽管也许现在这么写不会出现问题,但是随着sql语句变得越来越复杂,你想要去重得到正确的结果就变得时分困难。 
http://blog.jooq.org/2013/07/30/10-common-mistakes-java-developers-make-when-writing-sql/(distinct的危害)

ANTI JOIN 
这种连接的关系跟SEMI JOIN刚好相反, 在IN 或者EXISTS 前加一个NOT 关键字就能使用这种连接。举个例子来说,我们列出书名表里没有书的作者:

using IN 
from author where author.id NOT IN (select book.author_id from book)

using EXISTS 
from author where NOT EXISTS (select 1 from book where book.author_id=author.id 
关于性能。可读性,表达性等特性也完全可以参考SEMI JOIN 
http://blog.jooq.org/2012/01/27/sql-incompatibilities-not-in-and-null-values/)。(not in遇到null时应该怎么办)

cross join 
这个连接过程就是两个连接的表的乘积:即将第一张表的每一天数据分别对应第二张表的每条数据。我们之前见过,这就是逗号在from语句中的用法 
在实际应用中,很少有地方能用到cross join,但是一旦用了,你就可以用这样的sql语句表达 
combine every author with every book 
author cross join book

division 
division的确是一个怪胎,简而言之,如果join是一个乘法运算,那摩division就是join的逆过程。division的关系很难用sql表达出来,结余一个新手指南,解释division已经超过了我们的目的。(http://blog.jooq.org/2012/03/30/advanced-sql-relational-division-in-jooq/) 
(http://en.wikipedia.org/wiki/Relational_algebra#Division) 
(https://www.simple-talk.com/sql/t-sql-programming/divided-we-stand-the-sql-of-relational-division/)。(相关文档,可自行查阅) 
推荐阅读 →_→ 《画图解释SQL联合语句》http://blog.jobbole.com/40443/

我们学到了什么? 
sql是对表的引用,join则是一种引用表的复杂方式。但是sql语言的表达方式和实际我们所需要的逻辑关系之间是有区别的,并非所有的逻辑关系都能找到对应的join操作 
所以这就要我们在平时多积累和学习关键逻辑,这样就能在以后编写sql语句中选择适当的join操作了

7 sql中如同变量的派生表 
sql是一种声明性的语言,并且sql语句中不能包含变量,但是你能写出类似于变量的语句,这些就是叫做派生表: 
说白了,所谓的派生表就是在括号中的子查询:

—A DERIVED TABLE 
from (select * from author)

需要注意的是有些时候我们可以给派生表定义一个相关名,及我们所说的别名。 
—-A derived table with an alias 
from (select * from author ) a 
派生表可以有效的避免由于sql逻辑而产生的问题。如果想重用一个select和where语句查询出的结果,就可以写成这样:以oracle为例:

–get authors` frist and last name ,and their age in days 
select frist_name,last_name,age 
from ( 
select first_name,last_name,current_date - date_of_birth 
from author 

—-if the age is greater than 10000 days 
where age >10000 
需要我们注意的是:在有些数据库,以及sql:1990标准中,派生表被归为下一级–通用表语句(common table experssion)。 
这就允许你在一个select 语句中对派生表多次重用,上面的例子就是等价于下面的语句: 
with a as( 
    select first_name,last_name,current_date -date_of_birth  
from author 
) 
select *  
from a 
where age >10000

当然你可以给a创建一个单独的视图,这样你就可以在更广泛的范围内重用这个派生表了。 
(http://en.wikipedia.org/wiki/View_%28SQL%29)。 
sql语句就是对表的引用,而非对字段的引用,要好好利用这一点,不要害怕使用派生表或者其他更复杂的语句

8 sql语句中group by 是对表的引用进行操作 
from a,b 
group by A.x,A.Y,B.z 
上面语句的结果就是产生出了一个包含三个字段的新表的引用,当你应用group by的时候,。select后没有使用聚合函数的列,都要出现在group by 后面。即你能对其进行下一级逻辑操作的列会减少,包括在select中的列。

需要注意的是:其他字段能使用聚合函数: 
select a.x,a.y.sum(a.z) from a group by a.x,a.y 
还有一点值得留意的是,mysql并不坚持这个标准,这的确是令人困惑的地方。

group by ,再次强调一次,是在表的引用上进行了操作,将其转换为一种新的引用方式。 
9sql语句中的select实质上是对关系的映射 
一旦建立起来了表的作用,进过修改,变形,你能够一步一步的将其映射到另一个模型中。select 语句就像一个“投影仪”,我们可以将其理解成一个将源表中的数据按照一定的逻辑转换成目标表数据的函数 
通过select语句,你能对每一个字段进行操作,通过复杂的表达式生成所需要的数据 
select语句有很多特殊的规则,至少你应该熟悉以下几条: 
1 你仅能够使用那些能通过表引用而得来的字段 
2如果你有group by 语句,你只能够使用group by 语句后面的字段或者聚合函数; 
3当你的语句中没有group by 的时候,可以使用开窗函数代替聚合函数, 
4当你的语句没有group by 的时候,你不能同时使用聚合函数和其他函数 
5有一些方法可以将普通函数封装在聚合函数中

为什么不能再一个没有group by的select语句中同时使用普通函数和聚合函数? 
1,凭直觉,这种做法从逻辑上就讲不通 
2如果直觉不能够说服你,那么语法肯定能。sql;1990标准引入了grouping sets,sql;2003标准引入了group sets:group by() 
无论什么时候,只要你的语句中出现了聚合函数,而且没有明确的group by语句,这时一个不明确的,空的grouping set 就会被应用到这段sql中,因此,原始的逻辑顺序的规则就被打破了,映射关系首先会影响到逻辑关系,其次语句关系

这段话原文就比较艰涩,可以简单理解如下:在既有聚合函数又有普通函数的 SQL 
语句中,如果没有 GROUP BY 进行分组,SQL 语句默认视整张表为一个分组,当聚合函数对某一字段进行聚合统计的时候,引用的表中的每一条 
record 就失去了意义,全部的数据都聚合为一个统计值,你此时对每一条 record 使用其它函数是没有意义的 
select 语句可能是sql语句中最难得部分了,尽管他看上去很简单,其他语句的作用其实就是对表的不同形式的引用,而select语句则是把这些引用整合到了一起,通过逻辑规则将源表映射到目标表,而且这个过程是可逆的,我们可以清楚的知道目标表的数据是怎么来的 
想要学习好sql语言,就要在使用射了select语句之前弄懂其他的语句,虽然select是语法结构中的第一个关键词,但是他应该是最后一个掌握的

10,sql语句中的几个简单的关键词:distinct,union ,order by 和offset 
集合运算:distinct,union 
排序运算 order by ,offset。。。fetch 
集合运算:set operation 
集合运算主要是在于集合上,事实上指的就是对表的一种操作。从概念上来说,他们很好理解: 
distinct 在映射之后对数据进行去重 
union将两个子查询拼接起来并去重 
union all将两个子查询拼接起来但不去重 
except 将第二个字查询中的结果从第一个子查询中去掉 
intersect 保留两个子查询中都有的结果并去重

排序运算 ordering operating 
排序运算跟逻辑关系无关,这是一个sql特有的功能,排序运算不仅在sql语句的最后,而且在sql语句运行的过程中也是最后执行的。使用order by he offset…fetch 是保证数据能够按照顺序排列的儿最有效的方式。其他所有的排序方式都有一定随机性,尽管他们得到的排序结果是可重提的。

offset。。。set是一个没有同意确定语法的语句,不同的数据库有不同的表达方式,如mysql和postgresql的limit。。。sffset,sql server和sybase的top。。。start at等。 
(http://www.jooq.org/doc/3.1/manual/sql-building/sql-statements/select-statement/limit-clause/)。

10 Common Mistakes Java Developers Make when Writing SQL

10 More Common Mistakes Java Developers Make when Writing SQL
--------------------- 
作者:让认真成为一种性格 
来源:CSDN 
原文:https://blog.csdn.net/liuyuzhu111/article/details/50514586 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值