MySQL必知必会笔记(2)
参考书籍
MySQL数据类型[补充]
1.串数据类型
最常用的数据类型是串数据类型。它们存储串,如名字、地址、电话号码、邮政编码等。有两种基本的串类型,分别为定长串和变长串(参见表1)。定长串的处理速度比变长串快。
数据类型 | 说明 |
---|---|
char |
1~255个字符的定长串。它的长度必须在创建时指定,否则MySQL 假定为CHAR(1) |
varchar |
长度可变,最多不超过255 字节。如果在创建时指定为varchar(n) ,则可存储0 到n 个字符的变长串(其中n≤255 ) |
enum |
接受最多65535 个串组成的一个预定义集合的某个串 |
text |
最大长度为64K 的变长文本 |
tinytext |
与text 相同,但最大长度为255 字节 |
mediumtext |
与text 相同,但最大长度为16K |
longtext |
与text 相同,但最大长度为4GB |
set |
接受最多65535 个串组成的一个预定义集合的零个或多个串 |
2.数值数据类型
所有数值数据类型(除BIT
和BOOLEAN
外)都可以有符号或无符号。
有符号数值列可以存储正或负的数值,无符号数值列只能存储正数。
默认情况为有符号,但如果你知道自己不需要存储负值,可以使用UNSIGNED
关键字,这样做将允许你存储两倍大小的值。
数据类型 | 说明 |
---|---|
bit |
位字段,1~ 64位 |
boolean (或bool ) |
布尔值,取0 或1 |
decimal (或dec ) |
精度可变的浮点值 |
double |
双精度浮点值 |
float |
单精度浮点值 |
bigint |
整数值,支持9223372036854775808~9223372036854775807 (如果是UNSIGNED ,为0~18446744073709551615 )的数 |
integer (或int ) |
整数值,支持-2147483648~2147483647 (如果是UNSIGNED ,为0~4294967295 )的数 |
mediumint |
整数值,支持-8388608~8388607 (如果是UNSIGNED ,为0~16777215 )的数 |
smallint |
整数值,支持-32768~32767 (如果是UNSIGNED ,为0~65535 )的数 |
tinyint |
整数值,支持-128~127 (如果为UNSIGNED ,为0~255 )的数 |
real |
4字节的浮点值 |
3.日期和时间数据类型
数据类型 | 说明 |
---|---|
date |
表示1000-01-01~9999-12-31 的日期,格式为YYYY-MM-DD |
datetime |
date 和time 的组合 |
timestamp |
功能和datetime 相同(但范围较小,1970-01-01 00:00:01~2038-01-19 03:14:07 ) |
time |
格式为HH:MM:SS |
year |
用2位数字表示,范围是70 (1970 年)~69 (2069 年),用4位数字表示,范围是1901 年~2155 年 |
4.二进制数据类型
数据类型 | 说明 |
---|---|
BLOB |
Blob 最大长度为64KB |
MEDIUMBLOB |
Blob 最大长度为16MB |
LONGBLOB |
Blob 最大长度为4GB |
TINYBLOB |
Blob 最大长度为255 字节 |
MySQL保留字[补充]
全文本搜索
并非所有引擎都支持全文本搜索。
两种最常使用的引擎为InnoDB
和MyISAM
,前者支持全文本搜索,而后者不支持。
全文本搜索不区分大小写。
全文本搜索会计算出一个等级值,匹配文本越靠前,等级值越大,返回的结果中越靠前
如果指定多个搜索项,则包含多数匹配词的那些行将具有比包含较少词(或仅有一个匹配)的那些行更高的等级值。
# 1.支持全文本搜索
# 建表时指定要进行索引的列以支持全文本搜索
# MySQL根据子句FULLTEXT(note_text)的指示对它进行索引。
# 这里的FULLTEXT索引单个列,如果需要也可以指定多个列。
# 不要在导入数据时使用FULLTEXT,更新索引需要时间,可以在导入数据后再修改表
CREATE TABLE productnotes
(
note_id int NOT NULL AUTO_INCREMENT,
prod_id char(10) NOT NULL,
note_date datetime NOT NULL,
note_text text NULL ,
PRIMARY KEY(note_id),
FULLTEXT(note_text)
) ENGINE=MyISAM;
# 2.进行全文本搜索
# 在索引之后,使用两个函数Match()和Against()执行全文本搜索
# Match()指定被搜索的列
# Against()指定要使用的搜索表达式。Against('rabbit')指定词rabbit作为搜索文本
select note_text from productnotes where match(note_text) against('rabbit');
# 上面的简单任务也可以使用like语句完成
select note_text from productnotes where note_text like '%rabbit%';
# rank是MySQL版本8.0.2中定义的MySQL保留字!!!不能使用rank作为列名
# 当字段名与MySQL保留字冲突时,可以用字符''将字段名括起来
# 或者改为其他名字,比如as rank1等
# 计算全文本搜索结果的等级值
select note_text, match(note_text) against('rabbit') as 'rank' from productnotes;
# 全文本搜索排除等级为0的行,并且搜索结果按照等级值降序排列
# 3.查询拓展
# 进行一个简单的全文本搜索,没有查询扩展
select note_text from productnotes where match(note_text) against('anvils');
# 相同的搜索,这次使用查询扩展
select note_text from productnotes where match(note_text) against('anvils' with query expansion);
布尔文本搜索
布尔操作符 | 说明 |
---|---|
+ |
包含,词必须存在 |
- |
排除,词必须不出现 |
> |
包含,而且增加等级值 |
< |
包含,且减少等级值 |
() |
把词组成子表达式(允许这些表达式作为一个组被包含、排除、排列等) |
~ |
取消一个词的排序值 |
* |
词尾的通配符 |
"" |
定义一个短语(与单个词的列表不一样,它匹配整个短语一边包含或排除这个短语) |
在布尔方式中,不按等级值降序排序返回的行。
# 全文本搜索检索包含词heavy的所有行
# 关键字IN BOOLEAN MODE,实际上没有指定布尔操作符,其结果与没有指定布尔方式的结果相同
select note_text from productnotes where match(note_text) against('heavy' in boolean mode);
# -rope* 排除包含以rope开始的词(如ropes)的行
select note_text from productnotes where match(note_text) against('heavy -rope*' in boolean mode);
# 匹配包含词rabbit和bait的行
select note_text from productnotes where match(note_text) against('+rabbit +bait' in boolean mode);
# 不指定操作符,搜索匹配包含rabbit和bait中的至少一个词的行
select note_text from productnotes where match(note_text) against('rabbit bait' in boolean mode);
# 搜索匹配短语rabbit bait而不是匹配两个词rabbit和bait。
select note_text from productnotes where match(note_text) against('"rabbit bait"' in boolean mode);
# 匹配rabbit和carrot,增加前者的等级,降低后者的等级
select note_text from productnotes where match(note_text) against('>rabbit <carrot' in boolean mode);
# 必须匹配词safe和combination,降低后者的等级
select note_text from productnotes where match(note_text) against('+safe +(<combination)' in boolean mode);
全文本搜索的一些说明
- 在索引全文本数据时,短词被忽略且从索引中排除。短词定义为那些具有3个或3个以下字符的词(如果需要,这个数目可以更改)。
- MySQL带有一个内建的非用词(
stopword
)列表,这些词在索引全文本数据时总是被忽略。如果需要,可以覆盖这个列表(stopword
列表详见这里)。 - 许多词出现的频率很高,搜索它们没有用处(返回太多的结果)。因此,MySQL规定了一条
50%
规则,如果一个词出现在50%
以上的行中,则将它作为一个非用词忽略。50%
规则不用于in boolean mode
。 - 如果表中的行数少于3行,则全文本搜索不返回结果(因为每个词或者不出现,或者至少出现在50%的行中)。
- 忽略词中的单引号。例如,
don't
索引为dont
。 - 不具有词分隔符(包括日语和汉语)的语言不能恰当地返回全文本搜索结果。
- 如前所述,仅在
MyISAM
数据库引擎中支持全文本搜索。 - 暂不支持邻近操作符。
插入数据
INSERT
是用来插入(或添加)行到数据库表的。
插入可以用几种方式使用:
- 插入完整的行
- 插入行的一部分
- 插入多行
- 插入某些查询的结果
本章代码
-- 插入完整的行
# 插入一个新客户到customers表
# 简单但不安全,如果原来表列结构调整,会有问题
insert into customers values(null,'Pep E. LaPew','100 Main Street','Los Angeles','CA','90046','USA',NULL,NULL);
# 表明括号内明确列名,更安全,稍繁琐
# 可以忽略某些列,比如cust_id
# 忽略的列必须满足下面某个条件
# 1.该列定义为允许NULL值(无值或空值)。
# 2.在表定义中给出默认值。这表示如果不给出值,将使用默认值。
insert into customers (cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country,cust_contact,cust_email)
values ('Pep E. LaPew','100 Main Street','Los Angeles','CA','90046','USA',NULL,NULL);
-- 插入多个行
# 方法1: 提交多个insert 语句
insert into customers(cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country)
values('Pep E. LaPew','100 Main Street','Los Angeles','CA','90046','USA');
insert into customers(cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country)
values('M. Martian','42 Galaxy Way','New York','NY','11213','USA');
# 方法2: 只要每条INSERT语句中的列名(和次序)相同,可以如下组合各语句
# 单条INSERT语句有多组值,每组值用一对圆括号括起来,用逗号分隔
insert into customers(cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country)
values('Pep E. LaPew','100 Main Street','Los Angeles','CA','90046','USA'),('M. Martian','42 Galaxy Way','New York','NY','11213','USA');
-- 插入检索出的数据
-- INSERT还存在另一种形式,可以利用它将一条SELECT语句的结果插入表中。
-- 建立一张custom_new表
CREATE TABLE `custnew` (
`cust_id` int(11) NOT NULL AUTO_INCREMENT,
`cust_name` char(50) NOT NULL,
`cust_address` char(50) DEFAULT NULL,
`cust_city` char(50) DEFAULT NULL,
`cust_state` char(5) DEFAULT NULL,
`cust_zip` char(10) DEFAULT NULL,
`cust_country` char(50) DEFAULT NULL,
`cust_contact` char(50) DEFAULT NULL,
`cust_email` char(255) DEFAULT NULL,
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB;
# 将customers表的数据导入custom_new表中
# 这里省略了custs_id,这样MySQL就会重新生成新值。
insert into custnew (cust_contact,cust_email,cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country)
select cust_contact,cust_email,cust_name,cust_address,cust_city,cust_state,cust_zip,cust_country from customers;
更新和删除数据
UPDATE
语句更新多行,并且在更新这些行中的一行或多行时出一个现错误,则整个UPDATE
操作被取消(错误发生前更新的所有行被恢复到它们原来的值)。为了即使是发生错误,也继续进行更新,可使用IGNORE
关键字,如下所示:UPDATE IGNORE customers...
本章代码
-- update语句 : 删除或更新指定列
# 更新某个列,比如客户10005现在有了电子邮件地址
update customers set cust_email = 'elmer@fudd.com' where cust_id = 10005;
# 更新多个列
UPDATE customers
SET cust_name = 'The Fudds',
cust_email = 'elmer@fudd.com'
WHERE cust_id = 10005;
# 删除某个列的值,可设置它为NULL(假如表定义允许NULL值)
update customers set cust_email = null where cust_id = 10005;
-- delete 语句: 删除整行而不是某列
# 从customers表中删除一行
delete from customers where cust_id = 10006;
-- 删除custnew表中所有行
delete from custnew;
-- truncate table语句
# 如果想从表中删除所有行,不要使用DELETE,可使用TRUNCATE TABLE语句
# TRUNCATE实际是删除原来的表并重新创建一个表,而不是逐行删除表中的数据
# 删除cust_new表
TRUNCATE TABLE custnew;
一些使用习惯
- 除非确实打算更新和删除每一行,否则绝对不要使用不带
WHERE
子句的UPDATE
或DELETE
语句。 - 保证每个表都有主键。
- 在对
UPDATE
或DELETE
语句使用wHERE
子句前,应该先用SELECT
进行测试,保证它过滤的是正确的记录,以防编写的WHERE
子句不正确。 - 使用强制实施引用完整性的数据库,这样
MySQL
将不允许删除具有与其他表相关联的数据的行。
创建和操纵表
如果你仅想在一个表不存在时创建它,应该在表名前给出IF NOT EXISTS
。
这样做不检查已有表的模式是否与你打算创建的表模式相匹配。它只是查看表名是否存在,并且仅在表名不存在时创建它。
NULL值就是没有值或缺值。允许NULL值的列也允许在插入行时不给出该列的值。不允许NULL值的列不接受该列没有值的行,换句话说,在插入或更新行时,该列必须有值。
NULL值不等于''
(空字符串)
MySQL中默认列允许NULL值
AUTO_INCREMENT
AUTO_INCREMENT
告诉MySQL
,本列每当增加一行时自动增量。每次执行一个INSERT
操作时,MySQL自动对该列增量,给该列赋予下一个可用的值。这样给每个行的某列分配一个唯一的列值,从而该列可以用作主键。每个列只允许一个AUTO_INCREMENT
列,而且它必须被索引。
AUTO_INCRE-MENT
,如何使用特殊的值?你可以简单地在INSERT语句中指定一个值,只要它是唯一的(至今尚未使用过)即可,该值将被用来替代自动生成的值。后续的增量将开始使用该手工插入的值指定默认值
如果在插入行时没有给出值,MySQL允许指定此时使用的默认值。如orderitems
指定quantity
列的值默认为1
。
CREATE TABLE orderitems
(
order_num int NOT NULL ,
order_item int NOT NULL ,
prod_id char(10) NOT NULL ,
quantity int NOT NULL DEFAULT 1,
item_price decimal(8,2) NOT NULL ,
PRIMARY KEY (order_num, order_item)
) ENGINE=InnoDB;
DBMS
不一样,MySQL
不允许使用函数作为默认值,它只支持常量。引擎类型
MySQL
与其他DBMS
不一样,它具有多种引擎。它打包多个引擎,这些引擎都隐藏在MySQL服务器内,全都能执行CREATE TABLE
和SELECT
等命令。
# 查看默认使用的引擎类型
SHOW VARIABLES LIKE 'default_storage_engine%';
InnoDB
是一个可靠的事务处理引擎,它不支持全文本搜索 (默认使用该引擎)MEMORY
在功能等同于MyISAM
,但由于数据存储在内存(不是磁盘)中,速度很快(特别适合于临时表);MyISAM
是一个性能极高的引擎,它支持全文本搜索,但不支持事务处理。
本章代码
-- 新建customers表
CREATE TABLE IF NOT EXISTS customers
(
cust_id int NOT NULL AUTO_INCREMENT,
cust_name char(50) NOT NULL ,
cust_address char(50) NULL ,
cust_city char(50) NULL ,
cust_state char(5) NULL ,
cust_zip char(10) NULL ,
cust_country char(50) NULL ,
cust_contact char(50) NULL ,
cust_email char(255) NULL ,
PRIMARY KEY (cust_id)
) ENGINE=InnoDB;
-- 更新表 alter table
# 给vendors表增加一个名为vend_phone的列
alter table vendors
add vend_phone char(20);
# 删除刚刚添加的列
alter table vendors
drop column vend_phone;
# ALTER TABLE的一种常见用途是定义外键
# 以下为书本配套文件create.sql中定义外键的语句
# 使用ALTER TABLE语句前记得做好备份! ! !
ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_orders FOREIGN KEY (order_num) REFERENCES orders (order_num);
ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_products FOREIGN KEY (prod_id) REFERENCES products (prod_id);
ALTER TABLE orders ADD CONSTRAINT fk_orders_customers FOREIGN KEY (cust_id) REFERENCES customers (cust_id);
ALTER TABLE products ADD CONSTRAINT fk_products_vendors FOREIGN KEY (vend_id) REFERENCES vendors (vend_id);
-- 删除表
# 删除customers2表(假设它存在),删除整张表而不单单是其内容
drop table customers2;
-- 重命名表
# 使用RENAME TABLE语句可以重命名一个表 (假设存在下述表)
rename table customers2 to customers;
# 对多个表重命名(假设存在下述表)
rename table backup_customers to customer,
backup_vendors to vendors,
backup_products to products;
视图
视图是一种虚拟表。它们包含的不是数据而是根据需要检索数据的查询。
视图提供了一种MySQL的SELECT语句层次的封装,可用来简化数据处理以及重新格式化基础数据或保护基础数据。
视图的一些规则和限制
- 与表一样,视图必须唯一命名(不能给视图取与别的视图或表相同的名字)。
- 对于可以创建的视图数目没有限制。
- 为了创建视图,必须具有足够的访问权限。这些限制通常由数据库管理人员授予。
- 视图可以嵌套,即可以利用从其他视图中检索数据的查询来构造一个视图。
ORDER BY
可以用在视图中,但如果从该视图检索数据SELECT
中也 含有ORDER BY
,那么该视图中的ORDER BY
将被覆盖。- 视图不能索引,也不能有关联的触发器或默认值。
- 视图可以和表一起使用。例如,编写一条联结表和视图的
SELECT
语句。
/*视图提供了一种MySQL的SELECT语句层次的封装,可用来简化数据处理以及重新格式化基础数据或保护基础数据。 */
-- 创建视图 create view
-- 查看已创建视图的语句 show create view viewname
-- 删除视图 drop view viewname
-- 更新视图(有下面两种方式)
-- 1. 先drop后create
-- 2. 直接使用create or repalce view
# 创建一个名为productcustomers的视图
# 保存了订购了任意产品的所有客户数据
create view productcustomers as
select cust_name,cust_contact,prod_id
from customers,orders,orderitems
where customers.cust_id = orders.cust_id
and orders.order_num = orderitems.order_num;
# 检索订购了产品TNT2的客户
select cust_name,cust_contact from productcustomers where prod_id = 'TNT2';
# 用视图重新格式化检索出的数据
# (来自第10章)在单个组合计算列中返回供应商名和位置
select concat(rtrim(vend_name),' (',rtrim(vend_country),')') as vend_title from vendors order by vend_name;
# 若经常使用上述格式组合,可以创建视图
create view vendorlocations as
select concat(rtrim(vend_name),' (',rtrim(vend_country),')') as vend_title from vendors order by vend_name;
# 检索出以创建所有邮件标签的数据
select * from vendorlocations;
# 用视图过滤不想要的数据
# 定义customeremaillist视图,它过滤没有电子邮件地址的客户
create view customeremaillist as
select cust_id,cust_name,cust_email from customers
where cust_email is not null;
select * from customeremaillist;
# 使用视图与计算字段
# (来自第10章)检索某个特定订单中的物品,计算每种物品的总价格
select prod_id,quantity,item_price,quantity*item_price as expanded_price from orderitems where order_num = 20005;
# 将其转换为一个视图
create view orderitemsexpanded as
select order_num,prod_id,quantity,item_price,quantity*item_price as expanded_price from orderitems;
# 创建视图的时候select添加了列名order_num,否则无法按照order_num进行过滤查找
select * from orderitemsexpanded where order_num = 20005;
更新视图的限制
GROUP BY
HAVING
;- 表的连接;
- 子查询;
- 并(
UNION
orUNION ALL
); - 聚集函数(
Min()
、count()
、Sum()
等); DISTINCT
;- 导出(计算)列。
如果视图有以上操作,则不能进行更新!
存储过程
什么是存储过程?
一组可编程的函数,是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
创建存储过程
;
与MySQL
语句末尾的;
发生解释冲突,解决办法是临时更改命令行中的语句分隔符,比如使用//
进行分隔DELIMITER // # 临时修改语句分隔符
CREATE PROCEDURE productpricing()
BEGIN
SELECT Avg(prod_price) AS priceaverage
FROM products;
END //
DELIMITER ; # 修改回去
正常创建
# 存储过程定义
# ()中可以定义传入的参数
CREATE PROCEDURE productpricing()
# 存储过程体
BEGIN
SELECT Avg(prod_price) AS priceaverage
FROM products;
END
调用存储过程
call productpricing();
删除存储过程
-- 删除存储过程,请注意:没有使用后面的(),只给出存储过程名。
drop procedure productpricing;
-- 当存储过程不存在进行删除时不报错可以使用下面的语句
drop procedure if exists productpricing;
使用参数
MySQL
支持IN
(传递给存储过程)、OUT
(从存储过程传出,如这里所用)和INOUT
(对存储过程传入和传出)类型的参数。
使用OUT参数
关键字OUT
指出相应的参数用来从存储过程传出一个值(返回给调用者)。
存储过程的代码位于BEGIN和END语句内,如前所见,它们是一系列SELECT
语句,用来检索值,然后保存到相应的变量(通过指定INTO
关键字)。
带参数版的productpricing
:调用之后返回3个值,分别是产品的最低价格、最高价格、平均价格。
create procedure productpricing(
out pl decimal(8,2), # 返回的结果保留两位小数
out ph decimal(8,2),
out pa decimal(8,2)
)
begin
select min(prod_price) into pl from products;
select max(prod_price) into ph from products;
select avg(prod_price) into pa from products;
end
# 所有MySQL变量必须以@开头
# 使用三个变量保存调用传出的结果
call productpricing(@pricelow,@pricehigh,@priceaverage);
# 查看结果
select @pricelow,@pricehigh,@priceaverage;
使用IN和OUT参数
# 使用IN和OUT参数,存储过程ordertotal接受订单号并返回该订单的合计
create procedure ordertotal(
in onumber int, # onumber定义为IN,因为订单号被传入存储过程
out ototal decimal(8,2) # ototal为OUT,因为要从存储过程返回合计
)
begin
select sum(item_price*quantity) from orderitems
where order_num = onumber
into ototal;
end
# 传入订单号获得总价
call ordertotal(20005,@total);
# 显示总价
select @total;
智能存储过程
-- 存储过程名:ordertotal
-- 参数: onumber: 订单号
-- taxable:值为1时需要纳税,为0时不需要
-- ototal:最后的合计
create procedure ordertotal(
in onumber int,
in taxable boolean,
out ototal decimal(8,2)
) comment 'obtain order total, optionally adding tax'
begin
-- 定义局部变量total
declare total decimal(8,2);
-- 定义局部变量税率(局部变量支持默认值)
declare taxrate int default 6;
-- 根据输入变量onumber获得订单合计,传递给局部变量total
SELECT SUM(item_price * quantity)
FROM orderitems
WHERE order_num = onumber INTO total;
-- 是否要增加营业税?
if taxable then
-- 需要, 给订单合计增加税率
select total+(total/100*taxrate) into total;
end if;
-- 最后,传递给输出变量ototal
SELECT total INTO ototal;
end
# 调用上述存储过程,不加税
call ordertotal(20005,0,@total);
select @total;
# 调用上述存储过程,加税
call ordertotal(20005,1,@total);
select @total;
显示存储过程
# 显示创建某个存储过程的CREATE语句
show create procedure ordertotal;
# 获得包括何时、由谁创建等详细信息的存储过程列表
# 该语句会列出MySQL中所有存储过程
show procedure status;
# 过滤模式:只展示ordertotal这个存储过程
show procedure status like 'ordertotal';
游标
DBMS
,MySQL
游标只能用于存储过程(和函数)。游标(cursor
),是一个存储在MySQL服务器上的数据库查询,游标不是一条SELECT
语句,而是被该语句检索出来的结果集;可以看做是指向查询结果集的指针;通过cursor
,就可以一次一行的从结果集中把行拿出来处理。
游标的处理过程(4步)
- 声明游标(declare):
declare 游标名称 cursor for table(或查询的结果集);
- 打开游标(open):
open 游标名称
- 检索游标(fetch):利用游标获取对应行的数据,
fetch 游标名称 into 局部变量
- 关闭游标(close):
close 游标名称
如果没有明确关闭游标,MySQL
将会在到达END
语句时自动关闭它。
-- 创建、打开、关闭游标
# 定义名为ordernumbers的游标,检索所有订单
# 游标定义在存储过程中,存储过程处理完成之后,游标就消失了
create procedure processorders()
begin
-- decalre the cursor 声明游标
declare ordernumbers cursor
for
select order_num from orders;
-- open the cursor 打开游标
open ordernumbers;
-- close the cursor 关闭游标
close ordernumbers;
end
-- 使用游标数据的几个例子
# 例1:检索当前行的order_num列,对数据不做实际处理
create procedure processorders()
begin
-- 声明局部变量
declare o int;
-- 声明游标
declare ordernumbers cursor
for
select order_num from orders;
-- 打开游标
open ordernumbers;
-- 获得订单号
fetch ordernumbers into o;
/*fetch检索当前行的order_num列(将自动从第一行开始)到一个名为o的局部声明变量中。
对检索出的数据不做任何处理。*/
-- 关闭游标
close ordernumbers;
end
# 例2:循环检索数据,从第一行到最后一行,对数据不做实际处理
create procedure processorders()
begin
-- 声明局部变量
declare done boolean default false;
declare o int;
-- 声明游标
declare ordernumbers cursor
for
select order_num from orders;
--
declare continue handler for sqlstate '02000' set done = true;
-- SQLSTATE '02000'是一个未找到条件,当REPEAT由于没有更多的行供循环而不能继续时,出现这个条件。
-- 打开游标
open ordernumbers;
-- 遍历所有行
repeat
-- 获得订单号
fetch ordernumbers into o;
-- FETCH在REPEAT内,因此它反复执行直到done为真
until done end repeat;
-- 关闭游标
close ordernumbers;
end
# 例3:循环检索数据,从第一行到最后一行,对取出的数据进行某种实际的处理
create procedure processorders()
begin
-- 声明局部变量
declare done boolean default 0;
declare o int;
declare t decimal(8,2);
-- 声明游标
declare ordernumbers cursor
for
select order_num from orders;
-- declare continue handler
declare continue handler for sqlstate '02000' set done = 1;
-- 新建表以保存数据(订单号和总价)
create table if not exists ordertotals
(order_num int,total decimal(8,2));
-- 打开游标
open ordernumbers;
-- 遍历所有行
repeat
-- 获取订单号
fetch ordernumbers into o;
-- 计算订单金额
call ordertotal(o,1,t); # 直接调用之前创建的ordertotal存储过程
-- 将订单号、金额插入表ordertotals内
insert into ordertotals(order_num,total) values(o,t);
-- end of loop
until done end repeat;
-- 关闭游标
close ordernumbers;
end
# 调用存储过程 precessorders()
call processorders();
# 输出结果
select * from ordertotals;
触发器
什么是触发器
触发器是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行操作(
INSERT
,DELETE
,UPDATE
)时就会激活它执行。简单理解为:你执行一条sql语句,这条sql语句的执行会自动去触发执行其他的sql语句。
1.点击数据库
2.双击表名,查看表数据
3.使用快捷键ctrl+d
打开表设计,点击触发器选项卡
创建触发器
创建触发器必须给出以下4条信息:
-
唯一的触发器名
-
触发器关联的表
-
触发器应该响应的活动(
INSERT
、DELETE
或UPDATE
) -
触发器何时执行(处理之前或之后)
触发器按每个表每个事件每次地定义,**每个表每个事件每次只允许一个触发器。**因此,每个表最多支持6个触发器(每条INSERT
、UPDATE
和DELETE
的之前和之后)。单一触发器不能与多个表和多个事件关联。
触发器使用create trigger
语句创建,比如
create trigger newproduct after insert on products
for each row
begin
select 'product added' into @new_pro;
end
删除触发器
drop trigger newproduct;
insert触发器
- 在
INSERT
触发器代码内,可引用一个名为NEW
的虚拟表,访问被插入的行 - 在
BEFORE INSERT
触发器中,NEW
中的值也可以被更新(允许更改被插入的值) - 对于
AUTO_INCREMENT
列,NEW
在INSERT
执行之前包含0
,在INSERT
执行之后包含新的自动生成值
# order表的order_num列是AUTO_INCREMENT列
create trigger neworder after insert on orders
for each row
begin
select new.order_num into @order_num;
end
delete触发器
- 在
DELETE
触发器代码内,你可以引用一个名为OLD
的虚拟表,访问被删除的行; OLD
中的值全都是只读的,不能更新。
# 创建一个备份表
CREATE TABLE archive_orders
(
order_num int NOT NULL ,
order_date datetime NOT NULL ,
cust_id int NOT NULL ,
PRIMARY KEY (order_num)
) ENGINE=InnoDB;
# 使用OLD保存将要被删除的行到一个备份表中
create trigger deleteorder
before delete on orders for each row
begin
insert into archive_orders(order_num,order_date,cust_id)
values(old.order_num,old.order_date,old.cust_id); # 引用一个名为OLD的虚拟表,访问被删除的行
end
delete from orders where order_num=20005
update触发器
- 在
UPDATE
触发器代码中,你可以引用一个名为OLD
的虚拟表访问以前(UPDATE
语句前)的值,引用一个名为NEW
的虚拟表访问新更新的值; - 在
BEFORE UPDATE
触发器中,NEW
中的值可能也被更新(允许更改将要用于UPDATE
语句中的值); OLD
中的值全都是只读的,不能更新。
# update触发器
# 在更新vendors表中的vend_state值时,插入前先修改为大写格式
create trigger updatevendor before update on vendors
for each row set new.vend_state = upper(new.vend_state);
# 更新1001供应商的州为china
update vendors set vend_state = 'china' where vend_id =1001;
# 查看update后数据,1001供应商对应的vend_state自动更新为大写的CHINA
select vend_state from vendors where vend_id=1001;
触发器总结
- 触发器的执行是自动的。如果
INSERT
、UPDATE
或DELETE
语旬能够执行,则相关的触发器也能执行。 - 使用触发器预处理来保证数据的一致性(大小写、格式等)。在触发器中执行这种类型的处理的优点是它总是进行这种处理,而且是透明地进行,与客户机应用无关。
- 触发器的一种非常有意义的使用是创建存档。使用触发器,把更改(如果需要,甚至还有之前和之后的状态)记录到另一个表非常容易。
- 不能从触发器内调用存储过程。所需的存储过程代码需要复制到触发器内。
事务处理
什么是事务管理
事务处理是一种机制,用来管理必须成批执行的MySQL操作,以保证数据库不包含不完整的操作结果。利用事务处理,可以保证一组操作不会中途停止,它们或者作为整体执行,或者完全不执行(除非明确指示)。如果没有错误发生,整组语句提交给(写到)数据库表。如果发生错误,则进行回退(撤销)以恢复数据库到某个已知且安全的状态。
相关术语
- 事务(
transaction
):指一组SQL
语句 - 回退(
rollback
):指撤销指定SQL
语句的过程 - 提交(
commit
):指将未存储的SQL
语句结果写入数据库表 - 保留点(
savepoint
):指事务处理中设置的临时占位符(place-holder
),你可以对它发布回退(与回退整个事务处理不同)。
transaction
-- 控制事务处理
# 开始事务及回退
select * from ordertotals; # 查看ordertotals表显示不为空
start transaction; # 开始事务处理
delete from ordertotals; # 删除ordertotals表中所有行
select * from ordertotals; # 查看ordertotals表显示为空
rollback; # rollback语句回退
select * from ordertotals; # rollback后,再次查看ordertotals表显示不为空
rollback
INSERT
、UPDATE
和DELETE
语句。你不能回退SELECT
语句(这样做也没有什么意义)。也不能回退CREATE
或DROP
操作。事务处理块中可以使用这两条语句,但如果你执行回退,它们不会被撤销。commit
一般的MySQL
语旬都是直接针对数据库表执行和编写的。这就是所谓的隐含提交(implicit commit
),即提交(写或保存)操作是自动进行的。
但是,在事务处理块中,提交不会隐含地进行。为进行明确的提交,使用COMMIT
语句,如下所示
# commit 提交
start transaction;
delete from orderitems where order_num = 20010;
delete from orders where order_num = 20010;
commit; # 仅在上述两条语句不出错时写出更改
savepoint
为了支持回退部分事务处理,必须能在事务处理块中合适的位置放置占位符。这样,如果需要回退,可以回退到某个占位符。
这些占位符称为保留点。为了创建占位符,可如下使用SAVEPOINT
语句:
# savepoint 保留点
# 创建保留点
savepoint delete1;
# 回退到保留点
rollback to delete1;
# 保留点在执行一条ROLLBACK或COMMIT语句后会自动释放
# 也可使用release savepoint手动释放保留点
release savepoint delete1;
可以在MySQL
中设置任意多的保留点,越多越好。
更改默认的提交行为
默认的MySQL
行为是自动提交所有修改,也就是执行一条SQL
语句,所做的修改立即生效!
-- 更改默认的提交行为
set autocommit = 0; # 设置autocommit为0(假)指示MySQL不自动提交更改,不管有没有COMMIT语句
MySQL字符集和校对顺序
-
字符集为字母和符号的集合
-
编码为某个字符集成员的内部表示
-
校对为规定字符如何比较的指令
-- 字符集和校对顺序
# 查看所支持的字符集完整列表
show character set;
# 查看所支持校对的完整列表,以及它们适用的字符集
show collation;
# 确定所用系统的字符集和校对
show variables like 'character%';
show variables like 'collation%';
# 以_ci结尾的校对不区分大小写,以_cs结尾的校对区分大小写
# 使用带子句的CREATE TABLE,给表指定字符集和校对
create table mytable
(
column1 int,
column2 varchar(10)
)default character set hebrew
collate hebrew_general_ci;
# 除了能指定字符集和校对的表范围外,MySQL还允许对每个列设置它们
create table mytable
(
column1 int,
column2 varchar(10),
column3 varchar(10) character set latin1 collate latin1_general_ci
)default character set hebrew
collate hebrew_general_ci;
# 校对collate在对用ORDER BY子句排序时起重要的作用
# 如果要用与创建表时不同的校对顺序排序,可在SELECT语句中指定备用的校对顺序
select * from customers order by lastname,firstname collate latin1_general_cs;
ORDER BY
子句中使用以外,COLLATE
还可以用于GROUP BY
、HAVING
、聚集函数、别名等。安全管理
访问控制
访问控制即你需要给用户提供他们所需的访问权,且仅提供他们所需的访问权。管理访问控制需要创建和管理用户账号。
管理用户
-- 管理用户
# 需要获得所有用户账号列表时
# mysql数据库有一个名为user的表,它包含所有用户账号。user表有一个名为user的列
use mysql;
select user from user;
创建用户
-- 创建用户账号
# 使用create user
# 用户名: ben 密码: passwd
create user ben identified by 'passwd';
# 重命名一个用户账号
rename user ben to bforta;
# 删除用户账号
drop user bforta;
# 查看赋予用户账号的权限
show grants for bforta;
# 允许用户在(crashcourse数据库的所有表)上使用SELECT,只读
grant select on crashcourse.* to bforta;
# 重新查看赋予用户账号的权限,发生变化
show grants for bforta;
# 撤销特定的权限
revoke select on crashcourse.* from bforta;
# 简化多次授权
grant select,insert on crashcourse.* to bforta;
-- 更改口令,新口令必须传递到Password()函数进行加密。
set password for bforta = 'newpasswd';
# 原来课本中使用的password()加密函数,在8.0版本中已经移除
# password() :This function was removed in MySQL 8.0.11.
-- 如果不指定用户名,直接修改当前登录用户的口令
set password = 'newpasswd';
数据库维护
数据备份
参数名 | 缩写 | 含义 | 默认值 |
---|---|---|---|
--user |
-u |
连接服务器所使用的用户名 | |
--password |
-p |
连接所用的用户密码 | |
--host |
-h |
MySQL 服务端地址 |
|
--port |
-P |
MySQL 端口号 |
|
--databases |
-B |
指定要备份的数据库 | |
--tables |
指定要备份的表 | ||
--all-databases |
-A |
备份MySQL 服务器上的所有数据库 |
|
--routines |
-R |
导出存储过程以及自定义函数 | 未开启 |
--triggers |
导出触发器 | 开启 | |
--no-data |
-d |
不导出任何数据,只导出数据库表结构 |
# 导出db数据库下的db_table表到test.sql
mysqldump -u root -p db db_table > test.sql
# 导出db数据库下的所有表到test.sql
mysqldump -u root -p db > test.sql
# 导出db1、db2数据库到test.sql
mysqldump -u root -p --databases db1 db2 > test.sql
# 导出db1的table_1表和table_2表到test.sql
mysqldump -u root -p --databases db1 --tables table_1 table_2 > test.sql
# 只导出db1数据库的所有表结构不导出数据
mysqldump -u root -p --databases db1 --no-data > test.sql
# 导出db1的表+视图+存储过程+表结构
mysqldump -u root -p --databases db1 -R > test.sql
导入数据库文件
source ~/test.sql
数据库维护
MySQL
提供了一系列的语句,可以用来保证数据库正确和正常运行。
# 用于检查表的键状态是否正确
analyze table orders;
# 检查表是否存在错误
check table orders,orderitems;
# 回收删除大量数据表的空间
optimize table orders;
MySQL中的IF/ELSE语句
在MySQL
中,可以使用IF/ELSE
语句来实现条件判断。IF/ELSE
语句的一般格式如下:
IF(condition, statement_if_true, statement_if_false)
这里的condition
是一个布尔表达式,如果它的值为TRUE
,则执行statement_if_true
,否则执行statement_if_false
。
例如,假设你有一个名为users的表,其中包含了用户的姓名和年龄。如果要根据用户的年龄来决定他们所属的年龄段,可以使用如下的IF/ELSE
语句:
SELECT name, age,
IF(age < 18, 'Underage', IF(age < 65, 'Adult', 'Senior')) AS age_group
FROM users;
在这个例子中,我们首先判断用户的年龄是否小于18岁,如果是,就返回字符串’Underage’;否则,我们继续判断用户的年龄是否小于65岁,如果是,则返回字符串’Adult’, 否则返回’Senior’。
MySQL中的CASE语句
在MySQL
中,可以使用CASE
语句来进行条件判断,它的一般格式如下:
CASE expression
WHEN value1 THEN result1
WHEN value2 THEN result2
...
ELSE else_result
END
在这个语句中,
expression
是要比较的表达式,value1
、value2
等是可能的值,result1
、result2
等是对应的结果。如果表达式与某个值匹配,则返回该值对应的结果;如果没有匹配,则返回else_result
。
例如,假设你有一个名为people
的表,其中包含了姓名和性别。如果要将查询结果中性别的’f’和’m’改为女性和男性,可以使用如下的CASE语句:
SELECT NAME,
( CASE sex WHEN 'f' THEN '女性' WHEN 'm' THEN '男性' END ) AS sex
FROM
people
需要注意的是,在MySQL中,CASE语句还可以有其他形式的写法,例如使用简化的语法:
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
...
ELSE else_result
END
这种写法与上面的语法类似,只是省略了表达式expression
,直接在WHEN
子句中写入条件。
例如,假设你有一个名为orders的表,其中包含了用户的购买金额和日期。如果要根据订单金额的不同,将订单分成三个等级(低级、中级、高级),可以使用如下的CASE语句:
SELECT amount, date,
CASE
WHEN amount < 1000 THEN 'Low'
WHEN amount >= 1000 AND amount <= 5000 THEN 'Medium'
ELSE 'High'
END AS order_level
FROM orders;
在这个例子中,我们首先判断订单金额是否小于1000元,如果是,就返回字符串’Low’;如果不是,我们继续判断订单金额是否在1000元到5000元之间,如果是,则返回字符串’Medium’,否则返回’High’。