1 数据库语言

DBMS提供操作命令和语言,使用户能够对数据库进行各式各样的操作,例如查询、增、删、改数据,定义、修改数据模式等

——— >这就构成了用户和数据库的接口。

DBMS所提供的语言一般局限于对数据库的操作,有别于计算完备的程序设计语言,称为数据库语言(database language)。

1.1 数据库语言分类

数据库语言一般分为这几种

1.1.1 形式化查询语言(Query Language )

本章的重点SQL就是这一类语言,它有严格的语法和文法

1.1.2 表格查询语言(Tabular Query Language )

用填表的方式描述查询需求

我们给一个关系的名字(student),DBMS自动把属性填到表里面去。然后用户往表里面填需求。

在上图中,比如用户填的P,表示打印(显示)操作;填的IS,表示找寻所有学院是IS学院的学生

1.1.3  图像查询语言(Graphic Query Language)

每一个方框代表一种数据结构

找出数据库中教师的名字和他们的年龄(青色表中段)。其中教师的职位是教授(左绿色表)、年龄大于45岁(青色表下段)、学院是计算机学院(右绿色表)

1.2 对于关系数据模型的查询语言

关系数据模型的查询语言是基于关系代数或关系演算的理论基础,进行开发的。

SQL基于关系演算,是一种非过程化的查询语言(回忆一下关系演算:用布尔公式表述结果应该满足的条件)【数据库笔记——数据模型_刘文巾的博客-CSDN博客】

***需要注意一点,查询语言不是编程语言。因为它不是图灵完备的,没有逻辑编程能力。

QL(query language)只支持数据的查询,不能编程(如果要编程,就需要和别的语言结合、嵌入。目前常用方法是将数据库语言嵌入到一种高级程序设计语言中【如C,后文会讲】。这种高级程序设计语言【C语言】称为数据库语言的宿主语言。)

1.3 SQL的四个子语言

数据定义语言 Data Definition Language (DDL)

用来定义、删除、修改数据模式(表、视图。。。)

查询语言 Query Language

重中之重,就是select语句及其各种子句

数据操纵语言Data Manipulation Language (DML)

插入、删除、更新数据库中的数据

数据控制语言Data Control Language (DCL)

控制、授权用户对数据的访问权限

2 SQL

2.1 SQL中的基本概念

基表:一个基表就是真实存在磁盘上的一个关系数据结构

视图:又叫作虚表,根据基表通过映射和计算得到的虚表。不是真实存储在磁盘上的数据,逻辑意义上的表

主键primary key  外键 foreign key: 同数据模型

2.2 SQL中的保留字

NULL——空值,由于引入了NULL,所以SQL是三值逻辑而不是二值逻辑(真、假、不知道)

UNIQUE——创建表的时候,说明一个属性值是否允许重复值

DEFAULT——为数据库某一张表的某一个属性值指定缺省值

CHECK——定义一张表的时候,我们会定义一些完整性约束。之后向数据库插入数据的时候,系统会check这个要插入的数据是否符合约束条件

2.3 后文大部分SQL案例使用的数据表格

依旧是水手信息S1 S2、船只信息B1 、船只预定信息R1

2.4 SQL基本架构

target——目标列表

distinct——可缺省,加了之后,要求系统对查询结果消除重复元素,如果不加这个关键字的话,系统不会主动消除重复元素【这个类似于上一章的投影,做投影的时候不会主动删除重复元素,用户要求(比如加distinct关键字)后,才会删除】

from——查询所涉及的表

qualification——查询结果需要满足的布尔表达式

联想到关系演算(数据库笔记——数据模型_刘文巾的博客-CSDN博客):

target-list就是关系演算定义中 '|'之前的部分.

relation-list就是这个元组应该要属于的某个关系

qualifications就是关系演算定义中 '|'后面的部分

2.5 系统执行一条SQL语句的概念性方法

这是一种naive的实现方法,实际的数据库会根据所存储和所操作的数据,以及不同的应用而改变一些流程

1 ) 计算所有在relation-list中,也就是本次查询涉及到的表的笛卡尔乘积

2 ) 删去所有不满足qualifications的元组

3 ) 删去所有不在target-list中的属性

4 )  如果有distinct关键字,那么就删除所有重复的元组

3 简单的SQL例子

3.1 两张表联立

比如我们要找定了103号船的水手的名字

这时候需要水手信息的表格S和预定船信息的表格R联立。

让两个表联系起来的条件是S.sid=R.sid

这里的S和R是数据库的别名。

SELECT S.sname
FROM Sailors S, Reserves R
WHERE S.sid=R.sid AND R.bid=103

按照前面naive的思路。我们进行这个SQL查询的时候,会对R和S先进性笛卡尔乘积。得到一张大表,然后找满足条件的元组。截取需要的属性。

3.1.1 别名

在不引起混淆的情况下属性对应的表格的名字可以不出现

!!!但是出于规范的考虑,还是建议写上!!!

SELECT S.sname
FROM Sailors S, Reserves R
WHERE S.sid=R.sid AND bid=103
SELECT sname
FROM Sailors, Reserves
WHERE Sailors.sid=Reserves.sid
AND bid=103

以上两个在语法和语义上也是可以的。但是为了规范起见,还是建议使用3.1中的SQL编程习惯。

3.2 distinct的使用

我们现在需要完成一个这样的查询:

我们现在要找一个至少预定了一艘船的水手。

SQL实现如下:

SELECT S.sid
FROM Sailors S, Reserves R
WHERE S.sid=R.sid

如果我们只是要水手的id的话,那么完全可以不用引入S这个表格,这里加上S表格,是和后面的SQL和查询要求相对应的。

此时,我们在SELECT后面加或者不加DISTINCT关键字,在语义上不会有什么异同(因为预定了水手的数量不会因为加了distinct语句而发生什么改变)。

如果我们是要水手的名字呢?直接S.sid改成S.sname就可以了。

此时我们加或者不加DISTINCT关键字,有什么区别吗?有的,如果水手出现重名的话,加了DISTINCT关键字之后可能水手的数量就变少了(几个重名的水手算成了一个人)

3.3 新属性的命名

SELECT S.age, age1=S.age-5, 2*S.age AS age2
FROM Sailors S
WHERE S.sname LIKE ‘B_%B’

第一行的后面两个表达式(“as”和“=”)表示我们查询的结果所需要填入的属性名(给计算得到的新属性赋予名字的两种方法)

SQL用LIKE关键词进行模糊匹配,_代表匹配一个字符,%代表匹配0个或者更多字符(这里的意思是首位和末尾都是大写B,且整个字符串至少长度为三个字符的水手名字)

3.4 连接词的使用

