程序员的SQL(基础)

目录

第一章:数据库入门

(SQL语言的介绍)

第二章:数据表的创建和管理

(创建表,修改表,删除表)

第三章:数据的增删改

(数据的插入,更新,删除,自动增长字段)

第四章:数据的检索

(select基本语法,单/多字符匹配,集合匹配,空值,反义运算符,多值/范围值检测,数据分组,抑制数据重复,计算字段,联合结果集,WITH子句)

第五章:函数(仅限MySQL)

(数学函数,字符串函数,日期时间函数,类型转换,空值处理,MySQL独有的函数)

第六章:索引与约束

(创建/删除索引,非空约束,唯一约束/复合,check约束,主键约束/复合,业务/逻辑主键,外键约束)

第七章:表连接

(交叉连接,内连接,左外/右外连接,全外连接,自连接)

第八章:子查询

(select/where/from子查询,IN/ANY/ALL运算符/EXISTS子查询,在INSERT INTO/UPDATE上的子查询)

第九章:SQL的一些优化

(SQL注入,SQL的查询优化器,索引,一些优化)

第十章:事务

(原子/隔离/一致/持久性,隔离性:脏读,不可重复读,幻影读,隔离级别:未提交读,提交读,可重复读,序列化读,新加入了锁的各种说明!)

第十一章:NULL的学问

