(2021.12.11 Sat)
关系型数据库中,一个表格称为关系,每一行称为一个记录,每个列是一个属性。SQL中大小写不敏感(case-insensitive)。
定义语句
建立表语句
CREATE TABLE table_name (field_name1 type [NOT NULL [UNIQUE]], field_name2 type [NOT NULL [UNIQUE]], ...);
说明:
- SQL列的数据类型包括
- CHAR(n): 长度为
n
的字符串型 - DECIMAL[(m[, n])]: 共
m
位(不包含小数点)且有n
位小数的数。当m
和n
省略时,其含义由系统自定义。该类型为精确数值类型,还可写成DEC(m[, n])或NUMERIC(m[, n]) - INTEGER或INT: 四字节或二字节整数
- SMALLINT: 二字节整数
- FLOAT(n):
n
位有效数字的实型数 - REAL: 单精度实型数
- DOUBLE PRECISION: 双精度实型数
- CHAR(n): 长度为
CREATE TABEL student (sid CHAR(2) NOT NULL UNIQUE, name CHAR(10), age SMALLINT, gender CHAR(2));
从其他表复制创建
CREATE TABLE table2 LIKE table1;
INSERT INTO table2 SELECT * FROM table1;
扩充表(添加列)
ALTER TABLE <table_name> ADD (field_name1 type [NOT NULL], field_name2 ...);
ALTER TABLE sudent ADD (birthplace CHAR(10)); //添加列birthplace
ALTER TABLE stu_score_bk ADD (sid INT AUTO_INCREMENT, PRIMARY KEY (sid));
// 添加一列sid,并将其设置为自加和primary key
修改表
ALTER TABLE <table_name> MODIFY (field_name1 type [NOT NULL], field_name2 type ...);
该语句可以修改表中列的类型和长度,但不能修改列名本身。若一个列要修改数据类型,则该数据必须全为空值。
删表
DROP TABLE <table_name>;
增删改UPDATE/DELETE/INSERT
增删改操作是对数据的基本操作,属于数据操作语言(Data Manipulation Language)。不同于前面的创建、添加、删表等操作,这些被成为数据定义语言(Data Definition Language),其实现了对数据库结构和操作的定义。DDL指令在执行之后会隐式执行commit
操作,即对数据库做了对应的动作。而DML语言并不隐式含有commit
操作,需要手动执行commit
指令才能实现对数据的修改。在执行DML操作后,手动加入commit
指令。
START TRANSACTION;
...
INSERT INTO Student (name,department) values ("xiaoming","computer science");
COMMIT;
删除记录
DELETE FROM student_table where name = 'xiaoming';
此外,执行操作后是否需要commit
也需要考虑数据引擎。对于不支持事务的引擎,如myisam
,commit
指令无效。而支持事务的引擎,如innodb
,支持自动提交commit
。
查看数据库是否支持commit
,可用如下指令查看,如果返回结果为on
,则支持查看。
mysql > show variables like '%autocommit%';
约束
(2021.12.15 Wed)
- DEFAULT: 用于向列中插入默认值。如果没有其他的规定,默认值会被添加到所有新记录。在创建表和修改表时都可以使用。
CREATE TABLE some_table
(
PID INT NOT NULL,
...
city VARCHAR(255) DEFAULT 'HONGKONG',
OrderDate DATE DEFAULT GETDATE()
)
ALTER TABLE some_table
ALTER city SET DEFAULT 'London';
撤销DEFAULT
约束
ALTER TABLE some_table
ALTER city DROP DEFAULT
CHECK约束
用于限制列中的值的范围。如果对单个列定义 CHECK 约束,那么该列只允许特定的值。如果对一个表定义 CHECK 约束,那么此约束会在特定的列中对值进行限制。
CREATE TABLE Persons
(
Id_P int NOT NULL,
...
City varchar(255),
CHECK (Id_P>0)
)
如果命名 CHECK 约束,以及为多个列定义 CHECK 约束,采用如下方法
CREATE TABLE Persons
(
Id_P int NOT NULL,
City varchar(255),
CONSTRAINT chk_Person CHECK (Id_P>0 AND City='Sandnes')
)
在修改表的过程中
ALTER TABLE Persons
ADD CHECK (Id_P>0)
如果命名 CHECK 约束,以及为多个列定义 CHECK 约束,采用如下方法
ALTER TABLE Persons
ADD CONSTRAINT chk_Person CHECK (Id_P>0 AND City='Sandnes')
撤销约束
ALTER TABLE Persons
DROP CHECK chk_Person
自增auto_increment
每次插入新纪录时,自动的创建主键字段的值。
下面这个例子中,创建表Persons
,设置字段PId
为自增字段,将其设置为主键。
CREATE TABLE Persons
(
PId int NOT NULL AUTO_INCREMENT,
...
City varchar(255),
PRIMARY KEY (PId)
)
默认的auto_increment字段的初值为1,每条新记录自增加1.
要让auto_increment
字段的初始值从其他值开始,需要使用如下指令
ALTER TABLE Persons AUTO_INCREMENT=99;
向这个表中添加数据时,不需要指定PId
的值,自动加1.
INSERT INTO Persons (FirstName,LastName)
VALUES ('Bill','Gates')
其他SQL服务器的命令和MySQL不同。
SQL简单查询 select...from...where
(2021.12.12 Sun)
基本格式如下
SELECT * FROM table_name
该格式选择表中所有属性返回。对表属性做投影,可直接写出所选属性
SELECT field_name1, field_name2, ... FROM table_name
选择的属性可以重命名,用AS
关键字,或不用关键字,只用空格隔离;选择的属性可以重新赋值;可重新定义属性的值
SELECT field_name1 AS field_name_edited,
field_name2 field_name_edited_2,
field_name3 * 0.5 field_name_edited_3,
'hrb' AS field_name_new
FROM table_name
关系运算符
除了常见的加减乘除等,还有对字符的连接操作||
,如'foo' || 'bar'
的运算结果是'foobar'
。对字符串的比较操作,实际上是比较字符串在字典中的顺序(假想字符串出现在字典中)。哪个字符串在前,哪个字符串的值比较小。比如'fodder' < 'foo'
,'bar'<'bargin'
。
另外,需要注意的是,尽管SQL语言本身大小写不敏感,但是涉及字符串的操作,或对字符串的操作,是大小写敏感的。
模式匹配
s LIKE p
其中的p
表示模式,该指令用于匹配(字符串)属性s
与模式p
。模式中的%
表示匹配任意多个(含0)字符,_
表示任意一个字符。
SELECT title FROM movies
WHERE title LIKE 'Star _ _ _ _';
该指令返回的可能结果是Star Trek
和Star Wars
。
符号%
可匹配0或任意多个字符。
SELECT title
FROM Movies
WHERE title LIKE '% ' ' s%';
SQL约定,字符串中两个连续的单引号表示一个单引号,而不作为字符串的结束符。该代码表示是查询名字中含有's
的所有电影。
日期和时间
日期由DATE
关键字加上表示日期的特定形式字符串组成
DATE'YYYY-MM-DD'
DATE'1960-01-01'
时间和时间戳类似于日期,其关键字分别是TIME
和TIMESTAMP
。
TIME'15:00:00.5'
TIMESTAMP'2021-12-12 09:46:35'
空值和涉及空值的比较
空值NULL
有几种常见的解释
- value unknown未知值,知道其有值但不知道其值
- value inapplicable不适用的值,任何值在这里都没有意义,如MovieStar关系中,如果该演员单身,则
spouse
属性为空,因为没配偶 - value withheld保留的值,属于某对象但无权知道的值,比如未公布的电话号码属于空值
空值运算的规则
- 对
NULL
与任何值(包括一个空值)进行算术运算,如加减乘除,其结果仍然是空值NULL
- 对空值做比较运算,包括与另一个
NULL
值做比较,结果都是UNKNOWN
值,而UNKNOWN
值时另外一个与TRUE
和FALSE
相同的布尔值
注意,NULL
可出现在元组中,但它不是一个常量,因此可对空值表达式进行运算,但不可以直接将NULL
作为一个操作数。
正确判断x
的值时否为NULL
的方式
x IS NULL
关于UNKNOWN
有下面关系
NOT UNKNOWN --> UNKNOWN
TRUE AND UNKNOWN --> UNKNOWN
FALSE AND UNKNOWN --> FALSE
TRUE OR UNKNOWN --> TRUE
FALSE OR UNKNOWN --> UNKNOWN
排序ORDER
排序指令ORDER BY
对输出结果进行排序,默认是升序ASC
,也可进行降序DESC
排列。除了对某个属性的值按升序或降序排列,也可以对多个属性值的运算做排序。
SELECT *
FROM Movies
WHERE studioName = 'Disney' AND year = 1990
ORDER BY A+B DESC;
多关系查询
SQL用简单的方式在一个查询中处理多个关系:在FROM
子句中列出每个关系,用逗号隔开,在SELECT
子句和WHERE
子句中引用任何出现在FROM
子句中关系的属性。
为避免引用属性歧义,可以在FROM
子句中给关系赋缩略名,或者在调用关系中的属性时直接加入关系的名字。
SELECT a.name, b.length
FROM Movies a, MovieExec b
WHERE a.name = b.name;
子查询subquery
在SQL中,一个查询可以通过不同的方式被用来计算另一个查询,当某个查询是另一个查询的一部分是,称之为子查询subquery。
子查询可以返回单个常量,这个常量能再WHERE
子句中和另一个常量做比较。子查询能返回关系,该关系可以在WHERE
子句中以不同的方式使用。子查询形成的关系也能出现在FROM
子句中,并且后面紧跟该关系的元组变量。
SELECT name
FROM MovieExec
WHERE cert IN
(SELECT producerC
FROM Movies
WHERE (title, yer) IN
(SELECT movieTitle, movieYear
FROM StarsIn
WHERE starName = 'Harrison Ford'
)
);
该查询返回Harrison Ford演过的电影的制片人。
SELECT name
FROM MovieExec, (SELECT producerC,
FROM Movies, StarsIn
WHERE title = movieTitle AND
year = movieYear AND
starName = 'Harrison Ford'
) Prod
WHERE cert = Prod.producerC;
使用FROM
子句子查询找出Ford出演的电影的制片人。
子查询也可产生标量。
SELECT name
FROM MovieExec
WHERE cert =
(SELECT producerC
FROM Movies
WHERE title = 'Star Wars'
);
此外,也可使用LEFT/RIGHT/INNER/OUTER JOIN
的命令实现关系的连接。(略)
与子查询经常一同出现的谓词包括IN
, ALL
, ANY
, EXISTS
。
SELECT snum, sscore
FROM SCORE
WHERE course_num = 'c2' AND sscore > ALL
(SELECT sscore
FROM SCORE
WHERE course_num = 'c5');
该查询返回c2
成绩高于c5
最高成绩的学生号和成绩。如果其中的关键词换成ANY
,则返回c2
成绩高于c5
最低成绩的学生号和成绩。
函数查询和聚集
常见的函数查询包括
-
AVG(field_name)
: 求指定数值列秩的算数平均值 -
COUNT(field_name)
: 求指定列中,值的个数 -
MAX(field_name)/MIN
: 值的最大或最小值 -
SUM(field_name)
: 值的总和
注意,COUNT(*)
用于查询有多少条记录,其前不能使用DISTINCT
关键字。而如果求某属性有多少个不重合的值,需使用COUNT(DISTINCT field_name)
。如果只是使用指令COUNT(field_name)
,则返回A属性非空的元组个数之和。AVG(field_name+5)
表示对field_name属性的值都加5再求均值。在使用SUM(field_name)
时,如果field_name
属性中含有NULL
值,则返回的和应为NULL
。
GROUP分组和HAVING条件
分组操作往往与函数查询联合使用。
SELECT A, COUNT(B)
FROM R
GROUP BY A;
HAVING
子句有这两个规则
-
HAVING
子句中的聚集只应用到正在检测的分组上 - 所有
FROM
子句中关系的属性都可以在HAVING
子句中用聚集运算,但是只有出现在GROUP BY
子句中的属性,才可能以不聚集的方式出现在HAVING
子句中。
(2022.07.22 Fri)
GROUP BY命令有另外两个OLAP修饰符联合使用,ROLLUP
和CUBE
。这两个修饰符的作用如下
-
ROLLUP
:对分组后各组的聚合结果进行汇总,在结果中输出,提供更高一级的聚合操作。即如果聚合函数是AVG
,则加了WITH ROLLUP
提示符后,额外输出一行,记录所有分组的AVG
。这个操作相当于做分组操作的结果与对全体数据做相同操作的结果的UNION ALL
。比如求一个班级里面各科目的平均分,并在此基础上计算所有科目的平均分
SELECT AVG(score) as avgscore, subject FROM score_table
GROUP BY subject WITH ROLLUP;
// or
SELECT AVG(score) AS avgscore, subject FROM score_table
GROUP BY subject
UNION ALL
SELECT 'total', AVG(score) AS avgscore FROM score_table
-
CUBE
:生成的结果集显示了所选列中值的所有组合的聚合,而ROLLUP
生成的结果集显示了所选列中值的某一层次结构的聚合。CUBE
案例:在MySQL 8.0上暂时运行失败,placeholder。
其他函数
(2021.12.15 Wed)
- FIRST/LAST(field_name): 返回该列的第一个/最后一个元素,该命令仅适用于MS Access。
SELECT FIRST(field_name) FROM table;
SELECT LAST(field_name) FROM table;
在MySQL中,返回该列的第一个/最后一个元素用如下方式
SELECT field_name
FROM table
ORDER BY field_name ASC/DESC (?)
LIMIT 1;
(2022.07.31 Sun)
注意这里的LIMIT
是一个分页函数,常规的用法是
LIMIT <starting_line>, <row_counter>;
该指令用于读取特定行(starting_line
)开始的row_counter
行数据。如果不指定开始行如LIMIT 20
,则从0行开始读取row_counter
行数据,即前20行数据。
分页函数的另一种语法是
LIMIT <row_counter> OFFSET <starting_line>;
- UCASE/LCASE(field_name): 把所选字段的值都改为大写/小写。在SQL server中是UPPER/LOWER。
SELECT UCASE(city_name) AS city_name_upper FROM city_table;
- MID(field_name, start[, length])/SUBSTR(field_name, start[, length]): 从文本字段中提取字符。其中的
start
只能从1开始,length
表示提取的长度,不标记则为返回剩余文本。MID和SUBSTR的动作完全相同。
SELECT MID(company_name, 1, 3) AS shorten_name FROM table;
如果table
表中的这个字段第一个数据是Google
,则返回Goo
。
- LEN(field_name): 返回文本字段中值的长度。MySQL中该指令为
LENGTH(field_name)
。
SELECT LENGTH(company_name) FROM table;
该字段中Google
返回6.
- ROUND(field_name[, digit]): 把数值字段舍入为指定位数的数字,默认的
digit=0
,即没有小数部分。返回值转换为BIGINT
型。
SELECT ROUND(field_name, 0) FROM table;
- NOW(): 返回系统当前时间,格式为
YYYY-MM-YY HH:MM:SS
SELECT city_name, NOW() AS current_time FROM table;
返回Harbin, 2021-12-15 16:53:30
- FORMAT(field_name, format): 对字段做格式转换。多用于时间格式转换。
SELECT FORMAT(NOW(), 'YYYY-MM-DD') AS nowtime, product_id FROM table;
一个用到局部变量的例子,在SQL server中,来自microsoft docs页面。
DECLARE @d DATE = GETDATE();
SELECT
FORMAT( @d, 'dd/MM/yyyy', 'en-US' ) AS 'Date',
FORMAT(123456789,'###-##-####') AS 'Custom Number';
- CAST(field_name AS type_name(para)): 对字段类型做转换。注意CAST没有舍入截断操作,将小数转换为整数会产生错误。
SELECT CAST(number_field AS INT) AS number_field_int FROM table;
SELECT CAST(number_field AS decimal(9, 2)) AS decimal_field FROM table; # 表示最大位数9,2位小数,即9999999.00
SELECT CAST(some_id AS CHAR(4)) as id_char FROM table;
UNION操作符
(2021.12.16 Thur)
MySQL的UNION操作符用于将连接两个以上的SELECT语句的结果组合到一个语句中。可根据关键字ALL/DISTINCT来决定是选择全部结果还是删除重复结果。
SELECT field1, field2, field3
FROM table1
UNION
SELECT field1, field2, field3
FROM table2
上面表达中,UNION
表示默认情况,即删除重复结果,等效于UNION DISTINCT
。如果想保留所有结果,含重复结果,使用UNION ALL
指令。
(2022.07.15 Fri)
此外,UNION
命令默认对结果排序,而UNION ALL
不排序。从速度来看,显然UNION ALL
更快。
视图view
视图是SQL中除了关系以外另一种基本数据结构。视图是数据库中满足一定约束条件的数据组成的表,但其本身不占用实际存储器。视图可看做是一个虚表。从用户角度看,关系和视图没有任何区别。
视图可用作某个用户的专用数据部分,便于使用,又提高了数据的独立性,也可以把表中数据隐藏起来,加强数据的保密性。
定义视图
CREATE VIEW <view_name> [(view_list)]
AS SELECT ...
[WITH CHECK OPTION];
结尾的WITH CHECK OPTION
语句,如果省略,则视图是一个只读表,不限制插入到该视图的数据值;否则是一个可更新表,插入到该视图的数据值必须满足在该视图定义时SELECT
语句指定的条件。
下面例子生成一个平均成绩视图。
CREATE VIEW ave_score (snum, name, gender, avg_s)
AS SELECT s.snum, s.name, s.gender, avg(sc.score)
FROM student s, score sc
WHERE s.snum = sc.snum
GROUP BY snum;
视图的查询指令与关系相同。
视图的更新指令与基本表相同。但是一个视图要进行更新操作,应满足下面基本条件
- 视图只由一个基本表导出
-
SELECT
语句中不含有GROUP
子句、组函数和DISTINCT
- 视图中的列不是由表达式定义的
- 基本表中全部带有
NOT NULL
的列都属于该视图
删除视图
DROP VIEW view_name
存储过程PROCEDURE
(2021.12.16 Thur)
SQL数据控制功能
控制功能指的是控制用户对数据的存储权力。
授权语句
GRANT grant_list ON table_name TO user_name/PUBLIC [WITH GRANT OPTION]
其中的grant_list
是权力表,如果授予所有权利,则用ALL
代替grant_list
.
权力表包括
- ALTER 修改表结构
- SELECT 查询权
- DELETE 删除记录
- INSERT 插入新纪录权
- UPDATE 对指定列或全体列的值修改的权利
而ALL
指代上面所有的权利。而如果所授予的不是数据表而是视图,则只能授予SELECT
, DELETE
, INSERT
, UPDATE
.
用户名可以是个别用户,也可以是全部用户,比如PUBLIC
。
如果出现WITH GRANT OPTION
,则得到授权的用户可以向其他人授权。
取消授权
REVOKE ALL/grant_list ON table_name FROM user_name/public
SQL事务Transaction
目前,数据库上的操作模型是用户查询或更新数据库。数据库上的操作一次只执行一个,一个操作留下的数据正是下一个操作所要起作用的。操作中软硬件都不会出错,不会留下操作的结果不能解释的数据库状态。
可串行化
银行行业或机票预定中,往往出现多个人同时进行同一个业务,也就是同时修改数据库的情况。比如订机票,两个人同时看到有一个空位,并且(几乎)同时执行订票操作,到底谁顶到票呢?
为避免上面说的情况引起数据库系统的错误和崩溃,引入了串行化概念。SQL允许程序员规定一个特定的事务必须对于别的事物可以串行化(serializabel),即这些事物必须表现得好像他们是串行的(serially)执行,也就是一个时刻只有一个事物,相互之间没有重叠。
DBMS中一个普通的方式是锁定(lock)数据库的元素防止被两个函数同时访问。
前面订票的案例,如果两个用户对订票的操作是串行执行的,那么就不会出现前面提到的尴尬情况。
原子性atomic
考虑一个例子,从A账户到B账户转账100卢比。其中包含两个动作,一个是A账户减少100卢比,第二个是B账户增加100卢比。
在执行转账的过程,一旦硬件或网络出现问题,会无法完成这两个看似分开,但实则应为一体的动作。
该案例说明数据库的操作的某些组合需要原子地atomically执行,即他们要么都执行要么都不执行。一个简单的解决方案是将对数据库的所有修改都在一个本地工作区中执行,而且只有在所有工作都完成后才能修改提交(commit)到数据库,于是所有改变成为数据库的一部分,对其他操作可见。
事务Transaction
对可串行化和原子性问题的解决方案将把数据库操作分组为事务。事务是必须原子地执行一个或多个数据库操作的集合,即要么所有操作都执行要么所有操作都不执行。SQL要求默认事务以可串行化方式执行。
在SQL中,每条语句自身就是一个事务。不过SQL允许程序员将几条语句组成一个事务。用命令START TRANSACTION
来标记事务的开始。有两种方式结束事务:
- SQL的
COMMIT
语句使得事务成功结束,即被提交了。在COMMIT
被执行之前,改变是试探性的,对其他事务可不可见均有可能。 - SQL的
ROLLBACK
语句使得事务夭折abort
或不成功结束。任何由该事务的SQL语句所引起的修改都被撤销,即被回滚rolled back
。所以他们不会持久的出现在数据库中。
只读事务read-only
如果告知SQL执行系统当前的事务是只读事务,即它不会修改数据库,那么SQL系统很可能能够充分利用这一点。通常,多个访问同一个数据的只读事务可以并行执行,但是多个写同一个数据的事务并不能并行执行。
告知SQL系统下一个事务是只读事务的语句是
SET TRANSACTION READ ONLY;
这条语句必须在事务开始前执行。
可以通过如下语句通知SQL下一个事务可以写数据
SET TRANSATION READ WRITE;
不过这个选项是默认选项。
读脏数据dirty data(待补充)
脏数据表示还没有提交的事务所写的数据的通用术语。藏独dirty read是对脏数据的读取。读脏数据的风险是写数据的事务可能最终夭折。
有时可能需要冒险偶尔脏读一次,从而避免
- DBMS用啦防止脏读所作的耗时的工作
- 为了等到不可能出现脏读而造成的并发性的损失
使用SET TRANSACTION
语句,SQL允许指定一个给定的事务是否可以脏读,比如
SET TRANSACTION READ WRITE
ISOLATION LEVEL READ UNCOMMITED;
这个语句做了两件事,一是声明可以写数据,二是声明事务用读未提交read-uncommited的隔离层次进行。即允许事务读脏数据。
隔离层次
共四种,可串行化、读未提交(允许脏读)、读提交(read-commited)和可重复读(repeatable-read)。
他们可以通过如下语句指定
SET TRANSATION ISOLATION LEVEL READ COMMITED;
SET TRANSATION ISOLATION LEVEL REPEATABLE READ;
对每条语句,默认事务是读写的,所以在适当的情况下,可在每条语句后面加上READ ONLY
。有一个指定选项
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
但这是SQL的默认情况,不必显式指定。
Reference
1 匙彦斌等主编,计算机软件技术基础教程,天津大学出版社
2 Jeffery U.等著,岳丽华等译,数据库系统基础教程,机械工业出版社