如果我们要找一个水手,预定了一艘红船或者一艘绿船:

SELECT S.sid
FROM Sailors S, Boats B, Reserves R
WHERE S.sid=R.sid AND R.bid=B.bid
AND (B.color=‘red’ OR B.color=‘green’)

如果我们的查询只是需要水手的id的话,在这里我们只需要Boats和Reserves两张表就可以了,这里我们加Sailors这张表,是为了和后面的对照SQL相对应。

当然,上面这个查询我们也可以用Union集合并的方式写这个查询

SELECT S.sid
FROM Sailors S, Boats B, Reserves R
WHERE S.sid=R.sid AND R.bid=B.bid
AND B.color=‘red’
UNION
SELECT S.sid
FROM Sailors S, Boats B, Reserves R
WHERE S.sid=R.sid AND R.bid=B.bid
AND B.color=‘green’

但如果我们要找的是,一个定了一条红船以及一条绿船的水手呢?

此时我们不能简单地把上面的"OR"直接换成"AND"。因为一条船只可能为一个颜色,不可能即使红色又是绿色。

我们可以用自联结的方式表达这个查询,Boats和Reserves出现两次,类似于一个自联结(Boats和对应的Reserves是靠bid联系起来的,Reserves之间靠sid相关联)

SELECT S.sid
FROM Sailors S, Boats B1, Reserves R1,
Boats B2, Reserves R2
WHERE S.sid=R1.sid AND R1.bid=B1.bid AND S.sid=R2.sid AND R2.bid=B2.bidAND B1.color=‘red’ AND B2.color=‘green’

几张表之间的关联如下

B1——(bid)——R1——(sid)——R2——(bid)——B2

|-----(sid)----->S

当然,我们也可以用INTERSECT集合交来表示这个查询(这个就是简单地把前面的UNION换成INTERSECT就可以了)

SELECT S.sid
FROM Sailors S, Boats B, Reserves R
WHERE S.sid=R.sid AND R.bid=B.bid
AND B.color=‘red’
INTERSECT
SELECT S.sid
FROM Sailors S, Boats B, Reserves R
WHERE S.sid=R.sid AND R.bid=B.bid
AND B.color=‘green’

3.5 嵌套查询

3.5.1 互相无关联的嵌套查询

在3.1 节中,我们用两个表之间的相关联来实现了“找预定了103号船的水手的名字”这一个查询。

当时我们使用的SQL是这样的:两张表格用sid相连接

SELECT S.sname
FROM Sailors S, Reserves R
WHERE S.sid=R.sid AND R.bid=103

我们现在也可以用嵌套来完成这个查询

SELECT S.sname
FROM Sailors S
WHERE S.sid IN (SELECT R.sidFROM Reserves RWHERE R.bid=103)

嵌套子查询中的SELECT语句在外层查询处理之前求解。

我们先在Reserves表内找到预定了103号船的所有水手的id(内层查询)。

然后在S表查询这些id对应的水手的名字。

当查询涉及多个关系时,用嵌套查询逐次求解层次分明,具有结构程序设计特点。

同时,嵌套查询的执行效率也比联接查询的效率高。(因为连接查询需要笛卡尔乘积这一步操作)

如果我们要找的是没有预定103号船的水手,那么我们用NOT IN代替IN即可:

SELECT S.sname
FROM Sailors S
WHERE S.sid NOT IN (SELECT R.sidFROM Reserves RWHERE R.bid=103)

3.5.2 互相关联的嵌套

下面这个例子是互相关联的嵌套

SELECT S.sname
FROM Sailors S
WHERE EXISTS (SELECT *FROM Reserves RWHERE R.bid=103 AND S.sid=R.sid)

此时,外循环S的每一个sid,都需要带入内循环去进行一次查询,如果满足内循环的条件,那么这个sid就是一个我们要查询的内容。

这条嵌套查询语句和上面无关联嵌套查询语句在语义上是等价的。但是上面那个无关联嵌套查询语句,内层循环只需要执行一次。而这里外层查询的S有几个sid,内层循环就需要进行几次。效率是很低的。

所以同样的SQL逻辑,不同的设计方法,效率会大大不同。

3.5.3 嵌套的一个比较绕的例子

怎么表达一个只预定了一次103号船的水手呢?(预定一次的意思是只预定了一天的船)

第一种思路是使用NOT IN 关键字

SELECT S.name
FROM   Sailors S Reserve R
WHERE  R.sid=S.sid AND R.bid=103 AND S.sid NOT IN ( SELECT R1.sidFROM Reserve R1 WHERE R1.bid=103 AND R1.day <>R.day)

外层循环时先找哪些定了103号船的水手

内层循环是什么意思呢?

字面理解是这样的:从另一张Reserve表中找水手的id,这一条元组中的这个水手也定了103号船。同时预定这条船的日期和外层循环预定了这条船的日期不一样。

翻译一下,内层循环就是找至少定了两次103号船的水手的id集合。

那么外层循环where里面说的是sid NOT IN 内存循环,不在内存循环查询到的集合中。

也就是说。满足条件的水手不在“至少定了两次103号船的水手”集合中,同时这个水手又预定了103号船。

两个取一个交际,就是只预定了一次103号船的水手的id

第二种思路是使用EXISTS 和NOT EXISTS关键字

SELECT  S.name
FROM    Sailors S
WHERE   EXISTS  (SELECT *FROM   Resreves R1WHERE  R1.bid=103 AND S.sid=R2.sid   )ANDNOT EXISTS (SELECT *FROM   Resreves R2WHERE  R2.bid=103 AND S.sid=R2.sid  AND R1.day <> R2.day )

思路和第一种思路是一样的 “预定了103号船”+不在”至少预定了两次103号船“。这两个条件相叠加。得到我们要的查询结果。

3.5.4 关系嵌套+自联结

比如我们要找只被一个水手预定过的船:

SELECT bid
FROM Reserves R1
WHERE bid NOT IN (SELECT bidFROM Reserves R2WHERE R2.sid <> R1.sid)

对R1里面的每条记录,我们把它的sid带进去

子查询的意思是 R2中所有 不被这个R1.sid而被其他sid预定的船的编号

不在子查询中的船,就是只被这个R1.sid预定的船

3.5.5 用嵌套的方法解决 3.4 “同时定红船以及绿船”的问题

我们再回来看找一个同时选了红船和绿船的水手这个查询

之前我们是用自联结、INTERSECT来表示的

我们现在用嵌套的方式来表达:

SELECT S.sid
FROM Sailors S, Boats B, Reserves R
WHERE S.sid=R.sid AND R.bid=B.bid AND B.color=‘red’AND S.sid IN (SELECT S2.sidFROM Sailors S2, Boats B2, Reserves R2WHERE S2.sid=R2.sid AND R2.bid=B2.bidAND B2.color=‘green’)

