赞
踩
创建数据库模板:
create database 数据库名称 --创建数据库 on primary ( name --数据库的逻辑名称 filename --物理存放位置及物理文件名称(Student_info.mdf就是在磁盘上显示的名称) size --设置数据文件初始大小 maxsize --设置最大限制 filegrowth --设置主数据文件增长幅度 ) log on --定义事务日志文件 ( name --逻辑名称 filename --物理存放位置及物理文件名称 size= --设置事务日志文件初始大小 maxsize --设置最大限制为 filegrowth --设置事务日志增长幅度 )
eg:按要求创建一个数据库
具体参数如下表:
参数名称 | 参考参数 |
---|---|
数据库名称 | Student_info1 |
数据库逻辑文件名称 | Student_info1_data |
数据库物理文件名称 | Stduent_info1_data.mdf |
数据库文件初始大小 | 20MB |
数据文件大小最大值 | 300MB |
数据文件增长增量 | 5MB |
日志逻辑文件名称 | Student_info_log1 |
日志物理文件名称 | Student_info_log1.ldf |
日志文件初始大小 | 5Mb |
日志文件大小最大值 | 50MB |
日志文件增长量 | 1MB |
代码:
create database Student_info1--创建数据库 on primary ( name= Student_info1,--数据库的逻辑名称 filename='E:\大2上学期\数据库原理\sql serve\Student_info1.mdf',--物理存放位置及物理文件名称(Student_info.mdf就是在磁盘上显示的名称) size=20,--初始大小 maxsize=300,--最大限制 filegrowth=5--主数据文件增长幅度为5MB ) log on--定义事务日志文件 ( name =Student_info_log1,--逻辑名称 filename='E:\大2上学期\数据库原理\sql serve\Student_info1.ldf',--物理存放位置及物理文件名称 size=5,--初始大小为5mb maxsize=50,--最大限制为50mb filegrowth=1--事务日志增长幅度为1mb )
查看数据库(查看是否创建了名称为itcast的数据库)
show database;
查看某个已经创建的数据库信息
show create database 数据库名称;
当我们发现数据库的属性,创建的时候不小心写错了,或者别的原因需要修改数据属性的时候应该怎么办呢?
例如:
将刚刚创建的数据库Student_info 数据文件的初始值大小 改为30MB, 最大值 改为500MB, 数据增长量 改为5%, 日志文件初始值 改为20MB, 最大值 改为60MB, 数据增长量 为6%.
代码:
--修改数据文件属性 ALTER DATABASE Student_info1 MODIFY FILE ( NAME = Student_info1,--要修改属性的文件名称 SIZE = 30,--修改初始大小为30mb maxsize=300,--修改最大限制为300mb filegrowth=5%--修改事务日志增长幅度为5% ) --修改事务日志文件属性 ALTER DATABASE Student_info1 MODIFY FILE ( NAME =Student_info_log1, size=20,--修改初始大小为20mb maxsize=60,--修改最大限制为60mb filegrowth=6%--修改事务日志增长幅度为6% )
数据库分离:数据库分离是指将数据库文件从数据库服务器实例中分离出来,相当于关闭了数据库。数据库分离后,应用程序不能连接到该数据库,数据库文件可以被其它进程访问。通常分离数据库用于快速地将数据库迁移到另一个SQLSERVER实例中.
从创建数据库的语句中我们不难知道,我们创建的数据库是一个存放在我们电脑硬盘上的一个文件,受DBMS(数据库管理系统)管理,如果我们想要自己创建的数据库在别的客户端,或者说是让别人在它的电脑上也能使用,我们就需要对数据库分离,然后找到被分离的数据库文件.mdf(数据文件),和.ldf(日志文件)
模板:
--分离
execute sys.sp_detach_db @dbname=数据库名
--附加
execute sp_attach_db Student_info1 ,filenme
eg:将数据库Student_info1分离后又附加
代码:
----将数据库Student_info1分离
execute sys.sp_detach_db @dbname='Student_info'
execute sp_attach_db Student_info1 ,
'E:\大2上学期\数据库原理\sql serve\Student_info1.mdf',
--filename每个人的不一样,与当时创建的数据库存放的地址有关
'E:\大2上学期\数据库原理\sql serve\本Student_info1.ldf'
--filename每个人的不一样,与当时创建的数据库存放的地址有关
模板:
create table 表名(
属性名1 数据类型,
属性名2 数据类型,
属性名3 数据类型
.....
--注意最后一条语句不用','
)
数据类型(data_type)规定了列可容纳何种数据类型。下面的表格包含了SQL中最常用的数据类型:
数据类型 | 描述 |
---|---|
integer(size),int(size),smallint(size),tinyint(size) | 仅容纳整数、在括号内规定数字的最大位数 |
decimal(size,d),numeric(size,d) | 容纳带有小数的数字、“size” 规定数字的最大位数、“d” 规定小数点右侧的最大位数 |
char(size) | 容纳固定长度的字符串(可容纳字母、数字以及特殊字符)、在括号中规定字符串的长度 |
varchar(size) | 容纳可变长度的字符串(可容纳字母、数字以及特殊的字符)、在括号中规定字符串的最大长度 |
date(yyyymmdd) | 容纳日期 |
数据查询的语句中包括了很多数据查询的操作,常用的DQL数据查询语法如下:
SELECT
字段列表
FROM
表名列表
WHERE
条件列表
GROUP BY
分组字段列表
HAVING
分组后条件列表
ORDER BY
排序字段列表
LIMIT
分页参数
SELECT 字段1,字段2,字段3... FROM 表名;
SELECT * FROM 表名;
其中,第一条语句可以选择其中的部分字段,而第二句可以查询所有的字段,其中*表示通配符全部。
在实际查询的过程中,我们可以为字段设置别名。在合适的时间设置别名有助于我们对数据表的字段理解,设置别名的语句如下所示:
SELECT 字段1[AS 别名1],字段2[AS 别名2] ... FROM 表名;
在某些情况下,我们希望查询的记录中不要有重复的内容,就可以用下面的语句:
SELECT DISTINCT 字段列表 FROM 表名;
在条件查询中,有一个通用的语法如下:
SELECT 字段列表 FROM 表名 WHERE 条件列表;
当我们需要进行条件查询时,需要用到很多查询条件运算符,这就需要我们对不同的运算符功能做了解:
比较运算符 | 功能 |
---|---|
> | 大于 |
>= | 大于等于 |
< | 小于 |
<= | 小于等于 |
= | 等于 |
!= 或 <> | 不等于 |
BETWEEN… AND… | 在某个范围之内(含最大、最小值) |
IN(…) | 在in之后的列表中的值 |
LIKE | 模糊匹配(_匹配单个字符,%匹配多个字符) |
IS NULL | 是NULL |
除上述比较运算符外,我们需要一些逻辑运算符来进行逻辑查询的编写:
逻辑运算符 | 功能 |
---|---|
AND 或 && | 并且 |
OR | 或者 |
NOT 或 ! | 非,不是 |
将一列数据作为一个整理进行计算的函数
聚合函数可以帮助我们完成很多聚合的功能,常用的聚合函数如下:
函数 | 功能 |
---|---|
count | 统计数量 |
max | 最大值 |
min | 最小值 |
avg | 平均值 |
sum | 求和 |
SELECT 聚合函数(字段列表) FROM 表名;
注意:所有的聚合函数是不计算null值的
SELECT 字段列表 FROM 表名 [WHERE 条件] GROUP BY 分组字段名 [HAVING 分组过后的条件];
WHERE和HAVING的区别:
执行时机不同:WHERE是分组之前进行过滤,不满足WHERE条件,不参与分组;而HAVING是分组之后对结果进行过滤。
判断条件不同:WHERE不能对聚合函数进行判断,而HAVING可以。
例如,我们有一张图书表格如下所示:
我们想查询两种图书种类的数量,可以按照下面的语句方式查询:
注意:
执行顺序: WHERE>聚合函数>HAVING
分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段没有任何意义
SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1, 字段2 排序方式2;
排序方式:
ASC:升序(默认值)
DESC:降序
注意:如果是多字段排序,与第一个字段值相同时,才会根据第二个字段进行排序。
例如,我们想要按照升序对某员工表进行查询,我们可以使用如下的语句:
当我们希望年龄相同的员工的入职时间排降序的时候,可以按照下面的内容操作:
SELECT 字段列表 FROM 表名 LIMIT 起始索引,查询记录数;
注意:
起始索引从0开始,起始索引=(查询页码-1)*每页显示记录数。
分页查询是数据库的方言,不同的数据库有不同的实现,MySQL是LIMIT。
如果查询的是第一页数据,起始索引可以省略,直接简写为limit10。
例如在一个数据表EMP中,我们想要使用第一页的十个数据,可以使用下面的操作:
这样就会返回第一页的十条数据,此时若我们想要查询第二页数据,可以使用
另外,不同的数据库中分页查询的实现是不同的,在mysql中实现是LIMIT。
数据查询的语句中包括了很多数据查询的操作,常用的DQL数据查询语法如下:
SELECT
字段列表
FROM
表名列表
WHERE
条件列表
GROUP BY
分组字段列表
HAVING
分组后条件列表
ORDER BY
排序字段列表
LIMIT
分页参数
同时这也是我们编写DQL语句的顺序,但是执行顺序与编写顺序并不相同。
DQL语句的执行顺序为:
SELECT 4
字段列表
FROM 1
表名列表
WHERE 2
条件列表
GROUP BY 3
分组字段列表
HAVING
分组后条件列表
ORDER BY 5
排序字段列表
LIMIT 6
分页参数
视图是一个虚拟的表,不同于直接操作数据表,视图是依据SELECT语句来创建的(后续会具体介绍),所以操作视图时会根据创建视图的SELECT语句生成一张虚拟表,然后在这张虚拟表上做SQL操作
用一句话概括视图与表的区别就是“是否保存了实际的数据”。所以视图并不是数据库真实存储的数据表,它可以看作是一个窗口,通过这个窗口我们可以看到数据库表中真实存在的数据。所以我们要区别视图和数据表的本质,即视图是基于真实表的一张虚拟的表,其数据来源均建立在真实表的基础上。
创建视图的基本语法为:
CREATE VIEW <视图名称>(<列名1>,<列名2>,...) AS <SELECT 语句>
其中SELECT 语句需要书写在 AS 关键字之后。 SELECT 语句中列的排列顺序和视图中列的排列顺序相同, SELECT 语句中的第 1 列就是视图中的第 1 列, SELECT 语句中的第 2 列就是视图中的第 2 列,以此类推。而且视图的列名是在视图名称之后的列表中定义的。
需要注意的是视图名在数据库中需要是唯一的,不能与其他视图和表重名。 视图不仅可以基于真实表,我们也可以在视图的基础上继续创建视图。
虽然在视图上继续创建视图的语法没有错误,但我们还是应该尽量避免这种操作。因为对多数DBMS 来说, 多重视图会降低 SQL 的性能。
需要注意的是在一般的DBMS中定义视图时不能使用ORDER BY语句,因为数据行是没有顺序的。但在MySQL中视图的定义是允许使用ORDER BY语句的。但是若从特定视图进行选择,而该视图使用了自己的 ORDER BY 语句,则视图定义中的 ORDER BY 将被忽略。
CREATE VIEW productsum (product_type, cnt_product)
AS
SELECT product_type, COUNT(*)
FROM product
GROUP BY product_type ;
创建的视图如下图所示:
基于多表的视图
我们在创建一张表用于学习多表视图,代码如下:
CREATE TABLE shop_product (shop_id CHAR(4) NOT NULL, shop_name VARCHAR(200) NOT NULL, product_id CHAR(4) NOT NULL, quantity INTEGER NOT NULL, PRIMARY KEY (shop_id, product_id)); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000A', '东京', '0001', 30); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000A', '东京', '0002', 50); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000A', '东京', '0003', 15); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0002', 30); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0003', 120); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0004', 20); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0006', 10); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0007', 40); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0003', 20); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0004', 50); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0006', 90); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0007', 70); INSERT INTO shop_product (shop_id, shop_name, product_id, quantity) VALUES ('000D', '福冈', '0001', 100);
我们在product表和shop_product表的基础上创建视图。
CREATE VIEW view_shop_product(product_type, sale_price, shop_name)
AS
SELECT product_type, sale_price, shop_name
FROM product,
shop_product
WHERE product.product_id = shop_product.product_id;
我们可以在这个视图的基础上进行查询,代码为:
SELECT sale_price, shop_name
FROM view_shop_product
WHERE product_type = '衣服';
查询结果为:
修改视图结构的基本语法如下:
ALTER VIEW <视图名> AS <SELECT语句>
其中视图名在数据库中需要是唯一的,不能与其他视图和表重名。当然也可以通过将当前视图删除然后重新创建的方式达到修改的效果。
下面代码以productsum视图示例怎样修改视图:
ALTER VIEW productsum
AS
SELECT product_type, sale_price
FROM Product
WHERE regist_date > '2009-09-11';
视图是一个虚拟表,所以对视图的操作就是对底层基础表的操作,所以修改时只有满足底层基本表的定义才能成功修改。对于一个视图来说,如果包含以下结构的任意一种都是不可以被更新的:
视图是从表派生出来的,因此如果原表可以更新,那么视图中的数据也可以更新,反之亦然。以下是一个示例:
UPDATE productsum
SET sale_price = '5000'
WHERE product_type = '办公用品';
需要注意的是原表中的数据部分内容也被更新了,原因是视图是原表的一个窗口,修改视图也只能修改透过窗口能看到的内容。
注意:这里虽然修改成功了,但是并不推荐这种使用方式。而且我们在创建视图时也尽量使用限制不允许通过视图来修改表
删除视图的基本语法为
DROP VIEW <视图名1> [, <视图名2>...]
,需要有相应权限才可以删除。
注册嘎下一步是指一个查询语句嵌套在另一个查询语句内部的查询,在 SELECT 子句中先计算子查询,子查询结果作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。
子查询就是将用来定义视图的SELECT语句直接用于FROM子句当中。由于子查询是一次性的,所以子查询不会像视图那样保存在存储介质中,而是在SELECT语句执行之后就消失了。
与在视图上再定义视图类似,子查询也没有具体的限制。见如下示例:
SELECT product_type, cnt_product
FROM (SELECT *
FROM (SELECT product_type,
COUNT(*) AS cnt_product
FROM product
GROUP BY product_type) AS productsum
WHERE cnt_product = 4) AS productsum2;
其中最内层的子查询我们将其命名为productsum,这条语句根据product_type分组并查询个数,第二层查询中将个数为4的商品查询出来,最外层查询product_type和cnt_product两列。
虽然嵌套子查询可以查询出结果,但是随着子查询嵌套的层数的叠加,SQL语句不仅会难以理解而且执行效率也会很差,所以要尽量避免这样的使用。
标量就是单一的意思,那么标量子查询也就是单一的子查询。所谓单一就是要求我们执行的SQL语句只能返回一个值,也就是要返回表中具体的某一行的某一列。
我们用一个示例来说明标量子查询的应用:查询出售单价高于平均销售单价的商品,语句如下:
SELECT product_id, product_name, sale_price
FROM product
WHERE sale_price > (SELECT AVG(sale_price) FROM product);
上面的这条语句首先后半部分查询出product表中的平均售价,前面的sql语句在根据WHERE条件挑选出合适的商品。
由于标量子查询的特性,导致标量子查询不仅仅局限于 WHERE 子句中,通常任何可以使用单一值的位置都可以使用。也就是说, 能够使用常数或者列名的地方,无论是 SELECT 子句、GROUP BY 子句、HAVING 子句,还是 ORDER BY 子句,几乎所有的地方都可以使用。下面是一个示例:
SELECT product_id,
product_name,
sale_price,
(SELECT AVG(sale_price)
FROM product) AS avg_price
FROM product;
关联意味着查询与子查询之间的关系,关联子查询就是通过一些标志将内外两层的查询连接起来起到过滤数据的目的,下面我们以上文中选出售价高于平均售价的商品进行说明:
SELECT product_type, product_name, sale_price
FROM product AS p1
WHERE sale_price > (SELECT AVG(sale_price)
FROM product AS p2
WHERE p1.product_type = p2.product_type
GROUP BY product_type);
上述SQL语句也就是关联子查询中我们将外面的product表标记为p1,将内部的product设置为p2,而且通过WHERE语句连接了两个查询。
关联子查询的执行过程简单概括为:
在子查询中像标量子查询,嵌套子查询或者关联子查询可以看作是子查询的一种操作方式即可。
创建序列的语法:
create sequence 序列名称
[incremnet by 步长]
[start with 开始值]
[maxvalue 最大值 | nomaxvalue]
[minvalue 最小值 | nominvalue]
[cycle|nocycle][cache 缓存大小|nocach
创建序列
create sequence myseq;
查看序列
select * from user_sequences;
删除序列
drop sequence myseq;
序列操作:
通过nextval属性操作序列
select myseq.nextval from dual;
通过currval属性操作序列
select myseq.currval from dual;
当用户每次重复调用mysql.nextval操作序列的时候,序列都会自动地增长,而且增长的步长(INCREMENT_BY设置)为1,而当用户只调用myseq.currval操作序列时,无论调用多少次,都只会返回当前最后一次增长的序列值,不会进行增长
sequence_name: 序列名称。
min_value: 此序列开始的默认最小值(默认是0max_value:此序列开始的默认最大值(默认是99…99<27个>)
increment_by: 序列每次增长的步长(默认是1)
cycle_flag: 循环标记,y表示循环;n表示非循环。
cache_size: 序列操作的缓存量(默认是20)。
last_number: 最后一次操作的数值。
//member表的数据库创建脚本 //范例:member表的数据库创建脚本 drop table member purge ; create table member ( mid number , name varchar2(50) not null , constraint pk_mid primary key(mid) ); //通过数据字典查看member表是否已经成功创建 select * from member ; //范例:编写数据插入语句,向member表中增加记录 insert into member (mid,name) values(myseq.nextval,‘vdata’) ; //范例:检索全部member表数据 select * from member; ### 序列的删除 范例:删除myseq序列 drop sequence myseq ; 范例:查询全部的序列 select * from user_ sequences;
//范例:创建一个新的序列,让其每次的增长步长3
drop sequence myseq ;
create sequence myseq increment by 3 ;
//范例:通过user_sequences数据字典查询序列是否已经成功创建
select sequence_name,increment_by from user_sequences ;
//范例:调用nextval属性,操作序列(本语句将执行三次)
select myseq.nextval from dual ;
//范例:创建序列,让其初始值设置为30,每次增长步长为2
drop sequence myseq ;
create sequence myseqincrement by 2 START with 30;
//范例:操作myseq序列,调用三次nextval属性观察结果
select myseq.nextval from dual;
//范例:创建序列,缓存设置为100
drop sequence myseq ;
create sequence myseq cache 100 ;
//范例:通过user_sequences数据字典表,查询序列信息
select cache_ size,last_number from user_sequences;
//范例:创建序列,不使用缓存
drop sequence myseq ;
create sequence myseq nocache ;
//范例:通过user_sequences数据字典表,查询序列信息
select cache_ size,last_number from user_sequences;
CREATE sequence 序列名称[maxvalue 最大值 | nomaxvalue]
[minvalue 最小值 | nominvalue]
[cycle|NOCYCL
//范例20:创建循环序列,让序列的内容在1、3、5、7、9之间循环
drop sequence myseq ;
create sequence myseq start with 1 INCREMENT by 2 MAXVALUE 9 MINVALUE 1 CYCLE CACHE 3 ;
//范例21:通过user_sequeces数据字典查询序列信息
select sequence_name,max_value,min_value,increment_by,cache_size from
user_sequences ;
alter sequence 序列名 [incremnet by 步长] [start with 开始值] [maxvalue 最大值 | nomaxvalue] [minvalue 最小值 | nominvalue] [cycle|nocycle][cache 缓存大小|nocach //范例:创建一个基本序列 drop sequence myseq ; create sequence myseq ; //范例:通过user_sequences数据字典查看所有序列 select * from user_ sequences; //将每次增长的步长修改为10。将序列的最大值修改为98765.缓存修改为10 alter sequence myseq increment by 10 MAXVALUE 98765 CACHE 100 ; //范例:通过user_sequences数据字典查看所有序列 select * from user_ sequences
假如你每天要开车完成一些列重复的操作:第一步拿出车钥匙,第二步开车,第三步侧位停车。现在出现了一款超牛逼的车,可以一键自动的完成这些重复的工作。每次你随便找个第二下车以后,车就自动帮助你车位停车到位了甚至,按一个按钮就能起飞了,你说爽不爽?
同样的,在工作中也会经常遇到很多重复性的工作,比如今天查小明考勤,明天查小红考勤,这时候就可以把常用的SQL写好存储起来,这就是存储过程。
这样下次遇到想查谁的考勤,直接使用存储过程就可以了,就不需要再重新写一遍SQL了,这就极大的提高了工作效率,美滋滋。
简单来说,就是定义一个函数,然后使用时调用。
需要两步:1)先定义存储过程 2)使用已经定义好的存储过程。
存储过程可以有参数,也可以没有参数,我们分开来讲。
(1)没有参数的存储过程
定义存储过程的语法形式:
create procedure 存储过程名称()begin <sql语句> ;end;
begin…end表示sql语句的开始和结束,相当于一个说明。语法里面的就是重复使用的sql语句。下面通过一个例子看下如何使用。
例如我们要查出“销售表”里的销售员姓名。
sql语句是:
select 姓名 from 销售表;
我们将这个sql语句放入存储过程的语法里,起个名字叫a_salor1:
create procedure a_salor1()begin select 姓名 from 销售表;end;
在navicat里创建以后,就可以在函数的位置看到它了:
下次要使用存储过程的时候我们只要call一下这个存储过程就可以,就不需要重新写一遍sql了。
call 存储过程名称();
(2)有参数的存储过程
前面的存储过程名称后面是(),括号里面没有参数,是空的。当括号里面有参数时,我们使用下面的语法:
create procedure 存储过程名称(参数1,参数2,...)begin <sql语句> ;end;
我们通过一个案例看下,现在要在“销售表”里查找出指定编号的姓名。如果指定销售员编号是10086,那么sql语句是:
select 姓名 from 销售表 where 编号='10086';
那么现在问题来了,一开始不知道指定编号是哪一个,只有使用的时候才知道业务需求。比如今天要查找编号10010,明天要查找编号10086。这时候就需要用到参数,来灵活应对这种情况:
create procedure salornum(num varchar(100))begin select 姓名 from 销售表 where 编号=num;end;
其中salornum是存储过程的名称,后面括号里面的num varchar(100)是参数,参数由2部分组成:参数名称是num;参数类型是是varchar(100),这里表示是字符串类型。
存储过程里面的sql语句(where 编号=num)使用了这个参数num。这样在使用存储过程的时候,给定参数的值就可以灵活的按实际的业务需求来查询了。
比如现在要查询编号=911的销售员姓名,那么就在使用存储过程的参数中给出编号的值,也就是下面括号里的911:
call salornum(911);
这样是不是方便了许多?
1)定义存储过程语法里的SQL语句代码块必须是完整的sql语句,而且必须用“;”结尾,一定要注意书写规范,否则一定会出错:
create procedure 存储过程名称(参数1,参数2,...)begin <sql语句> ;end;
2)定义不同的存储过程,要使用不同的存储过程名称,相同的存储过程的名字会引起系统报错。所有的表名、列名都是不能重复的。
在数据库中,我们将一条 SQL 语句称为一次基本的操作。将若干条 SQL 语句“打包”在一起,共同执行一个完整的任务,这就是事务。
事务( Transaction)由一次或者多次基本操作构成,或者说,事务由一条或者多条 SQL 语句构成。
事务有一个最显著的特征,就是它包含的所有 SQL 语句作为一个整体向数据库提交,只有所有的 SQL 语句都执行完成,整个事务才算成功,一旦某个 SQL 语句执行失败,整个事务就失败了。事务失败后需要回滚所有的 SQL 语句。
事务中的所有 SQL 语句是一个整体,共同进退,不可分割,要么全部执行成功,要么全部执行失败。
事务有很多实用的场景。例如对于电商网站,通常将用户订单存储在一张表中,将商品库存情况存储在另一张表中,当有用户下单时,需要执行两条 SQL 语句,一条负责更新订单表,一条负责更新库存表,这两条 SQL 语句必须同时执行成功。如果只有一条语句执行成功,另一条语句执行失败,将导致数据库出错,这种后果是无法接受的。
为了避免出现意外,可以将以上两条语句放到一个事务中,其中一条语句执行失败时,数据库将回滚到原来的状态。对于买家来说,数据库回滚会导致下单失败,但这很容易处理,让买家再次下单即可。数据库的正确性永远是最重要的。
其实我们平时使用数据库时,就已经在使用事务了,只不过这种事务只包含一条 SQL 语句,并且由数据库引擎自动封装和提交。这意味着,对于任何一条 SQL 语句,要么执行成功,要么执行失败,不能成功一部分,失败一部分。
一般来说,事务具有四个标准属性,分别是原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability),简称 ACID。具体说明如下:
为了更直观的解释ACID特性,下面先说明A, B, C之间互相转账的过程。
假设A有10元,B有15元,C有8元
A给B转账5元,操作记为T1。
T1: read(A), A=A-5, write(A), read(B), B=B+5, write(B)。
T1操作的大体流程,先读取A到账户余额,将A的账户余额扣减5元后再写入数据库中,
读取B的账户余额,将B的账户余额增加5元后再写入到数据库中。
同时,C给B转账4元,操作记为T2。
T2: read(C), C=C-4, write(C), read(B), B=B+4, write(B)
T1操作的大体流程,先读取C的账户余额,将C的账户余额扣减4元后再写入数据库中,
读取B的账户余额,将B的账户余额增加4元后再写入到数据库中。
一个事务中的所有 SQL 语句,要么全部执行成功,要么全部执行失败,不会结束在中间的某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行,是一个最小执行单元,不可分割。
A给B转账5元的操作T1是包含多个读写操作,这些操作要么全部执行,要么全部不执行。
假设由于断电等意外事件,导致T1只执行了部分操作,如T1:read(A), A=A-5, write(A)
这就会导致A凭空少了5元,并且B没有收到A转的5元,
因此事务需要保证保证在事务执行过程中出现错误时,将已经执行的操作“撤销”,恢复到原始状态。
在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的数据必须完全符合所有的预设规则,其中包含数据的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
事务应确保数据库的状态从一个一致状态转变为另一个一致状态。
假设A有10元,B有15元,C有8元,不管A, B, C之间如何进行转账(没有其他人参与),三个点账户总余额一定是33元,而不会是其它值。
数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
A给B转账5元,同时C给B转账4元,这个两个事务应该是互相隔离的,互不影响。
最终A余额为5元,C余额为4元,B总共收到两次转账,余额应该为24元。
假设T1, T2可以交叉执行,如下图所示。最终结果看起来B只收到了A的5元转账,余额为20元。
已被提交的事务对数据库的修改应该永久保存在数据库中。
事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
各种数据库对事务的支持细节不尽相同,本教程以 MySQL 数据库为例进行讲解,MySQL 使用标准 SQL 来支持事务。
与事务控制有关的 SQL 命令包括:
一个事务要么提交(Commit),要么回滚(Rollback),提交意味着成功,回滚意味着失败。编写事务代码时,以 BEGIN 命令开头,后跟一条或者多条 SQL 语句,最后书写 COMMIT 或者 ROLLBACK 命令;COMMIT 和 ROLLBACK 对应事务的两种状态,只能出现一个。
事务控制命令仅能与 DML 类别的 SQL 命令一起使用,包括 INSERT、UPDATE、DELETE 和 SELECT,在创建或者删除表时不能使用事务,因为这些操作在数据库中是自动提交的。
开始事务有以下两种命令,选择其一即可:
BEGIN;
或者
START TRANSACTION;
该命令用来标记一个事务的起始点。
提交事务使用如下命令:
COMMIT;
提交事务意味着真正执行事务包含的 SQL 语句,并把对数据库的修改写入到磁盘上的物理数据库中。COMMIT 意味着事务结束,并且执行成功。
例如,有包含如下记录的 CUSTOMERS 表:
±—±---------±----±----------±---------+
| ID | NAME | AGE | ADDRESS | SALARY |
±—±---------±----±----------±---------+
| 1 | Ramesh | 32 | Ahmedabad | 2000.00 |
| 2 | Khilan | 25 | Delhi | 1500.00 |
| 3 | kaushik | 23 | Kota | 2000.00 |
±—±---------±----±----------±---------+
打开一个 MySQL 命令行窗口(我们称它为 A 窗口),使用事务向表中插入两条数据:
mysql> BEGIN;
mysql> INSERT INTO CUSTOMERS (NAME, AGE, ADDRESS, SALARY) VALUES (‘Chaitali’, 25, ‘Mumbai’, 6500.00 );
mysql> INSERT INTO CUSTOMERS (NAME, AGE, ADDRESS, SALARY) VALUES (‘Hardik’, 27, ‘Bhopal’, 8500.00 );
再打开另外一个 MYSQL 命令行窗口(我们称它为 B 窗口),使用 SELECT 命令查看 CUSTOMERS 表的内容:
mysql> SELECT * FROM CUSTOMERS;
±—±--------±----±----------±-------+
| id | name | age | address | salary |
±—±--------±----±----------±-------+
| 1 | Ramesh | 32 | Ahmedabad | 2000 |
| 2 | Khilan | 25 | Delhi | 1500 |
| 3 | Kaushik | 23 | Kota | 2000 |
±—±--------±----±----------±-------+
您看,A 窗口对表的修改并没有影响到 B 窗口,B 窗口只能查看到修改之前的内容,这说明 A 窗口的修改动作并没有立即更新到物理数据库,所以在其它会话窗口中无法看到。
在 A 窗口中提交事务:
mysql> COMMIT;
在 B 窗口中再次查看 CUSTOMERS 表的内容:
mysql> SELECT * FROM CUSTOMERS;
±—±---------±----±----------±-------+
| id | name | age | address | salary |
±—±---------±----±----------±-------+
| 1 | Ramesh | 32 | Ahmedabad | 2000 |
| 2 | Khilan | 25 | Delhi | 1500 |
| 3 | Kaushik | 23 | Kota | 2000 |
| 4 | Chaitali | 25 | Mumbai | 6500 |
| 5 | Hardik | 27 | Bhopal | 8500 |
±—±---------±----±----------±-------+
您看,在 A 窗口提交事务以后,对表所做的修改才真正更新到物理数据库中,所以才能在其它会话窗口中查看到 A 窗口中插入的数据。
回滚意味着撤销尚未保存到物理数据库中的操作,具体语法格式如下:
ROLLBACK;
事务执行过程中如果发生了某种故障,事务不能继续执行,就可以撤销事务,此时对数据库的修改并不会保存到物理数据库中。撤销意味着事务结束,并且执行失败。
例如,有包含如下记录的 CUSTOMERS 表:
±—±---------±----±----------±-------+
| id | name | age | address | salary |
±—±---------±----±----------±-------+
| 1 | Ramesh | 32 | Ahmedabad | 2000 |
| 2 | Khilan | 25 | Delhi | 1500 |
| 3 | Kaushik | 23 | Kota | 2000 |
| 4 | Chaitali | 25 | Mumbai | 6500 |
| 5 | Hardik | 27 | Bhopal | 8500 |
±—±---------±----±----------±-------+
使用事务删除最后两个用户,并回滚:
mysql> BEGIN;
mysql> DELETE FROM CUSTOMERS WHERE ID=4;
mysql> DELETE FROM CUSTOMERS WHERE ID=5;
mysql> ROLLBACK;
mysql> SELECT * FROM CUSTOMERS;
±—±---------±----±----------±-------+
| id | name | age | address | salary |
±—±---------±----±----------±-------+
| 1 | Ramesh | 32 | Ahmedabad | 2000 |
| 2 | Khilan | 25 | Delhi | 1500 |
| 3 | Kaushik | 23 | Kota | 2000 |
| 4 | Chaitali | 25 | Mumbai | 6500 |
| 5 | Hardik | 27 | Bhopal | 8500 |
±—±---------±----±----------±-------+
您看,回滚事务以后,物理数据库中的数据并没有发生改变,表中的内容和事务执行之前的内容一致。
ROLLBACK 命令默认回滚整个事务,也即事务中的所有修改操作都无效。但是 SQL 允许回滚事务的一部分,此时需要在事务中设置一个标记点,在该标记点之后的 SQL 语句将被回滚,之前的 SQL 语句将被成功执行。
设置标记点使用 SAVEPOINT 命令,具体语法如下:
SAVEPOINT point_name;
point_name 为标记点名字。
回滚到标记点使用 ROLLBACK TO 命令,具体语法如下:
ROLLBACK TO point_name;
例如,有包含如下记录的 CUSTOMERS 表:
±—±---------±----±----------±-------+
| id | name | age | address | salary |
±—±---------±----±----------±-------+
| 1 | Ramesh | 32 | Ahmedabad | 2000 |
| 2 | Khilan | 25 | Delhi | 1500 |
| 3 | Kaushik | 23 | Kota | 2000 |
| 4 | Chaitali | 25 | Mumbai | 6500 |
| 5 | Hardik | 27 | Bhopal | 8500 |
±—±---------±----±----------±-------+
使用事务删除最后两个用户,并回滚到标记点:
mysql> BEGIN;
mysql> DELETE FROM CUSTOMERS WHERE ID=4;
mysql> SAVEPOINT sp;
mysql> DELETE FROM CUSTOMERS WHERE ID=5;
mysql> ROLLBACK TO sp;
mysql> SELECT * FROM CUSTOMERS;
±—±--------±----±----------±-------+
| id | name | age | address | salary |
±—±--------±----±----------±-------+
| 1 | Ramesh | 32 | Ahmedabad | 2000 |
| 2 | Khilan | 25 | Delhi | 1500 |
| 3 | Kaushik | 23 | Kota | 2000 |
| 5 | Hardik | 27 | Bhopal | 8500 |
±—±--------±----±----------±-------+
您看,我们回滚到标记点 sp,只有 ID 为 4 的用户被删除,ID 为 5 的用户依然留在数据库中。
1.count函数
该函数将返回满足where条件子句中记录的个数
select count(*) from 表名
或者是这样写:
select count(*) from 表名 where 条件
2.sum函数,返回某一列的所有数值的和
select sum(字段名) from 表名
3.avg函数,它可以返回某一列的平均值
select avg(字段名) from 表名
4.max函数,它可以返回某一列中的最大数值
select max(字段名) from 表名
5.min函数,它可以返回一列中的最小数值
select min(字段名) from 表名
6.variance(方差)函数,求某一列的方差
select variance(字段名) from 表名
7.stddev函数,求某一列数值的标准差
select stddev(字段名) from 表名
8. add_months函数,该函数的功能是将给定的日期增加一个月
select add_months(日期字段) from 表名
9.last_day函数,该函数可以返回指定月份的最后一天
select last_day(日期字段名) from 表名
10.months_between函数,该函数可以计算给定的两个日期中有多少个月
select months_between(日期字段名,日期字段名) from 表名
11.new_time函数,该函数可以将表中字段的时间
调整成对应时区的时间,
select new_time(要调整时间的字段名) from 表名
12.next_day函数,该函数返回指定日期所在同一个星期或之后一个星期内,所求的星期天数的日期
select 日期字段名,next_day(日期字段名,要求的星期日期) from 表名
13.sysdate函数,该函数将返回系统的日期和时间,此处演示使用distinct将重复的数据过滤
select distinct sysdate from 表名
14.abs函数,该函数返回给定数字的绝对值,
select abs(要求绝对值的字段名) from 表名
15.ceil函数和floor函数,ceil函数是返回与给定参数相等或比给定参数大的最小整数,floor函数是返回与给定参数相等或比给定参数小的最大整数。
select ceil(字段名) from 表名
select floor(字段名) from 表名
16.cos, cosh, sin, sinh, tan, tanh这些函数可以返回给定参数的三角函数值,默认的参数认定是弧度制,由于360角度为2个弧度,所以计算的时候需要将参数乘上0.0174…才可得到我们所熟悉的角度数值
select cos(字段名) from 表名
17.exp函数,该函数会返回以给定参数为指数,e为底数的幂值
select exp(字段名) from 表名
18.ln函数和log函数,这是两个对数函数,ln函数是以e为底的对数函数,使用方法如下
select ln(abs(字段名)) from 表名
log函数可以指定以什么为底,如下所示:
select log(字段名,底值) from 表名
19.mod函数,该函数的作用是求两数相除后的余数
select mod(字段名,字段名) from 表名
20.power函数,返回某一个数对另一个数的幂,第一个数为底数,第二个数为指数
select power(字段名,字段名) from 表名
21.sign函数,判断参数是正数还是负数,负数是返回-1,正数时返回1,零时返回零
select sign(字段名) from 表名
22.sqrt函数,该函数返回参数的平方根,注意该函数参数不能为负数
select sqrt(字段名) from 表名
23.chr函数,该函数返回与参数数值相同的字符,返回的字符取决于数据库所依赖的字符集编码
select chr("字段名") from 表名
24.concat函数,将多个字符串连接成一个字符串。
select concat (id, name, score) as info from tt2;
25.translate函数,translate(a,b,c),三个参数分别是目标字符串(a),源字符串(b)和目的字符串©,该函数的作用是将a与b中相同的字符串都替换成字符串c
14.abs函数,该函数返回给定数字的绝对值,
select abs(要求绝对值的字段名) from 表名
15.ceil函数和floor函数,ceil函数是返回与给定参数相等或比给定参数大的最小整数,floor函数是返回与给定参数相等或比给定参数小的最大整数。
select ceil(字段名) from 表名
select floor(字段名) from 表名
16.cos, cosh, sin, sinh, tan, tanh这些函数可以返回给定参数的三角函数值,默认的参数认定是弧度制,由于360角度为2个弧度,所以计算的时候需要将参数乘上0.0174…才可得到我们所熟悉的角度数值
select cos(字段名) from 表名
17.exp函数,该函数会返回以给定参数为指数,e为底数的幂值
select exp(字段名) from 表名
18.ln函数和log函数,这是两个对数函数,ln函数是以e为底的对数函数,使用方法如下
select ln(abs(字段名)) from 表名
log函数可以指定以什么为底,如下所示:
select log(字段名,底值) from 表名
19.mod函数,该函数的作用是求两数相除后的余数
select mod(字段名,字段名) from 表名
20.power函数,返回某一个数对另一个数的幂,第一个数为底数,第二个数为指数
select power(字段名,字段名) from 表名
21.sign函数,判断参数是正数还是负数,负数是返回-1,正数时返回1,零时返回零
select sign(字段名) from 表名
22.sqrt函数,该函数返回参数的平方根,注意该函数参数不能为负数
select sqrt(字段名) from 表名
23.chr函数,该函数返回与参数数值相同的字符,返回的字符取决于数据库所依赖的字符集编码
select chr("字段名") from 表名
24.concat函数,将多个字符串连接成一个字符串。
select concat (id, name, score) as info from tt2;
25.translate函数,translate(a,b,c),三个参数分别是目标字符串(a),源字符串(b)和目的字符串©,该函数的作用是将a与b中相同的字符串都替换成字符串c
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。