事务的概念
例子
事务的特性(ACID)
并发控制
读写异常
封锁
事务的隔离级别
未提交读(Read Uncommitted)
提交读(Read Committed)
可重复读(Repeatable Read)
可序列化(Serializable)
封锁协议
并行调度
使用事务
总结
基本概念
索引的存储
索引的分类
聚集索引
非聚集索引
索引失效
索引设计的原则
总结
CRUD
CREATE TABLE
UPDATE
INSERT
DELETE
修改表结构
权限分配
模糊查询
Join
Inner Join
Left Join
Cross join
###主键与外键
主键与外键
数据库事务通常包含了一个序列的对数据库的读/写操作(一个单元的一系列SQL语句的集合)。包含有以下两个目的:
当事务被提交给了数据库管理系统(DataBaseManagerService,DBMS),则DBMS需要确保该事务中的所有操作都成功完成且其结果被永久保存在数据库中,如果事务中有的操作没有成功完成,则事务中的所有操作都需要回滚,回到事务执行前的状态;同时,该事务对数据库或者其他事务的执行无影响,所有的事务都好像在独立的运行。
某人要在商店使用电子货币购买100元的东西,当中至少包括两个操作:
支持事务的数据库管理系统(transactional DBMS)就是要确保以上两个操作(整个“事务”)都能完成,或一起取消;否则就会出现100元平白消失或出现的情况
并非任意的对数据库的操作序列都是数据库事务。数据库事务拥有以下四个特性,习惯上被称之为ACID特性。
A, atomacity 原子性 事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。通常,与某个事务关联的操作具有共同的目标,并且是相互依赖的。如果系统只执行这些操作的一个子集,则可能会破坏事务的总体目标。原子性消除了系统处理操作子集的可能性。
C, consistency 一致性
事务将数据库从一种一致状态转变为下一种一致状态。也就是说,事务在完成时,必须使所有的数据都保持一致状态(各种 constraint 不被破坏)。
I, isolation 隔离性 由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。换句话说,一个事务的影响在该事务提交前对其他事务都不可见。
D, durability 持久性
事务完成之后,它对于系统的影响是永久性的。该修改即使出现致命的系统故障也将一直保持。
原子性和一致性的区别:
转账:张三给李四转账100元。那数据库假设需要 张三扣100,李四加100,记录一条流水。 如果流水没记录成功,那整体回滚,张三也没转账成功,李四也没多钱。这就是原子性的体现。
而张三必须扣100,李四必须加100,这个就是一致性了,如果因为某些逻辑原因,导致张三扣了100,流水记录100转账,而李四只加了60。然后这3条操作都成功了,那原子性就符合了,但是一致性就不符合了
#并发控制
并发控制的主要方法是封锁(Locking)。
数据库是要被广大客户所共享访问的,那么在数据库操作过程中很可能出现以下几种不确定情况:
两个事务T1,T2读入同一数据并修改,T2提交的结果被T1破坏了,导致T1的修改丢失。(订票系统)
事务T1读取数据后,事务T2执行更新操作,使T1无法再次读取结果。
事务T1修改某个数据并写回磁盘,事务T2读取同一数据,但T1由于某种原因撤销了,这时T1修改过的数据恢复原来的值,T2读取的数据就与数据库中的数据不一致。
事务在操作过程中进行两次查询,第二次查询结果包含了第一次查询中未出现的数据(这里并不要求两次查询SQL语句相同)这是因为在两次查询过程中有另外一个事务插入数据造成的。
封锁是实现并发控制的一个非常重要的技术,所谓 封锁就是事务T在对某个数据对象例如表、记录等操作之前,先向系统发出请求,对其加锁。数据库系统提供两种锁:
排他锁(写锁):若事务T对数据对象A加写锁,则只允许T读取和修改A,其他事务都不能再对A加任何类型的锁,直到T释放A上的锁为止。
共享锁(读锁):若事务T对数据对象A加读锁,则只允许T可以读取但不能修改A,其他事务只能再对A加读锁,而不能加写锁,直到T释放A上的读锁为止。
为了避免上面出现几种情况在标准SQL规范中定义了4个事务隔离级别,不同隔离级别对事务处理不同 。
未提交读(READ UNCOMMITTED)是最低的隔离级别。允许脏读(dirty reads),但不允许更新丢失,事务可以看到其他事务“尚未提交”的修改。
允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
禁止不可重复读取和脏读取,但是有时可能出现幻读数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
最高的隔离级别,它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
使用读锁和写锁时,需要约定一定的规则。比如:何时申请、持续时间、何时释放等。这些规则被称为 封锁协议。针对不同的事务隔离级别,有不同的封锁协议。
事务T在修改数据R之前必须先对其加写锁,直到事务结束才释放。一级封锁协议防止了丢失修改,但不能保证可重复读和不读脏数据。
在一级封锁协议的基础上增加事务T在读数据R前必须加读锁,读完就可以释放。二级封锁协议进一步防止读脏数据,但不能保证可重复读。
一级封锁协议的基础上增加事务T在读数据R前必须加读锁,直到事务结束才释放。三阶封锁协议除了防止丢失修改和读脏数据外,进一步防止了不可重复读。
四级封锁协议是对三级封锁协议的增强,其实现机制也最为简单,直接对事务中所读取或者更改的数据所在的表加表锁,也就是说,其他事务不能 读写 该表中的任何数据。
调度是一个或多个事务的重要操作按时间排序的一个序列。如果一个调度的动作首先是一个事务的所有动作,然后是另一个事务的所有动作,以此类推,而没有动作的混合,那么我们说这一调度是串行的。
事务的正确性原则告诉我们,每个串行调度都将保持数据库状态的一致性。 通常,不管数据库初态怎样,一个调度对数据库状态的影响都和某个串行调度相同,我们就说这个调度是可串行化的。
可串行性是并行调度正确性的唯一准则,两段锁(简称2PL)协议是为保证并行调度可串行性而提供的封锁协议。两段锁协议规定:在对任何数据进行读、写操作之前,事务道首先要获得对该数据的封锁,而且在释放一个封锁之生,事务不再获得任何其他封锁。
所谓“两段”锁的含义是:事务分为两个阶段,第一阶段是获得封锁,也称为扩展阶段,第二阶段是释放封锁,也称为收缩阶段。
在MySQL中使用START TRANSACTION 或 BEGIN开启事务,提交事务使用COMMIT,ROLLBACK用来放弃事务。MySQL默认设置了事务的自动提交,即一条SQL语句就是一个事务。
事务的(ACID)特性是由关系数据库管理系统(RDBMS,数据库系统)来实现的。数据库管理系统采用日志来保证事务的原子性、一致性和持久性。日志记录了事务对数据库所做的更新,如果某个事务在执行过程中发生错误,就可以根据日志,撤销事务对数据库已做的更新,使数据库退回到执行事务前的初始状态。
数据库管理系统采用锁机制来实现事务的隔离性。当多个事务同时更新数据库中相同的数据时,只允许持有锁的事务能更新该数据,其他事务必须等待,直到前一个事务释放了锁,其他事务才有机会更新该数据。
#索引
在数据库中,索引的含义与日常意义上的“索引”一词并无多大区别(想想小时候查字典),它是用于提高数据库表数据访问速度的数据库对象。
一条索引记录中包含的基本信息包括:键值(即你定义索引时指定的所有字段的值) 逻辑指针(指向数据页或者另一索引页)。
当你为一张空表创建索引时,数据库系统将为你分配一个索引页,该索引页在你插入数据前一直是空的。此页此时既是根结点,也是叶结点。每当你往表中插入一行数据,数据库系统即向此根结点中插入一行索引记录。当根结点满时,数据库系统大抵按以下步骤进行分裂:
通常状况下,由于索引记录仅包含索引字段值(以及4-9字节的指针),索引实体比真实的数据行要小许多,索引页相较数据页来说要密集许多。一个索引页可以存储数量更多的索引记录,这意味着在索引中查找时在I/O上占很大的优势,理解这一点有助于从本质上了解使用索引的优势。
汉语字典的正文本身就是一个聚集索引。比如,我们要查“安”字,就会很自然地翻开字典的前几页,因为“安”的拼音是“an”,而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的,那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明您的字典中没有这个字;同样的,如果查“张”字,那您也会将您的字典翻到最后部分,因为“张”的拼音是“zhang”。也就是说,字典的正文部分本身就是一个目录,您不需要再去查其他目录来找到您需要找的内容。正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”。
如果您认识某个字,您可以快速地从自动中查到这个字。但您也可能会遇到您不认识的字,不知道它的发音,这时候,您就不能按照刚才的方法找到您要查的字,而需要去根据“偏旁部首”查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法,比如您查“张”字,我们可以看到在查部首之后的检字表中“张”的页码是672页,检字表中“张”的上面是“驰”字,但页码却是63页,“张”的下面是“弩”字,页面是390页。很显然,这些字并不是真正的分别位于“张”字的上下方,现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字,但它需要两个过程,先找到目录中的结果,然后再翻到您所需要的页码。
表数据按照索引的顺序来存储的。对于聚集索引,叶子结点即存储了真实的数据行,不再有另外单独的数据页。在聚集索引中,叶结点也即数据结点,所有数据行的存储顺序与索引的存储顺序一致。
在一张表上只能创建一个聚集索引,因为真实数据的物理顺序只可能是一种。如果一张表没有聚集索引,那么它被称为“堆集”(Heap)。这样的表中的数据行没有特定的顺序,所有的新行将被添加的表的末尾位置。
表数据存储顺序与索引顺序无关。对于非聚集索引,叶结点包含索引字段值及指向数据页数据行的逻辑指针,该层紧邻数据页,其行数量与数据表行数据量一致。
非聚集索引与聚集索引相比:
叶子结点并非数据结点 叶子结点为每一真正的数据行存储一个“键-指针”对 叶子结点中还存储了一个指针偏移量,根据页指针及指针偏移量可以定位到具体的数据行。 类似的,在除叶结点外的其它索引结点,存储的也是类似的内容,只不过它是指向下一级的索引页的。
索引并不是时时都会生效的,比如以下几种情况,将导致索引失效:
聚集索引是一种稀疏索引,数据页上一级的索引页存储的是页指针,而不是行指针。而对于非聚集索引,则是密集索引,在数据页的上一级索引页它为每一个数据行存储一条索引记录。
与非聚集索引相比,聚集索引有着更快检索速度、更快的字段排序。
在MySQL中InnoDB按照主键进行聚集,如果没有定义主键,InnoDB会试着使用唯一的非空索引来代替。如果没有这种索引,InnoDB就会定义隐藏的主键然后在上面进行聚集,但是主键和聚集索引是不等价的。在InnoDB中Normal索引即非聚集索引。
SQL语言由一些简单句子构成基本的语法,所有的SQL语句均有自己的格式,典型的SQL语句(查询语句)结构如图所示:
QL语法的基础是子句(clause),子句中会包括一些关键词(keyword)。每条SQL语句均由一个关键词开始,该关键词描述这条语句要产生的动作。SQL中常用的关键词及其功能如表所示:
CREATE TABLE
CREATE TABLE `user` (
`id` INT AUTO_INCREMENT,
`name` VARCHAR (20),
PRIMARY KEY (`id`)
);
VARCHAR记得指定长度。
UPDATE
UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
INSERT
INSERT INTO 表名称 VALUES (值1, 值2,....)
INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....)
DELETE
DELETE FROM 表名称 WHERE 列名称 = 值
1
ALTER TABLE table_name add column_name datatype
ALTER TABLE table_name drop COLUMN column_name
ALTER TABLE table_name modify COLUMN column_name datatype
grant select,insert on userdb.userinfo to‘zhangsan‘@‘localhost‘
%:表示任意0个或多个字符。可匹配任意类型和长度的字符,有些情况下若是中文,请使用两个百分号(%%)表示。
select * from test where text like ‘%1%‘;
_ : 表示任意单个字符。匹配单个任意字符,它常用来限制表达式的字符长度语句。
--倒数第三个字符为 1 ,且最小长度为 5
select * from test where text like ‘__%1__‘;
#连接 在数据库原理中,关系运算包含 选择、投影、连接 这三种运算。相应的在SQL语句中也有表现,其中Where子句作为选择运算,Select子句作为投影运算,From子句作为连接运算。
连接运算是从两个关系的笛卡尔积中选择属性间满足一定条件的元组,在连接中最常用的是等值连接和自然连接。
等值连接:关系R、S,取两者笛卡尔积中属性值相等的元组,不要求属性相同。比如 R.A=S.B 自然连接(内连接):是一种特殊的等值连接,它要求比较的属性列必须是相同的属性组,并且把结果中重复属性去掉。 关系R:
A B C
a1 b1 5
a1 b2 6
a2 b3 8
a2 b4 12
关系S:
B E
b1 3
b2 7
b3 10
b3 2
b5 2
自然连接 R & S的结果为:
A B C E
a1 b1 5 3
a1 b2 6 7
a2 b3 8 10
a2 b3 8 2
两个关系在做自然连接时,选择两个关系在公共属性上值相等的元组构成新的关系。此时关系R中某些元组有可能在S中不存在公共属性上相等的元组,从而造成R中这些元组在操作时被舍弃,同样,S中某些元组也可能被舍弃。这些舍弃的元组被称为 悬浮元组。
如果把悬浮元组也保存在结果中,而在其他属性上置为NULL,那么这种连接就成为 外连接,如果只保留左边关系R中的悬浮元组就叫做 左外连接(左连接),如果只保留右边关系S中的悬浮元组就叫做 右外连接(右连接)。
Join 用于多表中字段之间的联系,语法如下:
... FROM table1 INNER|LEFT|RIGHT JOIN table2 ON conditiona
-- table1:左表;table2:右表。
JOIN 按照功能大致分为如下三类:
取得两个表中存在连接匹配关系的记录。
取得左表(table1)完全记录,即是右表(table2)并无对应匹配记录。
与 LEFT JOIN 相反,取得右表(table2)完全记录,即是左表(table1)并无匹配对应记录。
在下面的示例中使用以下数据:
mysql> select A.id,A.name,B.name from A,B where A.id=B.id;
id name name
1 Pirate Rutabaga
2 Monkey Pirate
3 Ninja Darth Vader
4 Spaghetti Ninja
4 rows in set (0.00 sec)
内连接,也叫等值连接,inner join产生同时符合A和B的一组数据。
mysql> select * from A inner join B on A.name = B.name;
id name id name
1 Pirate 2 Pirate
3 Ninja 4 Ninja
mysql> select * from A left join B on A.name = B.name;
-- 或者:select * from A left outer join B on A.name = B.name;
id name id name
1 Pirate 2 Pirate
2 Monkey NULL NULL
3 Ninja 4 Ninja
4 Spaghetti NULL NULL
4 rows in set (0.00 sec)
left join,(或left outer join:在Mysql中两者等价,推荐使用left join)左连接从左表(A)产生一套完整的记录,与匹配的记录(右表(B))。如果没有匹配,右侧将包含null。
如果想只从左表(A)中产生一套记录,但不包含右表(B)的记录,可以通过设置where语句来执行,如下:
mysql> select * from A left join B on A.name=B.name where A.id is null or B.id is null;
id name id name
2 Monkey NULL NULL
4 Spaghetti NULL NULL
2 rows in set (0.00 sec)
根据上面的例子可以求差集,如下:
SELECT * FROM A LEFT JOIN B ON A.name = B.name
WHERE B.id IS NULL
union
SELECT * FROM A right JOIN B ON A.name = B.name
WHERE A.id IS NULL;
id name id name
2 Monkey NULL NULL
4 Spaghetti NULL NULL
NULL NULL 1 Rutabaga
NULL NULL 3 Darth Vader
用于合并多个 select 语句的结果集,并去掉重复的值。 union all :作用和 union 类似,但不会去掉重复的值。
交叉连接,得到的结果是两个表的乘积,即笛卡尔积。
实际上,在 MySQL 中(仅限于 MySQL) CROSS JOIN 与 INNER JOIN 的表现是一样的,在不指定 ON 条件得到的结果都是笛卡尔积,反之取得两个表完全匹配的结果。
INNER JOIN与 CROSS JOIN 可以省略 INNER 或 CROSS 关键字,因此下面的 SQL 效果是一样的:
... FROM table1 INNER JOIN table2
... FROM table1 CROSS JOIN table2
... FROM table1 JOIN table2
#主键与外键
外键取值规则:空值或参照的主键值
(1)插入非空值时,如果主键值中没有这个值,则不能插入。
(2)更新时,不能改为主键表中没有的值。
(3)删除主键表记录时,可以在建外键时选定外键记录一起联删除还是拒绝删除。
(4)更新主键记录时,同样有级联更新和拒绝执行的选择。
简而言之,SQL的主键和外键就是起约束作用。 关系型数据库中一条记录中有若干个属性,若其中某一个属性组(注意是组)能唯一标识一条记录,该属性就可以成为一个主键。例如:
学生表(学号,姓名,性别,班级)
其中每个学生的学号是唯一的,学号就是一个主键;
课程表(课程编号,课程名,学分)
其中课程编号是唯一的,课程编号就是一个主键;
成绩表(学号,课程号,成绩)
成绩表中单一一个属性无法唯一标识一条记录,学号和课程编号的组合才可以唯一标识一条记录,所以学号和课程编号的属性组是一个主键。 成绩表中的学号不是成绩表中的主键,但它和学生表中的学号相对应,并且学生表中的学号是学生表的主键,则称成绩表中的学号是学生表的外键;同理,成绩表中的课程号是课程表的外键。
定义主键和外键主要是为了维护关系数据库的完整性,总结一下:
1.主键是能确定一条记录的唯一标识。比如,一条记录包括身份证号码,姓名,年龄。身份证号码是唯一确认你这个人的,其他的都可能有重复,所以身份证号码是主键。 外键用于与另一张表相关联。是能确认另一张表记录的字段,用于保持数据的一致性。比如,A表中的一个字段,是B表的主键,那它就可以是A表的外键。
2.主键、外键和索引的区别
定义:
主键:唯一标识一条记录,不能有重复,不允许为空。
外键:表的外键是另一表的主键,外键是可以有重复的,可以是空值。
索引:该字段没有重复值,但可以有一个空值。
作用:
主键:用来保证数据完整性
外键:用来和其他表建立联系用
索引:用来提高查询排序的速度
个数:
主键:主键只能有一个。
外键:一个表可以有多个外键。
索引:一个表可以有多个唯一索引。
创建SQL的主键和外键约束的方法:
--如果在表创建好了以后再加约束,则格式分别为
--主键
ALTER TABLE 表名
--"PK"为主键的缩写,字段名为要在其上创建主键的字段名,"PK_字段名"就为约束名
ADD CONSTRAINT PK_字段名
--同上
PRIMARY KEY(字段名)
--唯一约束
ALTER TABLE 表名
ADD CONSTRAINT UQ_字段名
UNIQUE(字段名)
--外键约束
ALTER TABLE 表名
--"FK"为外键的缩写
ADD CONSTRAINT FK_字段名
FOREIGN KEY(字段名) REFERENCES 关联的表名(关联的字段名)
--举个例子
ALTER TABLE 表A
ADD CONSTRAINT FK_B
FOREIGN KEY(TicketNo) REFERENCES 表B(TicketNo)
--级联更新,级联删除,这样在删除主表Student时,成绩表中该学生的所有成绩都会删除
ALTER TABLE 成绩表
ADD CONSTRAINT FK_StudentNo
FOREIGN KEY(StudentNo) REFERENCES Student(StudentNo)
ON UPDATE CASCADE ON DELETE CASCADE
--检查约束
ALTER TABLE 表名
ADD CONSTRAINT CK_字段名
--括号中的"CK_字段名>0"为条件表达式,用关系运算符连接
CHECK(字段名>0)
--默认值约束
ALTER TABLE 表名
ADD CONSTRAINT DF_字段名
--其中的‘默认值‘为想要设置的默认值,注意‘FOR‘
DEFAULT ‘默认值‘ FOR 字段名
--删除创建的约束
ALTER TABLE 表名
--约束名为前面创建的如:FK_字段名这样的约束名
DROP CONSTRAINT 约束名
--注意:如果约束是在创建表的时候创建的,则不能用命令删除
--只能在‘企业管理器‘里面删除
--获取SqlServer中的表结构
SELECT syscolumns.name,systypes.name,syscolumns.isnullable,syscolumns.length
FROM syscolumns,systypes
WHERE syscolumns.xusertype = systypes.xusertype
AND syscolumns.id = OBJECT_ID(‘Student‘)
--单独查询表递增字段
SELECT [NAME] FROM syscolumns
WHERE
id = OBJECT_ID(N‘Student‘) AND COLUMNPROPERTY(id,name,‘IsIdentity‘)=1
--获取表主外键约束
EXEC sp_helpconstraint ‘Student‘
--查询表主键外键信息
SELECT
sysobjects.id AS objectId,
OBJECT_NAME(sysobjects.parent_obj) AS TableName,
sysobjects.name AS constraintName,
sysobjects.xtype AS constraintType,
syscolumns.name AS columnName
FROM sysobjects INNER JOIN sysconstraints
ON sysobjects.xtype IN(‘C‘,‘F‘,‘PK‘,‘UQ‘,‘D‘)
AND sysobjects.id = sysconstraints.constid
LEFT OUTER JOIN syscolumns ON sysconstraints.id = syscolumns.id
WHERE OBJECT_NAME(sysobjects.parent_obj) = ‘Student‘
原文:https://blog.csdn.net/qq_36982160/article/details/89258056#_450
原文:https://www.cnblogs.com/teng21314/p/12033247.html