外循环的意思是找到所有预约了红船的水手的id,同时这个id是满足内循环条件的元组里面的id。

内循环的意思是找到所有预约了绿船的水手的id

3.5.4 第3.4节 “同时定红船以及绿船”的问题 进一步的思考

在3.4和3.5.5中,我们查找的是满足条件的水手的id。如果我们需要查的是水手的姓名呢?

首先,自联结的方法,直接把sid换成sname就可以了,别的都不动

SELECT S.sname
FROM Sailors S, Boats B1, Reserves R1,
Boats B2, Reserves R2
WHERE S.sid=R1.sid AND R1.bid=B1.bid AND S.sid=R2.sid AND R2.bid=B2.bidAND B1.color=‘red’ AND B2.color=‘green’

然后嵌套的方法,也是直接把sid换成sname就可以了

SELECT S.sname
FROM Sailors S, Boats B, Reserves R
WHERE S.sid=R.sid AND R.bid=B.bid AND B.color=‘red’AND S.sid IN (SELECT S2.sidFROM Sailors S2, Boats B2, Reserves R2WHERE S2.sid=R2.sid AND R2.bid=B2.bidAND B2.color=‘green’)

但是对于INTERSECT的实现方法,可以直接将sid换成sname嘛?

这种方法是有一定的风险的。和3.2节加不加DISTINCT关键字一样。如果水手用重名的话,就有可能出现问题(比如一个叫Bob的只订了红船,另一个叫Bob的只订了绿船。但是两个查询分别返回了Bob,INTERSECT之后Bob是算在最终的结果里面的)

如果一定要用INTERSECT呢?我想到的办法就是再加一层INTERSECT:

SELECT SX.sname
FROM Sailors SX
WHERE SX.sid IN(SELECT S.sidFROM Sailors S, Boats B, Reserves RWHERE S.sid=R.sid AND R.bid=B.bidAND B.color=‘red’INTERSECTSELECT S.sidFROM Sailors S, Boats B, Reserves RWHERE S.sid=R.sid AND R.bid=B.bidAND B.color=‘green’)

内层循环就是原来3.4节中的INTERSECT部分

3.5.5 FROM语句的嵌套

这里会用到一丢丢第四节的东西。

我们现在需要找找平均年龄最小的rating。

首先 ,聚集函数不能嵌套,所以以下写法是错误的:

SELECT S.rating
FROM Sailors S
WHERE S.age = (SELECT MIN (AVG (S2.age))FROM Sailors S2)

那么我们应该怎么办呢?这时候我们可以对FROM语句使用嵌套:

这里用到了第四节GROUPBY的内容

SELECT Temp.rating
FROM (SELECT S.rating, AVG (S.age) AS avgageFROM Sailors SGROUP BY S.rating) AS Temp
WHERE Temp.avgage = (SELECT MIN (Temp.avgage)FROM Temp)

Temp是一个新的表,表的内容是每一个rating和rating对应的平均年龄的一一对应

WHERE中就是找等于最小平均年龄的那一组的rating

3.6 SQL 连接词

除了 NOT IN, NOT EXISTS, NOT UNIQUE之外,还可以有 op ANY(比某些op) op ALL (比所有op) (op是<,>,=,≤,≥,≠中的一个)

比如我们要找比某些叫Horatio等级高的水手:

SELECT *
FROM Sailors S
WHERE S.rating > ANY (SELECT S2.ratingFROM Sailors S2WHERE S2.sname=‘Horatio’)

子查询的意思是所有叫Horatio的水手的等级。

如果要找的是比所有叫Horatio等级高的水手,那么就把ANY替换成ALL就可以了,即:

SELECT *
FROM Sailors S
WHERE S.rating > ALL (SELECT S2.ratingFROM Sailors S2WHERE S2.sname=‘Horatio’)

3.7 SQL除法

3.7.1 关系代数除法回忆

回顾一下关系代数中的除法(数据库笔记——数据模型_刘文巾的博客-CSDN博客)

和关系代数里面用基础运算符表达除法一样,我们也可以用”否定之否定“来表达除法

回顾一下关系代数中对于除法的表述(数据库笔记——数据模型_刘文巾的博客-CSDN博客)

3.7.2 SQL除法举例

找到预定了所有船的水手

SELECT S.sname
FROM Sailors S
WHERE NOT EXISTS ((SELECT B.bidFROM Boats B)EXCEPT(SELECT R.bidFROM Reserves RWHERE R.sid=S.sid))

子查询里面 第一个子句:找到Boat里面所有的船的编号

子查询里面第二个字句  找到所有当前外查询遍历到的sid 预定的船的编号

两个子句一减,就是 所有当前外查询遍历到的sid 没有预定的船的编号

然后外查询的条件是NOT EXIST,即没有 没有预定的船,也就是所有的船都被预定了。

如果我们不用EXCEPT呢?

SELECT S.sname
FROM Sailors S
WHERE NOT EXISTS (SELECT B.bidFROM Boats B WHERE NOT EXISTS (SELECT R.bidFROM Reserves RWHERE R.bid=B.bidAND R.sid=S.sid))

这个比较复杂,我们一层一层看:

最外层:我们从Sailors数据库中寻找水手的名字,这些水手不满足一些条件

中间层:水手不满足什么条件呢? 从Boats数据库中找船的id,这些船不满足一些条件

最里层:这些船不满足什么条件呢?条件是: 在预约数据集Reserves里面,这个水手定了这艘船

然后我们从里向外分析:

船不满足”水手定了这艘船“——水手没有定这艘船

水手不满足”水手没有定这艘船“——水手定了所有船

3.8 SQL 排序

ORDER BY 关键字用于对结果集按照一个列或者多个列进行排序。

ORDER BY 关键字默认按照升序对记录进行排序。如果需要按照降序对记录进行排序,可以使用 DESC 关键字。

以上面这个表为例

SELECT *
FROM Websites
ORDER BY alexa

对结果按照alexa排序,结果为:

3.8.2 倒排序

倒排序就是在上面的基础上,加一个DESC即可

SELECT *
FROM Websites
ORDER BY alexa DESC

结果为:

3.8.3 多列排序

SELECT *
FROM Websites
ORDER BY country,alexa

上述SQL结果为:

DESC或者ASC只对它紧跟着的第一个列名有效,其他的不受影响

3.9 其他SQL命令

3.9.1  LIMIT

选取头几条记录

SELECT *
FROM Students
LIMIT 2

上面一条语句是从Students这个表中选取头两条记录