(NULL值在数据库代表啥,NULL和计算字段/字符串/函数/聚合函数


第一章:数据库入门

1.数据库与数据库管理系统(DBMS):

数据库指存储数据的仓库,而DBMS指操纵和管理数据库的系统软件。是用于建立、使用和维护数据库。

2.DB2介绍:

在关系数据库理论方面一直走在业界的前列,所以DB2 的功能和性能都是非常优秀的,不过对开发人员的要求也比其
他数据库系统更高,使用不当很容易造成宕机、死锁等问题;DB2 在SQL的扩展方面比较保守,很多其他数据库系统支持的SQL 扩展特性在DB2 上都无法使用;同时DB2 对数据的类型要求也非常严格,在数据类型不匹配的时候会报错而不是进行类型转换,而且如果发生精度溢出、数据超长等问题的时候也会直接报错,这虽然保证了数据的正确性,但是也使得基于DB2的开发更加麻烦。因此,很多开发人员称DB2 为“最难用的数据库系统”

3.oracle介绍:

Oracle从DB2等产品中吸取到了很多优点,同时又避免了IBM的官僚体制与过度学术化,大胆的引进了许多新的理论与特性,所以Oracle 无论是功能、性能还是可用性都是非常好的

4.SQL Server介绍:

Microsoft SQL Server 的可用性做的非常好,提供了很多了外围工具(可视化...)来帮助用户对数据库进行管理,用户甚至无需直接执行任何SQL 语句就可以完成数据库的创建、数据表的创建、数据的备份/恢复等工作;Microsoft SQL Server 的开发者社区也是非常庞大的,因此有众多可以参考的学习资料。Microsoft SQL Server 的劣势也是非常明显的:只能运行于Windows 操作系统,因此我们无法在Linux、Unix 上运行它;不管微软给出什么样的测试数据,在实际使用中Microsoft SQL Server 在大数据量和大交易量的环境中的表现都是不尽人意的,当企业的业务量到达一个水平后就要考虑升级到Oracle或者DB2 了。

5.MySQL介绍:

MySQL 被广泛地应用在中小型系统中,特别是在网络应用中用户群更多。MySQL 目前还很难用于支撑大业务量的系统,所以目前MySQL 大部分还是用来运行非核心业务

第二章:数据表的创建和管理

1.字符相关类型

固定长度字符类型:名称为char。如果设定的字符串长度不满固定长度,那么剩余部分将以空格填充。由于剩余部分会以空格填充,那么在读取的字段值的时候就会将后面填充的空格也读取出来,这有的时候是很不方便的的。

可变长度字符类型:名称为varchar。可变长度字符类型一般也需要指定一个长度,但是这个长度指的是此字段所能保存的字符串的最大长度,如果保存的字符串的长度没超过最大长度的话,数据库将不会将剩余部分用空格填充。(速度不如固定长度!)

限制:固定长度字符类型和可变长度字符类型都只能存储基于ASCII 的字符,这样对于使用中文、韩文、日文等Unicode字符集的程序来讲将会造成储存问题

可变长度字符类型:名称为nvarchar。使用国际化可变长度字符类型,这种类型可以用两个字节来保存一个字符,这样就可
以解决中韩日等字符串保存的问题了。

2.创建数据表

CREATE TABLE 表名
(
字段名1 字段类型,
字段名2 字段类型,
字段名3 字段类型,
………………
约束定义 1,
约束定义 2,
………………
)

注意:

这个表名不能与数据库中已有的表名重复;

括号中是一条或者多条表定义,表定义包括字段定义和约束定义两种,一张表中至少要有一个字段定义

约束定义则是可选的。约束定义包括主键定义、外键定义以及唯一约束定义等。

定义非空约束:(字段定义后加not null 关键字)

CREATE TABLE T_Student (FNumber VARCHAR(20) NOT NULL ,FName VARCHAR(20)
NOT NULL ,FAge INT NOT NULL ,FFavorite VARCHAR(20),FPhoneNumber VARCHAR(20))

定义默认值:(字段定义后加 default "no")

CREATE TABLE T_Teacher (FNumber VARCHAR(20),FName VARCHAR(20),FAge
INT,FISMaster VARCHAR(5) DEFAULT 'NO')

定义主键:(添加约束primary key (fnumber))

CREATE TABLE T_Bus (FNumber VARCHAR(20),FDriverName VARCHAR(20),
FUsedYears INT,PRIMARY KEY (FNumber))

注意:

在有的数据库系统中主键字段名两侧的括号是可以省略的,也就是可以写成PRIMARY KEY FNumber,不过为了能够更好的跨数据库,建议不要采用这种不通用的写法。

联合主键:(表中没有主键时)

通过公司名称FCompanyName 和公司内部工号FInternalNumber 两个字段一起就可以唯一确定一个人,我们可以让FCompanyName、FInternalNumber 两个字段联合起来做为主键!只要在PRIMARY KEY后的括号中列出做为联合主键的各个字段就可以了。

但是一般不会使用联合主键,因为在数据更新的时候需要处理两个字段速度慢;系统维护的时候麻烦,表关联可能无法进行!

定义外键:(foreign key 外键 references 表(主键))

通过使用外键,我们才能把互相独立的表关联起来!

CREATE TABLE T_Department (FId VARCHAR(20),FName VARCHAR(20),
FLevel INT,PRIMARY KEY (FId))
CREATE TABLE T_Employee (FNumber VARCHAR(20),FName VARCHAR(20),
FDepartmentId VARCHAR(20),
FOREIGN KEY (FDepartmentId) REFERENCES T_Department(FId))

3.修改已有数据表

添加字段的语法:(alter  table  表名 add  字段名  字段类型)

ALTER TABLE T_PERSON ADD FFavorite VARCHAR(20)

删除字段的语法:(alter table 表名 drop 字段名)

ALTER TABLET_Person DROP FAge

4.删除数据表

 删除数据表:(drop table 表名)

DROP TABLE T_Person

注意:如果在表之间创建了外键关联关系,那么在删除被引用数据表的时候会删除失败,因为这样会导致关联关系被破坏,所以必须首先删除引用表(带有外键的表),然后才能删除被引用表(关联到主键的表)。比如A表创建了指向B表的外键关联关系,那么必须首先删除A表后才能删除B表。

第三章:数据的增删改

本章要用到的SQL表:

CREATE TABLE T_Person (FName VARCHAR(20),FAge INT,FRemark
VARCHAR(20),PRIMARY KEY (FName));
CREATE TABLE T_Debt (FNumber VARCHAR(20),FAmount DECIMAL(10,2) NOT NULL,
FPerson VARCHAR(20),PRIMARY KEY (FNumber),
FOREIGN KEY (FPerson) REFERENCES T_Person(FName)) ;

1.数据的插入

INSERT INTO T_Person(FName,FAge,FRemark) VALUES('Tom',18,'USA')

注意:

VALUES前列出的字段名和VALUES后边列出的字段值是按顺序一一对应的;

INSERT语句中也并不需要我们指定表中的所有列,比如在插入数据的时候某些字段没有值,我们可以忽略这些字段。

INSERT语句也可以省略VALUES前面的字段定义,但VALUES后面的值列表中必须按照CREATE TABLE语句中的顺序排列

数据插入对非空约束对数据插入的影响;(必须给一个值)

数据插入对主键对数据插入的影响;(必须给一个不重复的值)

数据插入对外键对数据插入的影响;(必须给定一个在关联的主键上出现过的值)

2.数据的更新

数据的全部更新:

UPDATE T_Person SET FRemark = 'SuperMan'

带where的更新:(使用更一般性)

UPDATE T_Person SET FAge = 12 WHERE FNAME='Tom'

注意:

与数据插入类似,还是要注意非空约束,主键,外键,对更新的影响。

3.数据的删除

数据的全部删除:

DELETE FROM T_Debt;
DELETE FROM T_Person;

类似还有带where的删除!

注意:

如果被删除的数据行是某个外键关联关系中的被引用数据的话,则进行删除的时候会失败,如果要删除成功则必须首先删除引用者才可以。

DROP TABLE 语句和DELETE:DELETE 语句仅仅是删除表中的数据行,而表的结构还存在;DROP TABLE语句则不仅将表中的数据行全部删除,而且还将表的结构也删除。

4.自动增长字段(MySQL)

在表定义中指定字段为AUTO_INCREMENT即可(从1开始)!

CREATE TABLE T_Person
(
FId INT PRIMARY KEY AUTO_INCREMENT,
FName VARCHAR(20),
FAge INT
);

第四章:数据的检索

本章要用到的SQL表:

CREATE TABLE T_Employee (FNumber VARCHAR(20),FName VARCHAR(20),FAge
INT,FSalary DECIMAL(10,2),PRIMARY KEY (FNumber));
INSERT INTO T_Employee(FNumber,FName,FAge,FSalary)
VALUES('DEV001','Tom',25,8300);
INSERT INTO T_Employee(FNumber,FName,FAge,FSalary)
VALUES('DEV002','Jerry',28,2300.80);
INSERT INTO T_Employee(FNumber,FName,FAge,FSalary)
VALUES('SALES001','John',23,5000);
INSERT INTO T_Employee(FNumber,FName,FAge,FSalary)
VALUES('SALES002','Kerry',28,6200);
INSERT INTO T_Employee(FNumber,FName,FAge,FSalary)
VALUES('SALES003','Stone',22,1200);
INSERT INTO T_Employee(FNumber,FName,FAge,FSalary)
VALUES('HR001','Jane',23,2200.88);
INSERT INTO T_Employee(FNumber,FName,FAge,FSalary)
VALUES('HR002','Tina',25,5200.36);
INSERT INTO T_Employee(FNumber,FName,FAge,FSalary) VALUES('IT001','Smith',28,3900);

1.select的基本用法

SELECT * FROM T_Employee  #检索出所有列

SELECT FNumber FROM T_Employee #检索出需要的列

SELECT FNumber AS Number1,FName AS Name,FAge AS Age,FSalary AS Salary FROM
T_Employee  #指定列的别名

SELECT FName FROM T_Employee
WHERE FSalary<5000 #带where的条件查询

注意:

定义别名的时候 “AS” 不是必须的,是可以省略的!

数据汇总:(MAX,MIN,AVG,SUM,COUNT)

SELECT MAX(FSalary) FROM T_Employee
WHERE FAge>25

SELECT AVG(FAge) FROM T_Employee
WHERE FSalary>3800

SELECT COUNT(*),COUNT(FNumber) FROM T_Employee

注意:

count(*)和count(col)的区别:COUNT(*)统计的是结果集的总条数,而COUNT(FName)统计的则是除了结果集中FName 不为空值(也就是不等于NULL)的记录的总条数

排序:(order by子句)

SELECT * FROM T_Employee
ORDER BY FAge ASC  #代表升序排列(默认行为),DESC代表降序排列

注意:

ORDER BY语句允许指定多个排序列,各个列之间使用逗号隔开即可。

对于多个排序规则,数据库系统会按照优先级进行处理。数据库系统首先按照第一个排序规则进行排序;如果按照第一个排序规则无法区分两条记录的顺序,则按照第二个排序规则进行排序;如果按照第二个排序规则无法区分两条记录的顺序,则按照第三个排序规则进行排序;……以此类推。

order by子句与where子句的说明:ORDER BY子句完全可以与WHERE子句一起使用,唯一需要注意的就是ORDER BY子句要
放到WHERE子句之后,不能颠倒它们的顺序。

2.高级数据过滤

单字符匹配:(“_”匹配单个出现的任意字符)

SELECT * FROM T_Employee
WHERE FName LIKE '_erry'

SELECT * FROM T_Employee
WHERE FName LIKE '__n_'  #单字符匹配符号也可以出现多次

多字符匹配:(“%”匹配任意次数(零或多个)出现的任意字符)

SELECT * FROM T_Employee
WHERE FName LIKE 'T%'

集合匹配:(通配符为“[]”,方括号中包含一个字符集)

比如通配符表达式“[bt]%”匹配第一个字符为b 或者t、长度不限的
字符串

SELECT * FROM T_Employee
WHERE FName LIKE '[SJ]%'

还可以使用否定符“^”来对集合取反,它匹配不与字符集中任意一个字符相匹配的字符。
比如通配符表达式“[^bt]%”匹配第一个字符不为b 或者t、长度不限的字符串

注意:

集合匹配只在SQLServer 上提供支持MySQL不支持!),不过在其他数据库中我们可以通过变通手段来实现相同的效果,比如:

SELECT * FROM T_Employee
WHERE FName LIKE 'S%' OR FName LIKE 'J%'

SELECT * FROM T_Employee
WHERE NOT(FName LIKE 'S%') AND NOT(FName LIKE 'J%')

在使用通配符过滤进行检索的时候,数据库系统会对全表进行扫描,所以执行速度非常慢。因此在使用其他方式可以实现的效果的时候就应该避免使用通配符过滤。

不要滥用通配符,通配符位于开头处匹配会非常慢

空值检测:

SELECT * FROM T_Employee  WHERE FNAME IS NULL

注意:

如果要检测“字段不为空”,则要使用IS NOT NULL;

反义运算符:

“!”运算符能够把“不等于”、“不大于”、“不小于”这样的语义直接翻译成SQL运算符,不过这个运算符只在SQLServer和DB2两种数据库系统上提供支持!也就是“!=”表示“不等于”、“!<”表示“不小于”,而“!>”表示“不大于”。