LIMIT y 分句表示—— 读取 y 条数据
LIMIT x, y 分句表示——跳过 x 条数据,读取 y 条数据
LIMIT y offset x 分句表示—— 跳过 x 条数据,读取 y 条数据
LIMIT n 等价于 limit 0,n

3.9.2 TOP PERCENT

选取头百分之多少的记录

SELECT TOP 50 PERCENT *
FROM Students

上面一条语句是从Students这个表中选取前百分之五十的记录

3.9.3 MINUS

使用方式和前面的UNION,INTERSECTION一样,只不过这边是集合差的意思

3.9.4 IFNULL

IFNULL() 函数用于判断第一个表达式是否为 NULL,如果为 NULL 则返回第二个参数的值,如果不为 NULL 则返回第一个参数的值。

IFNULL(expression, alt_value)
参数 描述
expression 必须,要测试是否为NULL的值
alt_value 必须,expression 表达式为 NULL 时返回的值

3.10 SQL连接

本小节需要用到的表:

    

3.10.1 (INNER) JOIN

内连接,或等值连接

获取两个表中字段匹配关系的记录

SELECT a.role_id, a.occupation, a.camp, b.mount_name
FROM roles a INNER JOIN mount_info b
ON a.role_id = b.role_id;

对于inner join 这两个语句是一样的

SELECT a.role_id, a.occupation, a.camp, b.mount_name
FROM roles a, mount_info b
WHERE a.role_id = b.role_id;

查询结果为:

3.10.2 LEFT JOIN

    

LEFT JOIN 会读取左侧数据表的全部数据,即使右侧表中无对应数据。

SELECT a.role_id, a.occupation, a.camp, b.mount_name
FROM roles a LEFT JOIN mount_info b
ON a.role_id = b.role_id;

查询结果为:

3.10.3 RIGHT JOIN

    

RIGHT JOIN 会读取右侧数据表的全部数据,即便左侧表无对应数据。

SELECT a.role_id, a.occupation, a.camp, b.mount_name
FROM roles a RIGHT JOIN mount_info b
ON a.role_id = b.role_id;

查询结果为:

3.10.4 连接语句 ON 和WHERE的区别

参考SQL中的ON和WHERE的区别_liitdar的博客-CSDN博客

以 LEFT JOIN 为例:在使用 LEFT JOIN 时,ON 和 WHERE 过滤条件的区别如下:

——ON 条件是在生成临时表时使用的条件,它不管 ON 中的条件是否为真,都会返回左边表中的记录;
——WHERE 条件是在临时表已经生成后,对临时表进行的过滤条件。因为此时已经没有 LEFT JOIN 的含义(必须返回左侧表的记录)了,所以如果 WHERE 条件不为真的记录就会被过滤掉。

    

以下面的查询为例

SELECT *
FROM roles LEFT JOIN mount_info
ON (roles.role_id = mount_info.role_id)
WHERE mount_info.mount_name="sheep";

查询结果为

分析一下上述SQL语句的执行过程:

1. 首先,根据ON过滤条件“roles.role_id = mount_info.role_id”(相当于去掉后面的WHERE过滤条件),生成中间表,如下:

2. 然后,针对上面生成的中间表,再根据WHERE过滤条件“mount_info.mount_name="sheep"”,产生最终查询结果,如下:

3.10.5 JOIN总结

                                                 

                                                            

3.11 IF

IF(sex = 'f', 'm', 'f')

这条语句表示:如果sex是'f'的话,那么返回'm',否则返回'f'

4 聚集计算

聚集计算有以上几个,分别对应了元组的个数、某一个属性(不同)值数量、某一个属性(不同)值的和、某一个属性(不同)值的平均数、某一个属性的最大值、某一个属性的最小值。

注:以count为例,count(*)会去计算有NULL的行。count+字段只考虑 非NULL的部分

4.1 聚集计算举例说明

4.1.1 水手个数

SELECT COUNT (*)
FROM Sailors S

4.1.2  叫bob的水手有几个不同的等级

SELECT COUNT (DISTINCT S.rating)
FROM Sailors S
WHERE S.sname=‘Bob’

4.1.3 级别为10的水手的平均年龄

SELECT AVG (S.age)
FROM Sailors S
WHERE S.rating=10

4.1.4 级别为10的水手的不同年龄的平均值

SELECT AVG (DISTINCT S.age)
FROM Sailors S
WHERE S.rating=10

4.1.5 级别最高的那些水手的名字

SELECT S.sname
FROM Sailors S
WHERE S.rating= (SELECT MAX(S2.rating)FROM Sailors S2)

内循环是求水手的最高等级

4.2 GROUPING

4.2.1 grouping语句的引出

我们如果需要对数据库每一个分组计算出一个值,那么我们就需要用到grouping.

GROUP BY子句按列值分组。列值相同的分为一组。当其后有多个列名时,则先按第一列名分组,再按第二列名在组中分组,直到GROUP子句指名的列都具有相同值的基本组。

比如,我们要找每个等级最年轻的水手

首先,我们不知道水手有几个等级、这几个等级的值都是多少

其次,就算我们知道了水手的等级,我们也不太好用我们已经介绍了的这些sql语句实现

比如我们知道了rating是从1到10递增的,我们可以这么写吗?

答案是不行的,因为我们在1.2节说过“QL(query language)只支持数据的查询,不能编程”。SQL语言无法实现for操作。

4.2.2 group by 和having语句格式

其中,规定target-list 中的属性必须是grouping-list中的子集。

sql要求 在selct字句和having出现的属性必须是分组属性集grouping-list中的子集

where语句筛选元组,having语句筛选组

4.2.3 GROUPING语句的逻辑流程

1)先对 from语句里面的relation-list做笛卡尔乘积

2) where 子句对笛卡尔乘积得到的大表做筛选

3) group by 分组,将group by语句属性值相等的元组放到同一分组

4) 按照having语句的条件对组做筛选

5)合格的组,按照select做相应的计算,每一个组得到一个结果

4.2.4 grouping 语句举例

“Find age of the youngest sailor with age ≥ 18, for each rating with at least 2 such sailors”

SELECT S.rating, MIN (S.age) AS minage
FROM Sailors S
WHERE S.age >= 18
GROUP BY S.rating
HAVING COUNT (*) > 1

逻辑步骤:

1) 先筛选年龄大于18的水手(where条件)

2)然后对rating排序,然后相同rating一组

3)最后就是having的条件 count(*)>1

Find age of the youngest sailor with age > 18, for each rating with at least 2 sailors (of any age)

刚才的要求是:

“Find age of the youngest sailor with age ≥ 18, for each rating with at least 2 such sailors”

和前一个的区别在于,前一个是18+的人数大于等于2作为组别的筛选条件。现在是组别所有人的人数大于等于2。

这里我们不能像之前那样直接 having 语句里面count(*) 。因为where已经把小宇等于18的人删掉了。

这时候我们需要having的嵌套语句:

SELECT S.rating, MIN (S.age)
FROM Sailors S
WHERE S.age > 18
GROUP BY S.rating
HAVING 1 < (SELECT COUNT (*)FROM Sailors S2WHERE S2.rating = S.rating)

4.2.4 “是grouping-list的子集”约束条件举例

关系型数据库不能从语义上检查判断selct和having 每一个属性在每一个分组中值是否单一,所以才规定了“在selct字句和having出现的属性必须是分组属性集grouping-list中的子集”这个约束条件。

对每艘红船,找他的预定次数

For each red boat, find the number of reservations for this boat

SELECT B.bid, COUNT (*) AS scount
FROM Boats B, Reserves R
WHERE R.bid=B.bid AND B.color=‘red’
GROUP BY B.bid

上面这个肯定是对的。但如果B.color='red'不在where语句,二是在Having里面,那么可不可以呢?就是以下这样的:

SELECT B.bid, COUNT (*) AS scount
FROM Boats B, Reserves R
WHERE R.bid=B.bid
GROUP BY B.bid
HAVING B.color=‘red’

首先,逻辑上是没有问题的,但是SQL会报错

为什么呢?就是前面说的”“在selct字句和having出现的属性必须是分组属性集grouping-list中的子集"..HAVING语句中的属性color不在分组属性集bid中,所以SQL会自动报错。哪怕语义逻辑上是正确的。

如果我们一定不能在WHERE中使用color='red'呢?

第一种方法是HAVING中使用嵌套,然后HAVING语句中的属性值也只是bid

SELECT B.bid, COUNT(*) AS scount
FROM Boats B, Reserves R
WHERE R.bid=B.bid
GROUP BY B.bid
HAVING B.bid in SELECT B2.bid FROM BOATS B2WHERE B2.color='red'

第二种方法是在GROUP BY中使用color='red'

SELECT B.bid, COUNT(*) AS scount
FROM Boats B, Reserves R
WHERE R.bid=B.bid
GROUP BY B.bid AND B.color='red'

5 CAST语句举例

类似于强制类型转换

5.1 CAST语句的几个作用

5.1.1 匹配函数的参数

比如截取字符串,设置首末位置的下标为整数

5.1.2 转换精度

Decimal (5,0) 表示长度为5位 小数点后0位

5.1.3 对空值赋以类型

假设我们有这样两个表:

我们想到一张students和soldier的外并,但我们只能用并操作来完成。

那我们就需要考虑并兼容的问题,并兼容的话就需要解决NULL空值的情况

CREATE VIEW prospects (name, school, service) ASSELECT name, school, CAST(NULL AS Varchar(20))FROM Students
UNIONSELECT name, CAST(NULL AS Varchar(20)), serviceFROM Soldiers ;

VIEW,视图,也就是虚表。我们根据查询结果临时算出来的。

prospects 括号里面的是视图的模式。

students和solders各补了一列,以满足并兼容。

CAST 语句的意思是,把NULL转换成最长20的字符串。

6 CASE语句

很多数据库为了节省空间,对属性进行了编码。但是用户使用的时候,看编码肯定不方便。于是我们需要把编码再还原成语义信息。这时候就可以用到CASE语句。

我们现在有这样一个数据库:

其中status是officer现在的状态。但是数据库中为了节省空间,并没有存储状态实际的字符串,而是用数字表示的。

但是用户看的时候,officer的状态是数字肯定是不方便的。于是我们需要再用户使用端再把数字转换回字符串:

SELECT name, CASE statusWHEN 1 THEN ‘Active Duty’WHEN 2 THEN ‘Reserve’WHEN 3 THEN ‘Special Assignment’WHEN 4 THEN ‘Retired’ELSE ‘Unknown’END AS status
FROM Officers ;

我们提取name 和status两个属性。

其中status属性我们进行转换,如果是1,那么在我们新的表中,status为‘active duty’;如果是2,那么在我们新的表中,status为‘Reserve’。。。以此类推。那么此时存在Officers里面的status属性就不是数字,是字符串了。

我们比较一下以下两条SQL语句:

SELECT type, CASEWHEN sum(hours_used)>0 THEN sum(accidents)/sum(hours_used)ELSE NULLEND AS accident_rate
FROM Machines
GROUP BY type;SELECT type, sum(accidents)/sum(hours_used)
FROM Machines
GROUP BY type
HAVING sum(hours_used)>0;

表达的语义都是每一组的平均出故障时间比例。

但是不同的是,如果我们的数据里面有没有使用过的机器(也就是他们sum(hours_used)为0的机器),在第一个SQL查询结果里面也会出现,值是NULL。

但在第二个SQL查询里面,就会被HAVING语句筛掉。结果只出现sum(hours_used)>0的部分)

7 子查询

之前的无关联嵌套和有关联嵌套都是子查询,我们这里系统的整理一下

子查询一共有三种:

7.1 标量子查询

凡是可以出现一个值的地方,都能是标量子查询

比如我们要查询平均奖金大于平均薪水的部门:

SELECT d.deptname, d.location
FROM dept AS d
WHERE (SELECT avg(bonus)FORM empWHERE deptno=d.deptno)> (SELECT avg(salary)FORM empWHERE deptno=d.deptno)

当然这里也可以grouping来表示

SELECT d.deptname, d.location
FROM dept AS d
GROUP BY d.deptno
HAVING (SELECT avg(bonus)FROM emp WHERE deptnp =d.deptno)>(SELECT avg(salary)FROM emp WHERE deptnp =d.deptno)

上面的写法是正确的,下面是错误的

SELECT d.deptname, d.location
FROM dept AS d
GROUP BY d.deptno
HAVING  avg(bonus) > avg(salary)

错误的原因和4.2.4中是一样的。就是前面说的”“在selct字句和having出现的属性必须是分组属性集grouping-list中的子集"..HAVING语句中的两个属性不在分组属性集bid中,所以SQL会自动报错。哪怕语义逻辑上是正确的。

7.2 表格表达式

表格表达式实际上得到的都是视图(虚表)

eg 每一年入职的员工平均拿到的钱

SELECT startyear, avg(pay)
FROM (SELECT name, salay+bonus AS pay, year(startdate) AS startyearFROM emp) AS emp2
GROUP BY startyear;

7.3 公共表表达式

在特别复杂的查询里面,可能某一种查询不止出现一次。如果我们每次用到这个表的时候,在SQL里面都写一次这个表的表达式,也不是不可以,但是得重复计算,效率偏低。