SQL提供了通用的表示“不等于”的运算符“<>”,这样“不等于”、“不大于”和“不小于”就分别可以表示成“<>”、“<=”和“>=”

其实经过试验:!>,!<MySQL不支持;<=,>=MySQL才支持;其他操作符MySQL都可以支持!

多值检测:(使用IN操作符来代替)

SELECT FAge,FNumber,FName FROM T_Employee
WHERE FAge=21 OR FAge=22 OR FAge=25
OR FAge=28 OR FAge=30 OR FAge=33
OR FAge=35 OR FAge=38 OR FAge=46

SELECT FAge,FNumber,FName FROM T_Employee
WHERE FAge IN (23,25,28)

范围值检测:(IN , AND , BETWEEN  AND关键字都可以来实现)

SELECT * FROM T_Employee
WHERE FAGE IN(23,24,25,26,27)

SELECT * FROM T_Employee
WHERE FAGE>=23 AND FAGE <=27

SELECT * FROM T_Employee
WHERE FAGE BETWEEN 23 AND 27

注意:

“字段名 BETTWEEN 左范围值  AND 右范围值”(闭区间),其等价于 “字段名>=左范围值  AND 字段名<=右范围值”

数据库系统对“BETTWEEN AND”进行了查询优化,使用它进行范围值检测将会得到比其他方式更好的性能,考虑优先使用它!

低效的 WHERE 1=1:(增加过滤条件,无法使用索引优化等策略)

因为使用添加了“1=1”的过滤条件以后数据库系统就无法使用索引等查询优化策略数据库系统将会被迫对每行数据进行扫描(也就是全表扫描)以比较此行是否满足过滤条件,当表中数据量比较大的时候查询速度会非常慢。

AND OR :用于连接多个过滤条件。优先处理 AND,因此当一个过滤表达式涉及到多个 AND 和 OR 时,应当使用 () 来决定优先级

IN 操作符:用于匹配一组值,其后也可以接一个 SELECT 子句,从而匹配子查询得到的一组值。

NOT 操作符:用于否定一个条件

3.数据分组(group by)

数据分组入门:数据分组用来将数据分为多个逻辑组,从而可以对每个组进行聚合运算;分组语句必须和聚合函数一起使用,GROUP BY子句负责将数据分成逻辑组,而聚合函数则对每一个组进行统计计算

SELECT FAge,COUNT(*) AS CountOfThisAge FROM T_Employee
GROUP BY FAge

#一般我们会进行排序

SELECT FSubCompany,FAge,COUNT(*) AS CountOfThisSubCompAge FROM
T_Employee
GROUP BY FSubCompany,FAge
ORDER BY FSubCompany

注意:

GROUP BY子句的位置,GROUP BY子句必须放到SELECT语句的之后,如果SELECT语句有WHERE子句,则GROUP BY子句必须放到WHERE语句的之后。(因为where不能对组进行操作,只能对数据进行操作!)

没有出现在GROUP BY子句中的列(聚合函数除外)是不能放到SELECT语句后的列名列表中的。

GROUP BY子句中可以指定多个列,只需要将多个列的列名用逗号隔开即可。指定多个分组规则以后,数据库系统将按照定义的分组顺序来对数据进行逐层分组,首先按照第一个分组列进行分组,然后在每个小组内按照第二个分组列进行再次分组……逐层分组。

在注意count(*),是对整个组进行统计只要有一列不为空就进行计数,而且可以对单列计数只要为空就不计数嘛!