为了提高效率,我们定义一个公共表表达式,在select之前定义、计算一次,得到一张视图。然后在之后的查询中直接使用即可。

比如我们要去找拿钱最多的部门:

这时候我们要对表格进行两次查询:第一次查询拿钱最多的这个值,第二次查询哪个部门拿的钱数的等于这个值

WITH payroll (deptno, totalpay) AS(SELECT deptno, sum(salary)+sum(bonus)FROM empGROUP BY deptno)
SELECT deptno
FROM payroll
WHERE totalpay = (SELECT max(totalpay)FROM payroll);

公共表表达式用WITH进行定义

这个公共表表达式的名字叫payroll,定义是AS之后的部分

比如我们要找,平均薪资一组比另一组多一倍以上的部门对

WITH deptavg (deptno, avgsal) AS(SELECT deptno, avg(salary)FROM empGROUP BY deptno)
SELECT d1.deptno, d1.avgsal, d2.deptno, d2.avgsal
FROM deptavg AS d1, deptavg AS d2
WHERE d1.avgsal>2*d2.avgsal;

7.3.1 综合使用公共表达式和cast语句实现外连接

我们现在有这么两张表。我们希望能同时找到 这学期所有开的课和对应的老师、这学期不开的课,这学期没有课的老师这三种信息,也就是两个表的外连接

WITHinnerjoin(name, rank, subject, enrollment) AS(SELECT t.name, t.rank, c.subject, c.enrollmentFROM teachers AS t, courses AS cWHERE t.name=c.teacher AND c.quarter=‘Fall 96’) ,teacher-only(name, rank) AS(SELECT name, rankFROM teachersEXCEPT ALLSELECT name, rankFROM innerjoin) ,course-only(subject, enrollment) AS(SELECT subject, enrollmentFROM coursesEXCEPT ALLSELECT subject, enrollmentFROM innerjoin)SELECT name, rank, subject, enrollment
FROM innerjoinUNION ALLSELECT name, rank,CAST (NULL AS Varchar(20)) AS subject,CAST (NULL AS Integer) AS enrollment
FROM teacher-onlyUNION ALLSELECT CAST (NULL AS Varchar(20)) AS name,CAST (NULL AS Varchar(20)) AS rank,subject, enrollment
FROM course-only ;

interjoin 表示这学期有老师上课的情况

teacher-only 所有老师减去interjoin里面的老师,就是这学期没有开课的老师

这里使用except all会比except效率高(如果我们的查询结果没有重复,或者重复没有影响,我们可以使用except all;因为except 会先做一次排序,效率不如except all)

course-only 所有课程减去interjoin里面的课程,就是这学期不开的课

外连接回顾:(数据库笔记——数据模型_刘文巾的博客-CSDN博客)

8 递归查询

自己的查询里面使用了自己的定义

8.1 有结束条件的递归查询

比如我们有这样一个表格,manager表示自己的直接上司。

我们希望找到hoover所管辖的员工中薪资大于100000的人

我们不能直接 where manager='HOOVER’ 来判断这个人是不是hoover的下属。因为这样只能判断hoover的直接管辖的员工,但是直接管辖的员工还有他们管辖的员工,那些人也是算hoover的下属的。。。。

这时候我们就需要使用递归实现了

WITH agents (name, salary) AS((SELECT name, salary                                     --- initial queryFROM FedEmpWHERE manager=„Hoover‟)UNION ALL(SELECT f.name, f.salary                                  --- recursive queryFROM agents AS a, FedEmp AS fWHERE f.manager = a.name))SELECT name                                                   --- final query
FROM agents
WHERE salary>100000 ;

agents表格最终达到的效果是hoover所有下属以及对应的薪水。

initial query指的是hoover的直接下属。

recursive query的话,就是找现在agents中的员工各自的直接下属。然后把这些直接下属放到agents中,再找他们的直接下属。。。以此循环。直到最底层员工,然后跳出循环。

得到表格后,我们再query就很方便了。

8.2 没有结束条件的递归查询

我们现在看这样一个问题

我们有这么几个机场,几个机场之间有航班相连。我们现在要找从SFO到JFK 开销最小的航班坐法

数据可视化之后的结果

SQL中是没有办法表达中转的

我们需要建立一个临时表。这个临时表记录了从SFO中转一次、两次、三次。。。可能到达的目的地。(如下表,记录了我们做一次、两次、三次航班后的目的地、路径、开销)

WITH trips (destination, route, nsegs, totalcost) AS((SELECT destination, CAST(destination AS varchar(20)), 1, costFROM flights                                                          --- initial queryWHERE origin=‘SFO’)UNION ALL(SELECT f.destination,                                             --- recursive queryCAST(t.route||’,’||f.destination AS varchar(20)), t.nsegs+1, t.totalcost+f.costFROM trips t, flights fWHERE t.destination=f.originAND f.destination<>’SFO’                                     --- stopping rule 1AND f.origin<>’JFK’                                          --- stopping rule 2AND t.nsegs<=3))                                             --- stopping rule 3SELECT route, totalcost                                             --- final query
FROM trips
WHERE destination=‘JFK’ AND totalcost=                                                 --- lowest cost rule(SELECT min(totalcost)FROM tripsWHERE destination=‘JFK’) ;

||表示字符串连接

我们重点看一下递归查找中的结束条件。

首先'<>'表示不等于 也就终点不能是SFO;

然后起点不能是JFK,

以及最多可以坐三次航班(这可以看作“兜底”。哪怕前面思考的不周到,我们也最多递归三次)

最终的查询结果为:

9 数据操纵语言

主要是三个操作

9.1 insert

将一条元组插入数据库中

VALUE后面是要插入的元组值,其次序 和域应与STUDENT的模式定义一致。

INSERT INTO EMPLOYEES
VALUES ('Smith', 'John', '1980-06-10', 'Los Angles', 16, 45000);

把VALUES 后面括号里面的元组插入到INTO后面的数据库中

9.2 delete

把满足条件(条件为WHERE后面的语句)的元组删除

DELETE FROM Person
WHERE LastName = 'Rasmussen' ;

如果没有WHERE子句,则删除指定表中的所有元组,使该表为一空表。(删除整个表要用 DROP TABLE语句)

9.3 update

把满足条件(条件为WHERE后面的语句)的元组中的某一些属性更新(更新方式为SET后面的语句)

UPDATE Person
SET Address = 'Zhongshan 23', City = 'Nanjing'
WHERE LastName = 'Wilson';

9.4 创建新表

比如我们要生成一个学生成绩临时表GRADE,表中包括SNAME,CNO,GRADE三个属性。

首先定义一个临时表GRADE:

CREATE TABLE GRADE(SNAME VARCHAR(8) NOT NULL,CNO   CHAR(6) NOT NULL,GRADE DEC(4,1) DEFAULT NULL);