HAVING子句:(需要对组进行筛选的时候,处理组结构

SELECT FAge,COUNT(*) AS CountOfThisAge FROM T_Employee
GROUP BY FAge
HAVING COUNT(*)>1

注意:

只要是where可以使用的筛选结构,having一样可以使用。

having子句只能放在group by子句之后,where子句只能放在group by子句之前。(having是处理组,而where是处理数据

4.显示结果集行数(limit关键字)

“LIMIT   首行行号,要返回的结果集的最大数目”(从0开始索引的!

SELECT * FROM T_Employee ORDER BY FSalary DESC LIMIT 2,5;

注意:

SQLServer2000中提供了TOP关键字用来返回结果集中的前N条记录,其语法为“SELECT TOP 限制结果集数目字段列表

5.抑制数据重复(去重复

SELECT DISTINCT FDepartment FROM T_Employee

6.计算字段

常量字段:

SELECT 'CowNew集团',918000000,FName,FAge,FSubCompany FROM T_Employee

字段间计算:

SELECT FNumber,FName,FAge * FSalary FROM T_Employee  #计算后返回的列名为空

SELECT FNumber,FName,FAge * FSalary AS FSalaryIndex FROM T_Employee #需要指定列名

注意:

加减乘除符号,以及小括号都可以使用

数据处理函数:(length,substring)

SELECT FName, LENGTH(FName) AS namelength FROM T_Employee
WHERE FName IS NOT NULL

substring:第一个参数为要取的主字符串,第二个参数为字串的起始位置(从1开始计数),第三个参数为
字串的长度。

SELECT FName, SUBSTRING(FName,2,3) FROM T_Employee
WHERE FName IS NOT NULL

sin,abs等函数。

字符串的拼接:(cncat,concat_ws)

SELECT CONCAT('工号为:',FNumber,'的员工的幸福指数:',FSalary/(FAge-21))
FROM T_Employee

CONCAT_WS可以在待拼接的字符串之间加入指定的分隔符,它的第一个参数值为采用的分隔符,而剩下的参数则为待拼接的字符串值

SELECT CONCAT_WS(',',FNumber,FAge,FDepartment,FSalary) FROM T_Employee

注意:SQLServer可以直接使用“+”号来拼接字符串。

7.计算字段的其他应用

SELECT * FROM T_Employee
WHERE Fsalary BETWEEN Fage*1.5+2000 AND Fage*1.8+5000

#年龄全部加1
UPDATE T_Employee SET FAge=FAge+1

#在不带FROM子句的SELECT语句中使用函数
SELECT LENGTH('abc')

8.联合结果集

联合基础:

可以使用UNION运算符来将两个或者多个查询结果集联合为一个结果集中。

UNION运算符要放置在两个查询语句之间。

SELECT FNumber,FName,FAge FROM T_Employee   #连接两个结果集
UNION
SELECT FIdCardNumber,FName,FAge FROM T_TempEmployee


SELECT FNumber,FName,FAge FROM T_Employee   #连接三个结果集
WHERE FAge<30
UNION
SELECT FIdCardNumber,FName,FAge FROM T_TempEmployee
WHERE FAge>40
UNION
SELECT FIdCardNumber,FName,FAge FROM T_TempEmployee
WHERE FAge<30

注意规则:

每个结果集必须有相同的列数,两个不同列数的结果集是不能联合在一起的

每个结果集的列必须类型相容,也就是说结果集的每个对应列的数据类型必须相同或者能够转换为同一种数据类型

MYSQL支持进行默认的数据类型转换!而SQLServer、Oracle、DB2不会进行转换!

UNION ALL:在联合结果集中返回所有的记录而不管它们是否唯一,会出现重复!而UNION是会天然的排序在去掉重复

联合结果集应用举例:

SELECT '正式员工最高年龄',MAX(FAge) FROM T_Employee  #员工年龄报表
UNION
SELECT '正式员工最低年龄',MIN(FAge) FROM T_Employee
UNION
SELECT '临时工最高年龄',MAX(FAge) FROM T_TempEmployee
UNION
SELECT '临时工最低年龄',MIN(FAge) FROM T_TempEmployee


SELECT FNumber,FSalary FROM T_Employee    #工资报表
UNION
SELECT '工资合计',SUM(FSalary) FROM T_Employee

                          
SELECT 1,1 * 1   #打印5以内自然数的平方要求打印出打印5以内自然数以及它们的平方数 
UNION
SELECT 2,2 * 2
UNION
SELECT 3,3 * 3
UNION
SELECT 4,4 * 4
UNION
SELECT 5,5 * 5

..............................

9.WITH子句

给select子查询定义一个别名。达到一次定义多次使用

WITH person_tom AS
(
SELECT * FROM T_Person
WHERE FName='TOM'
)
SELECT * FROM T_Person
WHERE FAge=person_tom.FAge
OR FSalary=person_tom.FSalary

也可在子查询中的列定义别名

WITH person_tom(F1,F2,F3) AS
(
    SELECT FAge,FName,FSalary FROM T_Person
    WHERE FName='TOM'
)
SELECT * FROM T_Person
WHERE FAge=person_tom.F1
OR FSalary=person_tom.F3

10.视图

视图是虚拟的表,本身不包含数据就是一个spl语句

视图可以使用临时表进行实现,也可以使用合并sql进行实现。

视图具有如下好处:

  1. 简化复杂的 SQL 操作,比如复杂的联结(UNION,表连接,子查询);

  2. 通过只给用户访问视图的权限,保证数据的安全性

@临时表实现
CREATE VIEW myview AS
SELECT Concat(col1, col2) AS concat_col, col3*col4 AS count_col
FROM mytable
WHERE col5 = val;

@使用合并过滤实现
select Code, Name from Country
where Continent = ‘Oceania’ and Name = ‘Australia’;

第五章:函数(仅限MySQL)

1.数学函数

SELECT FWeight - 50,ABS(FWeight - 50) , ABS(-5.38) FROM T_Person #求绝对值

求指数:第一个参数为待求幂的表达式,第二个参数为幂

SELECT FWeight,POWER(FWeight,-0.5),POWER(FWeight,2),  
POWER(FWeight,3),POWER(FWeight,4) FROM T_Person; 
SELECT FWeight,SQRT(FWeight) FROM T_Person  #求平方根
SELECT RAND()   #RAND()函数用来生成随机算法
SELECT FName,FWeight, CEILING(FWeight), CEILING(FWeight*-1)  #向上取整
FROM T_Person

SELECT FName,FWeight,FLOOR(FWeight),FLOOR(FWeight*-1)  #向下取整
FROM T_Person

四舍五入:

m为待进行四舍五入的数值,而d则为计算精度,也就是进行四舍五入时保留的小数位数

d为0时,就是取整,没有小数位嘛!

SELECT FName,FWeight, ROUND(FWeight,1), 
ROUND(FWeight*-1,0) , ROUND(FWeight,-1) 
FROM T_Person;   
SELECT FName,FWeight,SIN(FWeight) FROM T_Person  #求正弦值

SELECT FName,FWeight, COS(FWeight) FROM T_Person #余弦值

SELECT FName,FWeight, ASIN(1/FWeight) FROM T_Person  #反正弦

SELECT FName,FWeight, ACOS(1/FWeight) FROM T_Person #反余弦

SELECT FName,FWeight, TAN(FWeight) FROM T_Person #正切

SELECT FName,FWeight, ATAN(FWeight) FROM T_Person #反正切

SELECT FName,FWeight,FWeight *PI() FROM T_Person #求圆周率
SELECT FName,FWeight-48.68,SIGN(FWeight-48.68) FROM T_Person #求符号 -1是负数 0是零 1是正数
SELECT FName,FWeight,MOD(FWeight , 5) FROM T_Person #求余数
SELECT FName,FWeight, LOG(FWeight) FROM T_Person #求自然对数

SELECT FName,FWeight, LOG10(FWeight) FROM T_Person  #求以10为底的对数
select RAND()  #随机数

2.字符串函数

SELECT FName, LENGTH(FName) FROM T_Person   求字符串长度
SELECT FName, LOWER(FName) FROM T_Person #转化为小写

SELECT FName, UPPER(FName) FROM T_Person #转化为大写
SELECT FName,LTRIM(FName),LTRIM(' abc ') FROM T_Person #去掉左空格

SELECT FName,RTRIM(FName),RTRIM(' abc ') FROM T_Person #去掉右空格

SELECT FName,TRIM(FName),TRIM(' abc ') FROM T_Person #去掉左右空格
SELECT SUBSTRING('abcdef111',2,3) #从(默认从1开始)下标2开始,截取3个字符

判断子串在主串中第一次出现的位置,不存在返回零。

SELECT FName, INSTR(FName,'m') , INSTR(FName,'ly')
FROM T_Person
SELECT FName, LEFT(FName,3) , LEFT(FName,2) #从左侧开始取字符串
FROM T_Person 

SELECT FName, RIGHT(FName,3) , RIGHT(FName,2) #从右侧开始取字符串
FROM T_Person
select FName,REPLACE(FName,'i','e'),FIDNumber,  #字符串替换
REPLACE(FIDNumber,'2345','abcd') FROM T_Person 

返回ASCII码,如果参数是一个字符串返回第一个字符的码值。

SELECT ASCII('a') , ASCII('abc')

返回ASCII码对应的字符

SELECT CHAR(56) , CHAR(90) ,'a', CHAR( ASCII('a') )

发音相似度:

下面的SQL语句用于查询和“Tim”发音相似度大于3 的员工

SELECT * FROM T_Person
WHERE DIFFERENCE(FName,'Tim')>=3

3.日期时间函数

日期类型的保存:

在 MYSQL、MSSQLServer 和DB2 中可以用字符串来表示日期时间类型,数据库系统会自动在内部将它们转换为日期时间类型,比如“'2008-08-08'”、“2008-08-08 08:00:00”、“08:00:00” 、“2008-08-08 08:00:00.000000”等。

取得当前日期:

NOW()函数还有SYSDATE()、CURRENT_TIMESTAMP等别名

SELECT NOW(),SYSDATE(),CURRENT_TIMESTAMP  #2008-01-12 01:13:19

 CURDATE(),CURRENT_DATE是别名

SELECT CURDATE(),CURRENT_DATE   #2008-01-12

CURTIME ()函数还有CURRENT_TIME等别名

SELECT CURTIME(),CURRENT_TIME #01:17:09

日期的增减:

增加一周,增加两个月,增加五个季度:

SELECT FBirthDay,
DATE_ADD(FBirthDay,INTERVAL 1 WEEK) as w1,
DATE_ADD(FBirthDay,INTERVAL 2 MONTH) as m2,
DATE_ADD(FBirthDay,INTERVAL 5 QUARTER) as q5
FROM T_Person

加3天2小时10分钟、1年6个月的日期时间:(格式:Y-Y-Y H-M-S)

SELECT FBirthDay,
DATE_ADD(FBirthDay,INTERVAL '3 2:10' DAY_MINUTE) as dm,
DATE_ADD(FBirthDay,INTERVAL '1-6' YEAR_MONTH) as ym
FROM T_Person

嵌套实现增加1年6个月:

SELECT FBirthDay,
DATE_ADD(DATE_ADD(FBirthDay,INTERVAL 1 YEAR),INTERVAL 6 MONTH) as dm
FROM T_Person

减少一周,两个月,5个季度:(简单的变为负数就行了

SELECT FBirthDay,
DATE_ADD(FBirthDay,INTERVAL -1 WEEK) as w1,
DATE_ADD(FBirthDay,INTERVAL -2 MONTH) as m2,
DATE_ADD(FBirthDay,INTERVAL -5 QUARTER) as q5
FROM T_Person

使用函数来进行减少:(DATA_SUB

SELECT FBirthDay,
DATE_SUB(FBirthDay,INTERVAL 1 WEEK) as w1,
DATE_SUB(FBirthDay,INTERVAL 2 MONTH) as m2,
DATE_SUB(FBirthDay, INTERVAL '3 2:10' DAY_MINUTE) as dm
FROM T_Person

计算日期差额:

DATEDIFF(date1,date2)函数将返回date1与date2之间的天数差额如果date2在date1之后返回正值否则返回负值

SELECT FRegDay,FBirthDay, DATEDIFF(FRegDay, FBirthDay) ,
DATEDIFF(FBirthDay ,FRegDay)
FROM T_Person

计算一个日期是星期几:
 

SELECT FBirthDay,DAYNAME(FBirthDay),
FRegDay,DAYNAME(FRegDay)
FROM T_Person

日期格式化:

SELECT
FBirthDay,
DATE_FORMAT(FBirthDay,'%y-%M %D %W') AS bd,
FRegDay,
DATE_FORMAT(FRegDay,'%Y年%m月%e日') AS rd
FROM T_Person

4.类型转换

CAST(expression AS type)和CONVERT(expression,type)函数!

SELECT
CAST('-30' AS SIGNED) as sig,
CONVERT ('36', UNSIGNED INTEGER) as usig,
CAST('2008-08-08' AS DATE) as d,
CONVERT ('08:09:10', TIME) as t

5.空值处理

COALESCE ( expression,value1,value2……,valuen)

COALESCE()函数将会返回包括expression在内的所有参数中的第一个非空表达式。

SELECT FName,FBirthDay,FRegDay,
COALESCE(FBirthDay,FRegDay,'2008-08-08') AS ImportDay
FROM T_Person

IFNULL简化版,只接受两个变量

SELECT FBirthDay,FRegDay,
IFNULL(FBirthDay,FRegDay) AS ImportDay
FROM T_Person

流程控制函数CAST

SELECT
    FName,
    (CASE FName
    WHEN 'Tom' THEN 'GoodBoy'
    WHEN 'Lily' THEN 'GoodGirl'
    WHEN 'Sam' THEN 'BadBoy'
    WHEN 'Kerry' THEN 'BadGirl'
    ELSE 'Normal'
    END) as isgood
FROM T_Person
SELECT
    FName,
    FWeight,
    (CASE
    WHEN FWeight<40 THEN 'thin'
    WHEN FWeight>50 THEN 'fat'
    ELSE 'ok'
    END) as isnormal
FROM T_Person

以上函数在各大数据库,都有实现只是函数名和一些用法,有些不同!

6.MySQL独有的函数

IF函数,需要嵌套实现多判别,否则只是二判别

SELECT
FName,
FWeight,
IF(FWeight>50,'太胖','正常') AS ISTooFat
FROM T_Person

CONV(N,from_base,to_base)函数:

将数字N 从from_base进制转换到to_base进制,并以字符串表示形式返回

SELECT CONV('26',10,2), CONV(26,10,2),CONV('7D',16,8)
SELECT FWeight,Round(FWeight),
BIN(Round(FWeight)) as b,   #转化为二进制
OCT(Round(FWeight)) as o,   #转化为八进制
HEX(Round(FWeight)) as h    #转化为十六进制
FROM T_Person

REPEAT(str,count)函数:
参数str为子字符串,而count为重复次数。

SELECT REPEAT('*',5), REPEAT('OK',3)
SELECT FName, REVERSE(FName)  #字符串反转
FROM T_Person

第六章:索引与约束

1.索引(普通索引!详细请看进阶篇!)

创建索引:

CREATE INDEX idx_person_nameage ON T_Person(FName,FAge)

删除索引:

DROP INDEX idx_person_nameage ON T_Person;

2.约束

非空约束:

CREATE TABLE T_Person (FNumber VARCHAR(20) NOT NULL ,FName
VARCHAR(20),FAge INT)

唯一约束:

CREATE TABLE T_Person (FNumber VARCHAR(20) UNIQUE,
FName VARCHAR(20),FAge INT)

复合唯一约束:

建立在多个字段上的约束,被约束的字段不能同时重复

:将在部门编号字段FDepartmentNumber 和工号字段FNumber 上设置复合唯一约束,并且命名为unic_dep_num

:该命名字段不会出现在表中,但是可以进行删除!

CREATE TABLE T_Person (FNumber VARCHAR(20),
FDepartmentNumber VARCHAR(20),
FName VARCHAR(20),FAge INT,
CONSTRAINT unic_dep_num UNIQUE(FNumber,FDepartmentNumber))

:通过这种形式定义的复合唯一约束由于有一个确定的名称,所以可以很容易的通过这个名字来删除这个约束。

注意:唯一键约束添加后,实际上建立了一个索引,将该索引删除后,就等于删除了联合唯一约束。

MYSQL:
alter table T_Person drop index unic_dep_num;

MSQLServer、Oracle、DB2:
alter  table T_Person drop constraint unic_dep_num;

添加唯一约束:

ALTER TABLE T_Person ADD CONSTRAINT unic_3 UNIQUE(FName, FAge)
MYSQL:
alter table T_Person drop index unic_3;

MSQLServer、Oracle、DB2:
alter  table T_Person drop constraint unic_3;

 CHECK 约束:

CREATE TABLE T_Person (
FNumber VARCHAR(20),FName VARCHAR(20) CHECK(LENGTH(FNumber)>12),
FAge INT CHECK(FAge >0),
FWorkYear INT CHECK(FWorkYear>0))

缺点:约束条件不能引用其他列。必须要CONSTRAINT关键字来进行约束才可!

CREATE TABLE T_Person (
FNumber VARCHAR(20),
FName VARCHAR(20),
FAge INT,
FWorkYear INT,
CONSTRAINT ck_1 CHECK(FWorkYear< FAge))

主键约束:

第一范式要求每张表都要有主键,每个表中却只能有一个主键约束。主键约束是外键关联的基础条件。

CREATE TABLE T_Person (FNumber VARCHAR(20) PRIMARY KEY,
FName VARCHAR(20),FAge INT)

CREATE TABLE T_Person (FNumber VARCHAR(20),
FName VARCHAR(20),FAge INT,primary key(FNumber));

还可以由多个字段来组成主键:(复合主键/联合主键

CREATE TABLE T_Person (FNumber VARCHAR(20),
FName VARCHAR(20),FAge INT,
CONSTRAINT pk_1 PRIMARY KEY(FNumber,FName))

如果要在创建表之后,创建主键:(注意:先确定列是非空,然后在创建)

ALTER TABLE T_Person
ADD CONSTRAINT pk_1 PRIMARY KEY(FNumber,FName)

删除主键:

ALTER TABLE T_Person  #只在MySQL里面有效!
DROP PRIMARY KEY; 

外键约束:

CREATE TABLE T_AUTHOR
(
FId VARCHAR(20) PRIMARY KEY,  #主键
FName VARCHAR(100),
FAge INT,
FEmail VARCHAR(20)
);

CREATE TABLE T_Book
(
FId VARCHAR(20) PRIMARY KEY,
FName VARCHAR(100),
FPageCount INT,
FAuthorId VARCHAR(20) ,
FOREIGN KEY (FAuthorId) REFERENCES T_AUTHOR(FId)  #外键进行关联
);

注意:

主键没有出现的元素,外键不能插入

删除表时,先要删除引用的外键表,再删除主键表

添加外键约束:

ALTER TABLE T_Book
ADD CONSTRAINT fk_book_author
FOREIGN KEY (FAuthorId) REFERENCES T_AUTHOR(FId)

3.业务主键和逻辑主键

业务主键,意义表达比较明确,但是可能唯一性不能保证,维护的时候比较困难(业务性比较强,改动会比较大,耦合性太强)。一般更倾向于使用没有意义的自动增长字段当成逻辑主键。

第七章:表连接

创建表的语句:

CREATE TABLE T_Customer (FId INT NOT NULL ,FName VARCHAR(20) NOT NULL ,
FAge INT,PRIMARY KEY (FId));

CREATE TABLE T_Order (FId INT NOT NULL ,FNumber VARCHAR(20) NOT NULL ,
FPrice DECIMAL(10,2),FCustomerId INT,FTypeId INT,PRIMARY KEY (FId))

交叉连接(CROSS JOIN):

交叉连接会将涉及到的所有表中的所有记录都包含在结果集中,返回被连接的两个表所有数据行的笛卡尔积

隐式定义:

SELECT c.FId, c.FName, c.FAge,
o.FId, o.FNumber, o.FPrice
FROM T_Customer c, T_Order o

显示定义:

SELECT T_Customer.FId, T_Customer.FName, T_Customer.FAge,
T_Order.FId, T_Order.FNumber, T_Order.FPrice
FROM T_Customer
CROSS JOIN T_Order

内连接(INNER JOIN):

定义:内连接组合两张表,并且基于两张表中的关联关系来连接它们。使用内连接需要指定基于什么条件进行连接

缺陷:空值也有时候可能就检索不到结果集当中!

被关联的表名:T_Customer;   进行连接时的条件:FCustomerId= T_Customer.FId

SELECT FNumber,FPrice
FROM T_Order INNER JOIN T_Customer
ON FCustomerId= T_Customer.FId
WHERE T_Customer.FName='TOM'

加上列名的域,增加可读性(可加上别名):

SELECT o.FId,o.FNumber,o.FPrice,
c.FId,c.FName,c .FAge
FROM T_Order as o JOIN T_Customer as c
ON o.FCustomerId= c.FId

以上在内连接里的等号条件:FCustomerId= T_Customer.FId称为等值连接

不等值连接:

SELECT T_Order.FNumber,T_Order.FPrice,
T_Customer.FName,T_Customer.FAge
FROM T_Order
INNER JOIN T_Customer
ON T_Order.FPrice< T_Customer.FAge*5
and T_Order.FCustomerId=T_Customer.FId

注意:

不等值连接是会产生大量的查询结果,因为它是做了笛卡尔积运算,所以一般后面需要加上等值约束!

外连接(OUTTER JOIN):

外部连接主要就是用来解决这种空值匹配问题的。外部连接的语法与内部连接几乎是一样的,主要区别就是对于空值的处理

定义:可以保证某个表中的记录总是完整的出现在结果集中,然后在根据这个表进行连接的过程!

注意:这里的左表和右表是相对于JOIN关键字来说的,位于JOIN关键字左侧的表即被称为左表,而位于JOIN关键字右侧的表即被称为右表

左外部连接(RIGHT OUTER JOIN):

左表中所有的记录都会被放到结果集中,无论是否在右表中存在匹配记录

SELECT o.FNumber,o.FPrice,o.FCustomerId,
c.FName,c.FAge
FROM T_Order as o
LEFT OUTER JOIN T_Customer as c
ON o.FCustomerId=c.FId

右外部连接(LEFT OUTER JOIN):

SELECT o.FNumber,o.FPrice,o.FCustomerId,
c.FName,c.FAge
FROM T_Order as o
RIGHT OUTER JOIN T_Customer as c
ON o.FCustomerId=c.FId

全外部连接(FULLOUTER JOIN):

最常使用的MYSQL就不支持全外部连接!

SELECT o.FNumber,o.FPrice,o.FCustomerId, 
c.FName,c.FAge
FROM T_Order o
FULL OUTER JOIN T_Customer c
ON o.FCustomerId=c.FId

可以使用左外部连接和右外部连接来模拟实现全外部连接:使用左外部连接和右外部连接分别进行匹配查询,然后使用UNION运算符来取两个查询结果集的合集

SELECT o.FNumber,o.FPrice,o.FCustomerId,
c.FName,c.FAge
FROM T_Order as o
LEFT OUTER JOIN T_Customer as c
ON o.FCustomerId=c.FId
UNION
SELECT o.FNumber,o.FPrice,o.FCustomerId,
c.FName,c.FAge
FROM T_Order as o
RIGHT OUTER JOIN T_Customer as c
ON o.FCustomerId=c.FId

自连接(SELF JOIN):

只要参与连接的表是同一张表(使用内连接来实现),那么它们就可以被称为自连接。

找出相同类型ID的不同用户

SELECT o1.FNumber,o1.FPrice,o1.FTypeId,
o2.FNumber,o2.FPrice,o2.FTypeId
FROM T_Order o1
INNER JOIN T_Order o2
ON o1.FTypeId=o2.FTypeId and o1.FId<>o2.FId

要注意:笛卡尔积啊,是个双向的过程,是带顺序的A->B和B->A他认为是不同的,但是这是相同的!

修改约束条件:

SELECT o1.FNumber,o1.FPrice,o1.FTypeId,
o2.FNumber,o2.FPrice,o2.FTypeId
FROM T_Order o1
INNER JOIN T_Order o2
ON o1.FTypeId=o2.FTypeId and o1.FId<o2.FId

自然连接(natural inner):

找到两个表的同名同属性的列,以此为基准进行连接。(并且去掉合并后的重复列

 `SELECT*FROM student NATURAL JOIN score;`

第八章:子查询

定义:被当作结果集的查询语句就称为子查询。SQL允许讲一个查询语句当做一个结果集给其他SQL语句来使用!

1.子查询入门

select所选列的子查询:

注意:子查询只能返回一条记录,否则会报错,因为其他两列只有一行记录!

SELECT 1 AS f1,2,(SELECT MIN(FYearPublished) FROM T_Book),(SELECT
MAX(FYearPublished) FROM T_Book) AS f4

这样可以正常进行的:

SELECT FId,FName,
(
SELECTMAX(FYearPublished)
FROM T_Book
)
FROM T_Category

where子句中的子查询:假设子查询只返回一行才可以!!

SELECT FReaderId FROM T_ReaderFavorite
WHERE FCategoryId=
(
SELECT FId FROM T_Category
WHERE FName='Story'
)

from子句的子查询:

SELECT T_Reader.FName,t2.FYear,t2.FName ,t2.F3
FROM T_Reader,
(SELECT FYearPublished AS FYear,FName,1+2 as F3 FROM T_Book WHERE
FYearPublished < 1800) as t2

查询每种书籍类型中的最早出版的书籍,可以使用子查询+表连接来实现:

先进行表的内连接(书的id号需要对应),然后进行子查询在每类书中选出最小价格的(外键关联主键的约束下进行

SELECT T_Category.FId, T_Book. FName,T_Book.FYearPublished
FROM T_Category
INNER JOIN T_Book ON T_Category.FId=T_Book.FCategoryId
WHERE T_Book.FYearPublished=
(
SELECT MIN(T_Book.FYearPublished)
FROM T_Book
WHERE T_Book.FCategoryId=T_Category.FId
)

2.集合运算符与子查询

IN运算符:(子查询的结果是多行多列就必须要用IN运算符了)

SELECT * FROM T_Reader
WHERE FYearOfJoin IN
(
select FYearPublished FROM T_Book
)

ANY运算符:(“= ANY” 等价于 IN运算符,就是只要在子查询集合中出现过即可!)

SELECT * FROM T_Book
WHERE FYearPublished<ANY
(
SELECT FYearOfBirth FROM T_Reader
)

SELECT * FROM T_Reader
WHERE FYearOfJoin =ANY
(
select FYearPublished FROM T_Book
)

注意:“<>ANY”则等价于NOT IN 运算符;ANY和SOME运算符一样

只要满足条件中的一个即可

ALL运算符:

需要满足所有的条件!

SELECT * FROM T_Book
WHERE FYearPublished<ALL
(
SELECT FYearOfJoin FROM T_Reader
)

EXISTS运算符:

定义:EXISTS就是用来测试子查询的结果是否为空,如果结果集为空则匹配结果为false,否则匹配结果为true。

SELECT * FROM T_Book
WHERE EXISTS
(
SELECT * FROM T_Reader
WHERE FProvince='ShanDong'
)

一般EXISTS和相关子查询在一起使用。在相关子查询中引用外部查询中的字段。

SELECT * FROM T_Category
WHERE EXISTS
(
SELECT * FROM T_Book
WHERE T_Book. FCategoryId = T_Category.FId
AND T_Book. FYearPublished<1950
)

在EXISTS后的子查询中,SQL对T_Category表中的每一行数据到子查询中进行匹配,测试T_Book 表中是否存在FCategoryId 字段值等于当前类别主键值出版年份在1950 年之前的书籍。

3.子查询在其他类型SQL语句的应用

T_ReaderFavorite中的输入复制插入到T_ReaderFavorite2表,SQL语句如下:

INSERT INTO T_ReaderFavorite2(FCategoryId,FReaderId)
SELECT FCategoryId,FReaderId FROM T_ReaderFavorite
INSERT INTO T_ReaderFavorite2(FCategoryId,FReaderId)
SELECT FCategoryId,
(CASE
WHEN FReaderId<=10 THEN FReaderId
ELSE FReaderId- FCategoryId
END
)
FROM T_ReaderFavorite

在UPDATE上的使用:

UPDATE T_Book as b1
SET b1.FYearPublished=2005
WHERE
(
SELECT COUNT(*) FROM T_Book b2
WHERE b1. FCategoryId=b2. FCategoryId
)>3

在DELETE上的使用:

DELETE FROM T_Book as b1
WHERE
(
SELECT COUNT(*) FROM T_Book b2
WHERE b1. FCategoryId=b2. FCategoryId
)>3

第九章:SQL的一些优化

SQL注入定义:就是通过把SQL命令插入到Web 表单 提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令 !

SQL注入预防:在设计与数据库链接并访问数据时,在需要填入数值或数据的地方,使用参数 (Parameter) 来给值

SQL内置的查询优化器可以自动识别查询条件是否可以使用索引,来进行优化!

1.创建必要的索引

在经常需要进行检索的字段上创建索引。创建索引给检索带来的性能提升往往是巨大的,因此在发现检索速度过慢的时候应该首先想到的就是创建索引。

2.使用预编译查询

应该尽量使用参数化SQL,这样不仅可以避免SQL注入漏洞攻击,最重要数据库会对这些参数化SQL执行预编译,这样第一次执行的时候DBMS会为这个SQL语句进行查询优化并且执行预编译,这样以后再执行这个SQL 的时候就直接使用预编译的结果

3.调整WHERE 子句中的连接顺序

SELECT *
FROM T_Person
WHERE FSalary > 50000
AND FPosition= ‘MANAGER’
AND 25 < (SELECT COUNT(*) FROM T_Manager
WHERE FManagerId=2);

我们将子查询的条件放到最前面,可以过滤掉最大数量记录!

SELECT *
FROM T_Person
WHERE
25 < (SELECT COUNT(*) FROM T_Manager
WHERE FManagerId=2)
AND FSalary > 50000
AND FPosition= ‘MANAGER’ ;

4.尽量将多条SQL语句压缩到一句SQL

每次执行SQL的时候都要建立网络连接进行权限校验进行SQL语句的查询优化、发送执行结果,这个过程是非常耗时的,因此应该尽量避免过多的执行SQL语句。

5.用EXISTS替代IN

因为IN 子句将执行一个子查询内部的排序和合并

6.用表连接替换EXISTS

表连接的方式比EXISTS 更有效率。

语句 1:
SELECT FName FROM T_Employee
WHERE EXISTS
(
SELECT 1 FROM T_Department
WHERE T_Employee.FDepartNo= FNumber
AND FKind='A'
);
语句 2:交叉连接
SELECT FName FROM T_Department, T_Employee
WHERE T_Employee. FDepartNo = T_Departmen. FNumber
AND FKind = ‘A’ ;

7.用Where子句替换HAVING 子句

HAVING 中的条件一般用于聚合函数的过滤,除此而外,应该将条件写在WHERE 子句中。

因为HAVING 只会在检索出所有记录之后才对结果集进行过滤。如果能通过WHERE 子句限制记录的数目,那就能减少这方面的开销

8.避免在索引列上使用计算

如果在索引列上计算那么,DBMS将不会使用索引而使用全表

9.用UNION ALL 替换UNION

因为UNION会在合并前排序去除重复元素,如果可以确定没有重复元素就直接使用UNION ALL即可!

10.避免隐式类型转换造成的全表扫描

11.拆分大的delete或insert语句

如果一次性查询再有并发的情况下,可能一次性锁住很多数据阻塞很多小的查询事务,耗费系统资源!

12. EXPLAIN 你的 SELECT 查询

可以让你知道MySQL是如何处理你的SQL语句的。这可以帮你分析你的查询语句或是表结构的性能瓶颈。

13.当确定只要一行数据时使用 LIMIT 1

14.千万不要 ORDER BY RAND()  会先排序再去打乱

15.永远为每张表设置一个ID

出于逻辑主键,InnoDB引擎都应该这样设置!

我们应该为数据库里的每张表都设置一个ID做为其主键,而且最好的是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的AUTO_INCREMENT标志。

http://www.jfox.info/archives/652.html好文一生推!

第十章:事务
 

好文一生推!

定义:就是数据库操作的一个逻辑工作单位。

四个基本特性原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。简称ACID 特性

原子性:一个事务中所有的数据库操作,是一个不可分割的整体,这些操作要么全部执行要么全部无效果

一致性:一个事务使数据库执行之前和执行之后都必须处于一致性状态。(比如:用户转账后,资金总额一样!)

隔离性:当多个用户并发访问数据库操作同一张表时,数据库会多线程并发访问,并且使各个事务之间互不影响。

持久性:一个事务一旦被提交那么就会永久的更新到数据库当中。

事务的原子性、一致性、持久性:

事务隔离性由锁实现,原子性、一致性和持久性由数据库的 redo log 和 undo log 实现。

redo log 称为重做日志,用来保证事务的原子性和持久性记录提交事务的操作。

undo log 来保证事务的一致性记录提交事务前的操作,undo 回滚行记录到某个特性版本及 MVCC 功能。

redo 是物理日志。

重做日志重做日志缓冲(redo log buffer)重做日志文件(redo log file)组成,前者是易失的,后者是持久的。

当事务提交时,日志缓冲不直接写入重做日志文件,而是等待一个事件周期后再执行 Fsync 操作,由于并非强制在事务提交时进行一次 Fsync 操作,显然这可以提高数据库性能。

请记住 3 点:

  • 重做日志是在 InnoDB 层产生的。

  • 重做日志是物理格式日志,记录的是对每个页的修改。

  • 重做日志在事务进行中不断被写入

undo 是逻辑日志

事务回滚和 MVCC,这就需要 undo。undo 是逻辑日志,只是将数据库逻辑恢复到原来的样子,但是数据结构和页本身在回滚之后可能不同。

事务的隔离性

使用了线程并发的机制来实现,当线程1和线程2对数据更新时,线程1取到数据即将更新然后线程切换,线程2取到数据并且进行修改然后切换线程,注意此时线程1修改数据是原数据而不是已经修改的,这时线程1不就把线程2修改的覆盖了嘛不是在线程2的基础上进行更新

解决此问题就通过了锁的机制来实现,锁分为:只读锁和写入锁。由于读数据不影响数据的更新问题所以只读锁可以多个并发事务都能获得锁;而写入锁由于数据更新问题,任一时刻只能有一个事务来获的写入锁

隔离性是通过加锁来实现的,所以会降低系统性能;所以事务提供了控制隔离程度的机制

事务的隔离级别

脏读:

读取了未提交的数据的方法就叫脏(dirty)读!一个线程回滚,一个线程读取到了未提交数据进行了正常更新,最后出现了错误的更新结果。(线程1更新数据,然后还没提交线程切换,线程2读取到了未提交的数据在进行更新,然后线程2切换到线程1然后进行了回滚,所以线程2的数据更新到数据库中是不被承认的!

不可重复读(指update或delete):

读取了已经提交过的数据的方法!跟脏读不一样,脏读是读取了还未提交的数据,而不可重复读是读取了已经提交过的数据!这是由于在两次查询间隔,被另一个事务修改并提交了。(在这个事务还没有结束时候,那不能查询结果不一样啊!

(只要在读取时是可以直接对这些数据加上锁禁止更新即可!)

幻影读取(指insert):

读取了已经提交过的数据的方法!跟不可重复读的区别就是,幻影读针对插入,不可重复读针对更新和删除操作!这是由于在查询两次的间隔里,数据被另一个事务修改并提交了。

(在读取的时候关键对insert的数据无法加锁,要解决该问题必须要对该表进行加锁才可!)

READ_UNCOMMITED(未提交读) :(事务进行了修改即使没有提交。对其他事务是透明的

导致脏读、不可重复读、幻影读问题。

READ_COMMITED (提交读):(事务只能读取到已经提交过的事务信息

可以解决脏读问题,但是解决不了不可重复读和幻影读的问题。

REPEATABLE_READ(可重复读):(保证在同一事务中多次读取的结果一致)(加记录锁)

解决了不可重复读的问题,但是无法解决幻影读新增加的数据。(给读取的原数据上加锁)

SERIALIZABLE(序列化读)(最严格的隔离级别):(强制事务串行执行

通过间隙读解决了幻影读

 能够避免 脏读 不可重复读 幻影读

总结:隔离级别越高,越能保证事务的隔离性,但对并发性能的影响也就越大。对于多数应用可以把隔离级别设置为ReadCommited,也就是授权读取,能够避免脏读,而保持较好的并发性能MySQL默认已经是repeatable read隔离级别,不同的数据库不同!

事务的使用

主流的 DBMS 都提供了启动提交以及回滚事务的机制,也提供了指定锁粒度隔离级别的机制

锁的概念:

行级锁:

  • 排它锁(Exclusive),简写为 X 锁,又称写锁。(不允许其他事务在获取锁

  • 共享锁(Shared),简写为 S 锁,又称读锁。(允许其他事务获取读锁

表级锁:

  • 意向共享锁(读锁 IS Lock),事务想要获取一张表的几行数据的共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁。

  • 意向排他锁(写锁 IX Lock),事务想要获取一张表中几行数据的排它锁,事务在给一个数据行加排它锁前必须先取得该表的 IX 锁。

仅从锁的角度来看,表级锁更适合以查询为主的应用场景,而行级锁则更适合于大量按索引条件并发更新少量数据的应用场景

表级锁有以下两个规定:

  1. 一个事务在获得某个数据行对象的 S 锁之前,必须先获得 IS 锁或者更强的锁;

  2. 一个事务在获得某个数据行对象的 X 锁之前,必须先获得 IX 锁。

乐观并发控制(锁):(就认为查数据不会加锁, 此数据被更新就会取消本次操作,实际上没有真正加上锁

认为每一次读数据都不会被修改就不会去加锁,在更新数据的时候会认为有人修改这个数据就会取消本次操作

假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。(别人思路!) 

悲观并发控制(锁):(只要是进行操作就会加上锁进行,实际加上了锁

认为每一次读数据操作都会被修改,所以在操作之前先上锁,也称为独占锁。

假定会发生并发冲突,加锁屏蔽一切可能违反数据完整性的操作。 (别人思路!

锁的算法:Record Lock(记录锁)、Gap Lock(间隙锁) 和 Next-Key Lock(记录锁和间隙锁的加强)。

  • Record Lock:单个行记录上的锁。主键、非空的唯一性索引对应的索引记录!(保证了有序

  • Gap Lock:间隙锁,锁定一个范围,而非记录本身。(保证了间隙)

  • Next-Key Lock:结合 Gap Lock 和 Record Lock,锁定一个范围(第一次读取到数据的范围+前后索引值范围),并且锁定记录本身。主要解决的问题是 RR 隔离级别下的幻读。

图来的真及时,真救命!好文一生推!https://www.jianshu.com/p/7d050498d9da

死锁:(两个线程都在同时等待对方释放锁的过程!

Mysql的内部架构:

第十一章:NULL的学问

定义:NULL在数据库中代表的是值未知,不是空值;任何检索都找不出NULL值除非使用查询:

SELECT * FROM T_Employee
WHERE FSalary<5000 OR FSalary IS NULL

NULL和计算字段:当字段是NULL不管进行任何运算都是NULL(代表未知),只能过滤掉NULL(未知)值/CAST语句

SELECT FId,FName, FSalary ,FSalary+2000
FROM T_Employee
WHERE FSalary IS NOT NULL
SELECT FId,FName, FSalary ,
(CASE
    WHEN FSalary IS NULL THEN 0
    ELSE FSalary
END
)+2000
FROM T_Employee

NULL和字符串:

如果NULL值出现在任何字符串相关计算字段中,那么计算结果永远是NULL

SELECT FId,FName,CONCAT(FName,'LOL'),FSalary
FROM T_Employee

NULL和函数:

如果NULL 值出现在普通函数中,那么计算结果永远是NULL。

SELECT FId,FName, FSalary ,ABS(FSalary-5000)
FROM T_Employee

NULL和聚合函数(MIN,MAX,SUM,COUNT):

和普通的函数不同,如果NULL值出现在聚合函数中,那么NULL值将会被忽略

SELECT MAX(FSalary) AS MAXSALARY,
MIN(FSalary) AS MINSALARY,
COUNT(FSalary)
FROM T_Employee

猜你喜欢

转载自blog.csdn.net/taka_is_beauty/article/details/89761288