其次插入有关的数据:

INSERT INTO GRADE
SELECT SNAME, CNO, GRADE
FROM  STUDENT,SC
WHERE STUDENT.SNO=SC.SNO

10 视图总结

10.1 普通视图

是一个虚表,用create view 创建的虚表。

它区别于基表,后者是以某种形式存在磁盘里面的。

普通视图是在基表的基础上利用查询得到的,数据库中只会记录他们的定义。

普通视图可以保证数据的逻辑独立性(按照功能创建普通视图)。

普通视图也能保证数据的安全性(比如用户可以看到的数据是总数居的一部分,那么给用户的数据可以是所有属性的一部分、所有元组的一部分、甚至是加工后的结果。此时可以保证数据的安全性

视图对应的内容总是实时、最新的内容,并不是视图定义时对应内容。这是由于基表随着更新操作其内容在不断变化,所以视图对应的内容也在不断变化。

10.1.1 普通视图的更新问题。

早期的SQL中是不允许视图来更新属性的,当时的视图是只读。

但现在的SQL中,只要视图的属性可以唯一对应基表中的属性,那么视图也是可以更新的。相当于更新的基表中的属性.

比如上面这样两个普通视图,第一个就可以更新视图里面的属性值,而第二个就不行。

10.1.2 普通视图的撤销

使用DROP VIEW 来撤销

DROP VIEW YoungSailor

10.2 临时视图和递归查询

临时视图的定义方式with和普通视图的create view很像,他们的实现也是类似的。

唯一不同的是,临时视图的定义也是临时的,数据库并不会存储他。当查询语句完成,临时试图就被遗弃。

11 嵌入式SQL

我们之前进行的,都是一条查询语句。而SQL不是编程语言,本身不具备程序设计能力。

那么如果我们要基于数据库进行应用程序开发,我们就需要SQL和某些编程语言相结合。

在这里,我们主要介绍在C语言中使用嵌入式SQL。

11.1 嵌入式SQL的特点

1)所有嵌入在c里面的SQL命令,都以“EXEC SQL"这个开始,以分号结束。编译可以用这个来识别这段语句是SQL语言还是C语言

2)使用宿主变量(host variables)在C和数据库之间传递数据和消息。宿主变量需要以”EXEC SQL“为开头进行定义。

3)在SQL语句中,如果是C语言中的变量,我们需要加上冒号以示区分SQL(数据库)中的变量属性

4)在宿主语言(也就是这里的C语言)中,宿主变量正常使用就ok了(和普通变量一样使用)

5)不能将宿主变量成定义数组或者结构

6)SQL中有一个通讯区SQLCA,可以利用这个数组变量实现C和SQL语言之间信息的交换

7)SQLCA.SQLCODE判断返回的状态(查询结果正常与否)

8)使用短整型 indicator(说明符)表示C里面没有空值(0还是1) 

11.2 宿主变量的定义

EXEC SQL BEGIN DECLARE SECTION;
char SNO[7];
char GIVENSNO[7];
char CNO[6];
char GIVENCNO[6];
float GRADE;
short GRADEI; /*indicator of GRADE*/
EXEC SQL END DECLARE SECTION;

这里面的GRADEI就是前面11.1第8条说的短整型说明符

11.3 连接数据库

首先要用connect和数据库建立连接

连接数据库需要uid用户标识符和pwd用户输入的密码

连接的时候,用这个用户名和密码来访问数据库

EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;

11.4 将C语言中的值插入到数据库

EXEC SQL INSERT INTO SC(SNO,CNO,GRADE)VALUES(:SNO, :CNO, :GRADE);

将C语言程序中的SNO,CNO,GRADE宿主变量(加冒号的部分)插入到SC数据库中

11.5 查询语句

EXEC SQL SELECT GRADEINTO :GRADE :GRADEIFROM SCWHERE SNO=:GIVENSNO AND CNO=:GIVENCNO;

查询的返回结果放到c变量里面去(INTO里面的部分)

11.6 游标

在11.5中,{SNO,CNO}是SC的主键。所以查询的结果只有一条语句。如果查询的结果是一组元组呢?

这时候我们需要使用游标cursor来处理一般的查询语句返回的元组集合 

定义一个游标,类似于C中执行的SQL语句一样来定义,for后面的就是查询语句。

我们把游标看成一个文件,那么对游标的操作就和对文件的是一样的了(会有一个读写指针),需要open,close。

读写指针一开始指向第一个元组 

fetch语句逐条元组地取每一个值,按照顺序赋给宿主变量。每一个宿主变量得到了一个值。

我们用一个循环再去fetch,直到集合中所有的元组的信息都被提取出来了。

那么循环什么时候结束呢?

SQLCA.SQLCODE的值是100的时候,表明查询结果的结果集处理完了。

下面是一个完整的嵌入式游标操作:(声明+打开+一条一条读取+关闭)

EXEC SQL DECLARE C1 CURSOR FORSELECT SNO, GRADEFROM SCWHERE CNO = :GIVENCNO;EXEC SQL OPEN C1;if (SQLCA.SQLCODE<0) exit(1); /* There is error in query*/while (1)
{
EXEC SQL FETCH C1 INTO :SNO, :GRADE :GRADEIif (SQLCA.SQLCODE==100) break;
/* treat data fetched from cursor, omitted*/
∶
}EXEC SQL CLOSE C1;

12 动态嵌入式sql

在之前11小节的嵌入式SQL中,SQL语句都是在编译之前就已经写好了。

但是在一些应用中。SQL语句并不能提前写好(根据用户的输入的信息构建sql;换句话说,程序执行之前需要执行什么SQL用户是不知道的)。他需要在程序运行过程中动态地建立。

12.1 非查询动态SQL

EXEC SQL BEGIN DECLARE SECTION;
char sqlstring[200];
EXEC SQL END DECLARE SECTION;char cond[150];
strcpy( sqlstring, ”DELETE FROM STUDENT WHERE ”);printf(“ Enter search condition :”);
scanf(“%s”, cond);strcat( sqlstring, cond);EXEC SQL EXECUTE IMMEDIATE :sqlstring;

上面的嵌入式SQL作用是将满足条件的学生删除,其中条件是使用者输入进去的

sqlstring是拼接之后的SQL语句

cond是用户需要输入的条件。条件是程序运行的时候,由用户来决定。

IMMEDIATE表示数据库系统动态地立即执行sqlstring里面的语句

12.2 有动态参数的嵌入式SQL

先用占位符(place holder)在事先写好的SQL语句中占一个位置,然后以后填进去

EXEC SQL BEGIN DECLARE SECTION;
char sqlstring[200];
int birth_year;
EXEC SQL END DECLARE SECTION;strcpy( sqlstring, ”DELETE FROM STUDENT WHERE YEAR(BDATE) <= :y; ”);printf(“ Enter birth year for delete :”);
scanf(“%d”, &birth_year);EXEC SQL PREPARE purge FROM :sqlstring;EXEC SQL EXECUTE purge USING :birth_year;

这里的:y就是占位符。

PREPARE语句先准备一下要执行的sql语句,此时还是占位符:y。

真正执行的时候EXECUTE,用宿主变量的值替换占位符位置的值。

13 嵌入式SQL存储过程

允许用户把公用的SQL语句段定义成一个过程,系统事先经过编译优化后存储在数据库系统里面。将来用户要用的时候直接调用它。(有点类似于程序语言中的函数)。

这样可以改善性能,方便开发

对于频繁同时使用这几个SQL语句段的用户来说,下次要用的时候,直接调用存储过程,不用重新写语句了。

如果需要修改需求,只要改定义SQL语句段成为过程的那一个地方就可以了,要不然SQL语句段在程序中使用过的地方都需要改。

不用对存储的语句再编译(SQL语句段都写在C程序里面的话,每次进行都要进行预编译优化,如果存成一个过程的话就只需要在存入DBMS的第一次过程中优化)。

EXEC SQLCREATE PROCEDURE drop_student(IN student_no CHAR(7),OUT message CHAR(30))BEGIN ATOMICDELETE FROM STUDENTWHERE SNO=student_no;DELETE FROM SCWHERE SNO=student_no;SET message=student_no || ’droped’;END;
EXEC SQL
∶
CALL drop_student(…); /* call this stored procedure later*/
∶

放在一起连续做的事情构建成一个存储过程,定义成一个模块

在上面例子中,drop_student就是这个存储过程。之后在C语言程序中直接调用这个,就代表了它定义里面的几条SQL语句了。

存储过程和函数类似,可以有输入输出  

in—— 学生的学号

out——返回值,告诉用户查询过程是成功还是失败

ATOMIC表示里面操作为原子操作——要么全部成功,要么一个不做

数据库笔记: SQL相关推荐

  1. 数据库笔记-sql执行顺序以及检索优先级

    sql 执行顺序优先级由高到低依次是: from 关键字后面的语句. where 关键字后面的语句." group by "后面的语句. select 后面的语句." o ...

  2. JavaWeb学习笔记(数据库、SQL语句、数据查询语法、完整性约束、编码、备份和恢复数据、多表查询)

    数据库.SQL语句.数据查询语法.完整性约束.编码.备份和恢复数据.多表查询 JavaWeb学习笔记 数据库 数据库概念 基本命令 启动和关闭mysql服务器 客户端登录退出mysql SQL语句 S ...

  3. 墨者学院刷题笔记——SQL手工注入漏洞测试(MongoDB数据库)

    今天继续给大家介绍Linux运维相关知识,本文主要内容是SQL手工注入漏洞测试(MongoDB数据库). 一.题目简介 我们这里采用墨者学院的MongoDB数据库渗透测试题目,其地址为:https:/ ...

  4. php 登陆 sql语句,PHP 连接MySQL数据库的SQL语句的简单示例

    这篇文章主要为大家详细介绍了PHP 连接MySQL数据库的SQL语句的简单示例,具有一定的参考价值,可以用来参考一下. 首先用phpmyadmin进入建立数据库user 再建个三段的表admin 再别 ...

  5. 『数据库』数据库笔记

    前言 结构化查询语言(Structured Query Language)简称SQL,是一种特殊目的的编程语言,是一种数据库查询和程序设计语言,用于存取数据以及查询.更新和管理关系数据库系统. 结构化 ...

  6. azure云数据库_Azure SQL数据库中的高级数据安全性–数据发现和分类

    azure云数据库 Azure SQL supports in building and managing wide range of SQL databases, tools, frameworks ...

  7. MySQL数据库之SQL的各种操作/Html/Java和XML的关系

    MySQL数据库之SQL的各种操作/Html/Java和XML的关系 今天内容:(1)数据库的概述(2)MySQL数据库的环境搭建(3)常用的数据类型(4)DDL数据定义语句(5)DML数据操纵语句1 ...

  8. 淘宝数据库OceanBase SQL编译器部分 源码阅读--解析SQL语法树

    http://blog.csdn.net/qq910894904/article/details/28658421 OceanBase是阿里巴巴集团自主研发的可扩展的关系型数据库,实现了跨行跨表的事务 ...

  9. postgreSql数据库笔记

    postgreSql数据库笔记 1.pg创建序列: SELECT nextval('seq_bsm' :: regclass) as XH;//查询序列值 //创建序列 CREATE SEQUENCE ...

最新文章

  1. 多图详解教程:Eclipse 3.6连接Tomcat 7
  2. 一维数组对象转成二维数组
  3. HBase在淘宝的应用和优化
  4. 在Kubernetes Pod中使用Service Account访问API Server
  5. python二级考试选择题公共基础知识_计算机二级Python易忘考点整理
  6. 搜狗520甜蜜告白攻势:爱的心动 让她看见
  7. 一年太久,研究员决定不等补丁直接披露 Safari 0day 详情
  8. 读《scikiit-learn机器学习》第七章_决策树
  9. 【转】三层架构的业务逻辑层存在的意义
  10. HDFS +zookeeper实现高可用
  11. textbox wpf 居中_WPF TextBox控件中文字实现垂直居中
  12. 如何在桌面上显示我的计算机,Win10如何将我的电脑(此电脑)显示到桌面上?
  13. Android 复杂的多类型列表视图新写法:MultiType 3.0
  14. 第十三周项目1---(4)Floyd算法验证
  15. 【codevs2853】方格游戏 DP
  16. 在浏览器输入一句话之后是如何响应的
  17. 让你的发动机与NXP Kinetis汽车套件一起运行---凯利讯半导体
  18. ERDAS 9.2安装教程
  19. TCP劫持及反弹shell攻击
  20. 手把手教你安装Ubuntu系统增强工具

热门文章

  1. 关于 xml 库运行时的segmentation fault 问题
  2. 【v2.x OGE-example 第一节】 绘制实体
  3. 无废话ExtJs 入门教程二十三[员工管理实例:Demo]
  4. 为加密的NTFS分区制作一把备份密钥
  5. javascript删除元素节点
  6. 文件操作中file.seek()方法
  7. PAT甲级1008 Elevator:[C++题解]模拟
  8. 得到课程《组织行为学》学习笔记07
  9. 《剑指offer》c++版本 12. 矩阵中的路径
  10. 深入理解向上转型与向下转型