第1章 了解SQL
数据库(database) 保存有组织的数据的容器(通常是一个文件或一组文件)。
表(table) 某种特定类型数据的结构化清单。
模式(schema) 关于数据库和表的布局及特性的信息。
列(column) 表中的一个字段。所有表都是由一个或多个列组成的。
数据类型(datatype) 所容许的数据的类型。每个表列都有相应的数据类型,它限制(或容许)该列中存储的数据。
行(row) 表中的一个记录。
主键(primary key) 一列(或一组列),其值能够唯一区分表中每个行。
表中的任何列都可以作为主键,只要它满足以下条件:
- 任意两行都不具有相同的主键值;
- 每个行都必须具有一个主键值(主键列不允许NULL值)。
主键的最好习惯:
- 不更新主键列中的值;
- 不重用主键列的值;
- 不在主键列中使用可能会更改的值。
第2章 MySQL简介
- DBMS(数据库管理系统)分两类:
- 基于共享文件系统的DBMS,如Microsoft Access和FileMaker;
- 基于客户机—服务器的DBMS,如MySQL、Oracle以及Microsoft SQL Server。
第3章 使用MySQL
SHOW DATABASES; /* 显示所有数据库 */
USE mysql; /* 选择数据库mysql */
SHOW TABLES; /* 显示所有表 */
SHOW COLUMNS FROM user; /* 显示user表中的列信息 */
DESCRIBE user; /* 同上 */
SHOW STATUS; /* 显示服务器状态信息 */
SHOW CREATE DATABASE mysql; /* 显示创建数据库mysql的SQL语句 */
SHOW CREATE TABLE user; /* 显示创建表user的SQL语句 */
show grants; /* 显示用户的授权信息 */
SHOW ERRORS; /* 显示服务器中的错误信息 */
SHOW WARNINGS; /* 显示服务器中的警告信息 */
可以使用
help show
来查看帮助信息。
第4章 检索数据
SELECT User FROM user;
SELECT user.User FROM user; /* 同上 */
SELECT User FROM mysql.user; /* 同上 */
SELECT Host,User FROM user;
SELECT * FROM user;
SELECT DISTINCT User FROM user; /* 去除重复 */
SELECT User FROM user LIMIT 3; /* 只显示查询结果的前3条记录 */
SELECT User FROM user LIMIT 2,3; /* 只显示查询结果的第2条开始(下标从0开始)后面的3条记录 */
SELECT User FROM user LIMIT 3 OFFSET 2; /* 同上 */
SQL不区分大小写,也忽略空格。
DISTINCT关键字应用于所有列而不仅是前置它的列。
第5章 排序检索数据
SELECT User FROM user ORDER BY User; /* 升序排序 */
SELECT User FROM user ORDER BY User ASC; /* 同上 */
SELECT User FROM user ORDER BY User DESC; /* 降序排序 */
SELECT User FROM user ORDER BY User, Host; /* 先按User升序排序,再按Host升序排序 */
SELECT User FROM user ORDER BY User DESC, Host; /* 先按User降序排序,再按Host升序排序 */
SELECT User FROM user ORDER BY User DESC, Host ASC; /* 同上 */
DESC关键字只应用到直接位于其前面的列名。
第6章 过滤数据
SELECT Host, User FROM user WHERE User='root';
SELECT Host, User, max_connections FROM user WHERE max_connections = 0;
SELECT Host, User, max_connections FROM user WHERE max_connections != 0;
SELECT Host, User, max_connections FROM user WHERE max_connections <> 0; /* 同上 */
SELECT Host, User, max_connections FROM user WHERE max_connections < 0;
SELECT Host, User, max_connections FROM user WHERE max_connections <= 0;
SELECT Host, User, max_connections FROM user WHERE max_connections > 0;
SELECT Host, User, max_connections FROM user WHERE max_connections >= 0;
SELECT Host, User, max_connections FROM user WHERE max_connections BETWEEN 0 AND 5;
SELECT Host, User, max_connections FROM user WHERE ssl_cipher IS NULL; /* ssl_cipher字段为NULL */
第7章 数据过滤
SELECT Host, User FROM user WHERE Host='localhost' AND User='root';
SELECT Host, User FROM user WHERE Host='localhost' OR User='root';
SELECT Host, User FROM user WHERE (Host='localhost' OR User='root') AND max_connections = 0;
SELECT Host, User FROM user WHERE Host IN ('%', 'localhost');
SELECT Host, User FROM user WHERE Host NOT IN ('%', 'localhost');
AND的优先级比OR高。
IN操作符完成与OR相同的功能,但是更快。
第8章 用通配符进行过滤
SELECT Host, User FROM user WHERE Host LIKE 'local%';
SELECT Host, User FROM user WHERE User LIKE 'r__t';
百分号(%)表示任何字符出现任意次数。%不能匹配NULL值。
下划线(_)只匹配单个字符。
第9章 用正则表达式进行搜索
SELECT Host, User FROM user WHERE User REGEXP 'r..t';
SELECT Host, User FROM user WHERE Host REGEXP '%|localhost';
SELECT Host, User FROM user WHERE User REGEXP 'r[a-z]{2}t';
SELECT Host, User FROM user WHERE User REGEXP 'mysql\\.sys';
可以使用类似
SELECT 'hello' REGEXP '[0-9]'
这样的SQL来测试正常表达式,匹配成功返回1,否则返回0。
第10章 创建计算字段
SELECT CONCAT(User, '(', Host, ')') FROM user;
SELECT CONCAT(User, '(', Host, ')') AS UserHost FROM user;
第11章 使用数据处理函数
函数 | 说明 |
---|---|
Left() | 返回串左边的字符 |
Length() | 返回串的长度 |
Locate() | 找出串的一个子串 |
Lower() | 将串转换为小写 |
LTrim() | 去掉串左边的空格返 |
Right() | 返回串右边的字符 |
RTrim() | 去掉串右边的空格 |
Soundex() | 返回串的SOUNDEX值 |
SubString() | 返回子串的字符 |
Upper() | 将串转换为大写 |
AddDate() | 增加一个日期(天、周等) |
AddTime() | 增加一个时间(时、分等) |
CurDate() | 返回当前日期 |
CurTime() | 返回当前时间 |
Date() | 返回日期时间的日期部分 |
DateDiff() | 计算两个日期之差 |
Date_Add() | 高度灵活的日期运算函数 |
Date_Format() | 返回一个格式化的日期或时间串 |
Day() | 返回一个日期的天数部分 |
DayOfWeek() | 对于一个日期,返回对应的星期几 |
Hour() | 返回一个时间的小时部分 |
Minute() | 返回一个时间的分钟部分 |
Month() | 返回一个日期的月份部分 |
Now() | 返回当前日期和时间 |
Second() | 返回一个时间的秒部分 |
Time() | 返回一个日期时间的时间部分 |
Year() | 返回一个日期的年份部分 |
Abs() | 返回一个数的绝对值 |
Cos() | 返回一个角度的余弦 |
Exp() | 返回一个数的指数值 |
Mod() | 返回除操作的余数 |
Pi() | 返回圆周率 |
Rand() | 返回一个随机数 |
Sin() | 返回一个角度的正弦 |
Sqrt() | 返回一个数的平方根 |
Tan() | 返回一个角度的正切 |
不管是插入或更新表值还是用WHERE子句进行过滤,日期必须为格式yyyy-mm-dd。并且总是应该使用4位数字的年份。
第12章 汇总数据
聚集函数(aggregate function) 运行在行组上,计算和返回单个值的函数。
AVG()函数 计算单列的平均值。
AVG()函数忽略列值为NULL的行。
COUNT()函数 进行计数。
使用COUNT(*)对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值。
使用COUNT(column)对特定列中具有值的行进行计数,忽略NULL值。
MAX()函数 返回指定列中的最大值。
MAX()函数忽略列值为NULL的行。
MAX()函数 返回指定列中的最小值。
MIN()函数忽略列值为NULL的行。
SUM()函数 用来返回指定列值的和(总计)。
SUM()函数忽略列值为NULL的行。
以上5个聚集函数都可以如下使用:
- 对所有的行执行计算,指定ALL参数或不给参数(因为ALL是默认行为);
只包含不同的值,指定DISTINCT参数。
如果指定列名,则DISTINCT只能用于COUNT()。DISTINCT不能用于COUNT(*),因此不允许使用COUNT(DISTINCT),否则会产生错误。类似地,DISTINCT必须使用列名,不能用于计算或表达式。
将DISTINCT用于MIN()和MAX() 。虽然DISTINCT从技术上可用于MIN()和MAX(),但这样做实际上没有价值。一个列中的最小值和最大值不管是否包含不同值都是相同的。
第13章 分组数据
使用GROUP BY创建分组时需要注意:
- GROUP BY子句可以包含任意数目的列。这使得能对分组进行嵌套,为数据分组提供更细致的控制。
- 如果在GROUP BY子句中嵌套了分组,数据将在最后规定的分组上进行汇总。换句话说,在建立分组时,指定的所有列都一起计算(所以不能从个别的列取回数据)。
- GROUP BY子句中列出的每个列都必须是检索列或有效的表达式(但不能是聚集函数)。如果在SELECT中使用表达式,则必须在GROUP BY子句中指定相同的表达式。不能使用别名。
- 除聚集计算语句外,SELECT语句中的每个列都必须在GROUP BY子句中给出。
- 如果分组列中具有NULL值,则NULL将作为一个分组返回。如果列中有多行NULL值,它们将分为一组。
GROUP BY子句必须出现在WHERE子句之后,ORDER BY子句之前。
使用WITH ROLLUP关键字,可以得到每个分组以及每个分组汇总级别(针对每个分组)的值。
分组过滤操作HAVING支持所有WHERE操作符。
WHERE在数据分组前进行过滤,HAVING在数据分组后进行过滤。
一般在使用GROUP BY子句时,应该也给出ORDER BY子句。这是保证数据正确排序的唯一方法。
SELECT子句顺序
子句 | 说明 | 是否必须使用 |
---|---|---|
SELECT | 要返回的列或表达式 | 是 |
FROM | 从中检索数据的表 | 仅在从表选择数据时使用 |
WHERE | 行级过滤 | 否 |
GROUP BY | 分组说明 | 仅在按组计算聚集时使用 |
HAVING | 组级过滤 | 否 |
ORDER BY | 输出排序顺序 | 否 |
LIMIT | 要检索的行数 | 否 |
第14章 使用子查询
子查询就是可以把一条SELECT语句返回的结果用于另一条SELECT语句的WHERE子句。
在WHERE子句中使用子查询,应该保证SELECT语句具有与WHERE子句中相同数目的列。通常,子查询将返回单个列并且与单个列匹配,但如果需要也可以使用多个列。
第15章 连结表
外键(foreign key) 为某个表中的一列,它包含另一个表的主键值,定义了两个表之间的关系。
由没有联结条件的表关系返回的结果为 笛卡儿积(cartesian product) 。如下:
SELECT vend_name, prod_name, prod_price FROM vendors, products ORDER BY vend_name, prod_name;
有时我们会听到返回称为 叉联结(cross join) 的笛卡儿积的联结类型。
等值联结(equijoin) 基于两个表之间的相等测试。这种联结也称为 内部联结 。下面的SQL都是等值联结:
SELECT vend_name, prod_name, prod_price FROM vendors, products WHERE vendors.vend_id = products.vend_id ORDER BY vend_name, prod_name; SELECT vend_name, prod_name, prod_price FROM vendors INNER JOIN products ON vendors.vend_id = products.vend_id;
第16章 创建高级联结
表别名只在查询执行中使用。
自联结
SELECT p1.prod_id, p1.prod_name FROM products AS p1, products AS p2 WHERE p1.vend_id = p2.vend_id AND p2.prod_id = 'DTNTR';
自联结远比处理子查询快得多。
无论何时对表进行联结,应该至少有一个列出现在不止一个表中(被联结的列)。标准的联结(前一章中介绍的内部联结)返回所有数据,甚至相同的列多次出现。 自然联结 排除多次出现,使每个列只返回一次。这一般是通过对表使用通配符(SELECT*),对所有其他表的列使用明确的子集来完成的。
SELECT c.*, o.order_num, o.order_data, io.prod_id, io.quantity, io.item_price FROM customers AS c, orders AS o, orderitems AS io WHERE c.cust_id = o.cust_id AND io.order_num = o.order_num AND prod_id = 'FB';
外部联结在结果中包含了那些在另一个表中没有关联行的行。
SELECT customers.cust_id, orders.order_num FROM customers LEFT OUTER JOIN orders ON customers.cust_id = orders.cust_id;
- 在使用OUTER JOIN语法时,必须使用RIGHT或LEFT关键字指定包括其所有行的表(RIGHT指出的是OUTER JOIN右边的表,而LEFT指出的是OUTER JOIN左边的表)。上面的例子使用LEFT OUTER JOIN从FROM子句的左边表(customers表)中选择所有行。为了从右边的表中选择所有行,应该使用RIGHT OUTER JOIN。
第17章 组合查询
MySQL允许执行多个查询(多条SELECT语句),并将结果作为单个查询结果集返回。这些组合查询通常称为 并(union) 或 复合查询(compound query) 。
使用UNION规则时需要遵循的规则:
- UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔(因此,如果组合4条SELECT语句,将要使用3个UNION关键字)。
- UNION中的每个查询必须包含相同的列、表达式或聚集函数(不过各个列不需要以相同的次序列出)。
- 列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含地转换的类型(例如,不同的数值类型或不同的日期类型)。
UNION从查询结果集中自动去除了重复的行(换句话说,它的行为与单条SELECT语句中使用多个WHERE子句条件一样)。这是UNION的默认行为,如果想返回所有匹配行,可使用UNION ALL而不是UNION。
在用UNION组合查询时,只能使用一条ORDER BY子句,它必须出现在最后一条SELECT语句之后。
第18章 全文本搜索
MyISAM支持全文本搜索,InnoDB不支持。
为了进行全文本搜索,必须索引被搜索的列。一般在创建表时使用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;
不要在导入数据时使用FULLTEXT。应该首先导入所有数据,然后再修改表,定义FULLTEXT。
使用两个函数Match()和Against()执行全文本搜索,其中Match()指定被搜索的列,Against()指定要使用的搜索表达式。
SELECT note_text FROM productnotes WHERE MATCH(note_text) AGAINST('rabbit');
传递给Match()的值必须与FULLTEXT()定义中的相同。如果指定多个列,则必须列出它们(而且次序正确)。
搜索不区分大小写,除非使用BINARY方式,否则全文本搜索不区分大小写。
也是使用LIKE子句进行搜索。
SELECT note_text FROM productnotes WHERE note_text LIKE '%rabbit%';
使用LIKE子句进行搜索时,不会对结果进行排序。而使用函数Match()和Against()执行全文本搜索时,会对结果进行排序,具有较高等级的行先返回。等级可以通过下面的方式获取:
SELECT note_text, MATCH(note_text) AGAINST('rabbit') AS 'rank' FROM productnotes;
在使用 查询扩展 时,MySQL对数据和索引进行两遍扫描来完成搜索:
- 进行一个基本的全文本搜索,找出与搜索条件匹配的所有行;
- MySQL检查这些匹配行并选择所有有用的词;
MySQL再次进行全文本搜索,这次不仅使用原来的条件,而且还使用所有有用的词。
SELECT note_text FROM productnotes WHERE MATCH(note_text) AGAINST('anvils' WITH QUERY EXPANSION);
即使没有FULLTEXT索引也可以使用 布尔文本搜索 ,但这是一种非常缓慢的操作。
排列而不排序 在布尔方式中,不按等级值降序排序返回的
全文本搜索的使用说明
- 在索引全文本数据时,短词被忽略且从索引中排除。短词定义为那些具有3个或3个以下字符的词(如果需要,这个数目可以更改)。
- MySQL带有一个内建的非用词(stopword)列表,这些词在索引全文本数据时总是被忽略。如果需要,可以覆盖这个列表。
- 许多词出现的频率很高,搜索它们没有用处(返回太多的结果)。因此,MySQL规定了一条50%规则,如果一个词出现在50%以上的行中,则将它作为一个非用词忽略。50%规则不用于IN BOOLEANMODE。
- 如果表中的行数少于3行,则全文本搜索不返回结果(因为每个词或者不出现,或者至少出现在50%的行中)。
- 忽略词中的单引号。例如,don’t索引为dont。
- 不具有词分隔符(包括日语和汉语)的语言不能恰当地返回全文本搜索结果。
如前所述,仅在MyISAM数据库引擎中支持全文本搜索。
show variables like 'ft%';
一些例子
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 */ 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,增加前者的等级,降低后者的等级 */ SELECT note_text FROM productnotes WHERE MATCH(note_text) AGAINST('+rabbit +(<bait)' IN BOOLEAN MODE); /* 匹配词rabbit和bait,降低后者的等级 */
- 全文本布尔操作符
布尔操作符 | 说明 |
---|---|
+ | 包含,词必须存在 |
- | 排除,词必须不出现 |
> | 包含,而且增加等级值 |
< | 包含,且减少等级值 |
() | 把词组成子表达式(允许这些子表达式作为一个组被包含、排除、排列等) |
~ | 取消一个词的排序值 |
* | 词尾的通配符 |
“” | 定义一个短语(与单个词的列表不一样,它匹配整个短语以便包含或排除这个短语) |
第19章 插入数据
如果表的定义允许,则可以在INSERT操作中省略某些列:
- 该列定义为允许NULL值(无值或空值)。
- 在表定义中给出默认值。这表示如果不给出值,将使用默认值。
可以通过在INSERT和INTO之间添加关键字LOW_PRIORITY,指示MySQL降低INSERT语句的优先级:
INSERT LOW_PRIORITY INTO
同样适用于UPDATE和DELETE。
一些例子:
/* 一次插入一条记录 */ INSERT INTO customers (cust_name, cust_address) VALUES ('A', 'B'); /* 一次插入多条记录 */ INSERT INTO customers (cust_name, cust_address) VALUES ('A', 'B') VALUES ('C', 'D'); /* 插入检索出的数据 */ INSERT INTO customers (cust_name, cust_address) SELECT cust_name, cust_address FROM customers_new;
INSERT SELECT中不要求列名匹配。MySQL使用的是列的位置,因此SELECT中的第一列(不管其列名)将用来填充表列中指定的第一个列,第二列将用来填充表列中指定的第二个列,如此等等。
第20章 更新和删除数据
如果用UPDATE语句更新多行,并且在更新这些行中的一行或多行时出一个现错误,则整个UPDATE操作被取消(错误发生前更新的所有行被恢复到它们原来的值)。为即使是发生错误,也继续进行更新,可使用IGNORE关键字,如下所示:
UPDATE IGNORE customers
- 如果想从表中删除所有行,不要使用DELETE。可使用TRUNCATE TABLE语句,它完成相同的工作,但速度更快(TRUNCATE实际是删除原来的表并重新创建一个表,而不是逐行删除表中的数据)。
第21章 创建和操纵表
主键值必须唯一。即,表中的每个行必须具有唯一的主键值。如果主键使用单个列,则它的值必须唯一。如果使用多个列,则这些列的组合值必须唯一。
函数last_insert_id() 返回最后一个AUTO_INCREMENT值。
外键不能跨引擎。
一些例子:
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, update_cnt INT NOT NULL DEFAULT 0, PRIMARY KEY(note_id, prod_id), ) ENGINE=MyISAM; ALTER TABLE Vendors DROP COLUMN vend_phone; ALTER TABLE orderitems ADD CONSTRAINT fk_orderitems_orders FOREIGN KEY (order_num) REFERENCES orders (order_num); DROP TABLE customers2; RENAME TABLE customers2 TO customers; RENAME TABLE customers TO customers_backup, vendors TO vendors_backup;
第22章 使用视图
视图仅仅是用来查看存储在别处的数据的一种设施。视图本身不包含数据,因此它们返回的数据是从其他表中检索出来的。
CREATE VIEW customeremaillist AS SELECT cust_id, cust_name, cust_email FROM customers WHERE cust_email IS NOT NULL;
视图的规则和限制
- 与表一样,视图必须唯一命名(不能给视图取与别的视图或表相同的名字)。
- 对于可以创建的视图数目没有限制。
- 为了创建视图,必须具有足够的访问权限。
- 视图可以嵌套,即可以利用从其他视图中检索数据的查询来构造一个视图。
- ORDER BY可以用在视图中,但如果从该视图检索数据SELECT中也含有ORDER BY,那么该视图中的ORDER BY将被覆盖。
- 视图不能索引,也不能有关联的触发器或默认值。
- 视图可以和表一起使用。
使用视图
- 视图用CREATE VIEW语句来创建。
- 使用SHOW CREATE VIEW viewname;来查看创建视图的语句。
- 用DROP删除视图,其语法为DROP VIEW viewname。
- 更新视图时,可以先用DROP再用CREATE,也可以直接用CREATE OR REPLACE VIEW。如果要更新的视图不存在,则第2条更新语句会创建一个视图;如果要更新的视图存在,则第2条更新语句会替换原有视图。
视图是可更新的(即,可以对它们使用INSERT、UPDATE和DELETE)。更新一个视图将更新其基表(可以回忆一下,视图本身没有数据)。如果你对视图增加或删除行,实际上是对其基表增加或删除行。
并非所有视图都是可更新的。基本上可以说,如果MySQL不能正确地确定被更新的基数据,则不允许更新(包括插入和删除)。这实际上意味着,如果视图定义中有以下操作,则不能进行视图的更新:
- 分组(使用GROUP BY和HAVING);
- 联结;
- 子查询;
- 并;
- 聚集函数(Min()、Count()、Sum()等);
- DISTINCT;
- 导出(计算)列。
第23章 使用存储过程
执行存储过程
CALL productpricing(@pricelow, @pricehigh, @priceaverage);
创建存储过程
-- Name: ordertotal -- Parameters: onumber = order number -- taxable = 0 if not taxable, 1 if taxable -- ototal = order total variable CREATE PROCEDURE ordertotal( IN onumber INT, IN taxable BOOLEAN, OUT ototal DECIMAL(8, 2) ) COMMENT 'Obtain order total, optionally adding tax' BEGIN --Declare variable for total DECLARE total DECIMAL(8, 2); --Declare tax percentage DECLARE taxrate INT DEFAULT 6; --GET the order total SELECT Sum(item_price*quantity) FROM orderitems WHERE order_num = onumber INTO total; --Is this taxable IF taxable THEN SELECT total+(total/100*taxrate) INTO total; END IF; SELECT total INTO ototal; END;
COMMENT的值会在
SHOW PROCEDURE STATUS
时显示出来。删除存储过程
DROP PROCEDURE ordertotal;
检查存储过程
SHOW CREATE PROCEDURE ordertotal;
为了获得包括何时、由谁创建等详细信息的存储过程列表,使用
SHOW PROCEDURE STATUS
或SHOW PROCEDURE STATUS LIKE 'ordertotal'
。使用mysql命令行实用程序时需要注意:
默认的MySQL语句分隔符为
;
mysql命令行实用程序也使用;
作为语句分隔符。如果命令行实用程序要解释存储过程自身内的;字符,则它们最终不会成为存储过程的成分,这会使存储过程中的SQL出现句法错误。
解决办法是临时更改命令行实用程序的语句分隔符,如下所示:DELIMITER // CREATE PROCEDURE productpricing() BEGIN SELECT AVG(prod_price) AS priceaverage FROM products; END // DELIMITER ;
除
\
符号外,任何字符都可以用作语句分隔符。
第24章 使用游标
CREATE PROCEDURE processorders ()
BEGIN
-- Declare local variables
DECLARE done TINYINT(1) DEFAULT 0;
DECLARE o INT;
DECLARE t DECIMAL(8,2);
-- Declare the cursor
DECLARE ordernumbers CURSOR FOR
SELECT order_num FROM orders;
-- Declare continue handler
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
-- Create a table to store the results
CREATE TABLE IF NOT EXISTS ordertotals(
order_num INT,
total DECIMAL(8,2)
);
-- Open the cursor
OPEN ordernumbers;
-- Loop through all rows
REPEAT
-- Get order number
FETCH ordernumbers INTO o;
-- Get the total for this order
CALL ordertotalmye(o,1,t);
-- Insert order and total into ordertotals
INSERT INTO ordertotals(order_num,total)
VALUES(o,t);
-- End of loop
UNTIL done END REPEAT;
-- Close the cursor
CLOSE ordernumbers;
END;
第25章 使用触发器
触发器是MySQL响应以下任意语句而自动执行的一条MySQL语句(或位于 BEGIN 和 END 语句之间的一组语 句):
- DELETE;
- INSERT;
- UPDATE。
在创建触发器时,需要给出4条信息:
- 唯一的触发器名;
- 触发器关联的表;
- 触发器应该响应的活动(DELETE、INSERT或UPDATE);
触发器何时执行(处理之前或之后)。
CREATE TRIGGER newproduct AFTER INSERT ON products FOR EACH ROW SELECT 'Product added';
只有表才支持触发器,视图不支持(临时表也不支持)。
触发器按每个表每个事件每次地定义,每个表每个事件每次只允许一个触发器。因此,每个表最多支持6个触发器(每条INSERT、UPDATE 和DELETE的之前和之后)。
如果BEFORE触发器失败,则MySQL将不执行请求的操作。此外,如果BEFORE触发器或语句本身失败,MySQL 将不执行AFTER触发器(如果有的话)。
删除触发器
DROP TRIGGER newproduct;
触发器不能更新或覆盖。为了修改一个触发器,必须先删除它, 然后再重新创建。
INSERT触发器在INSERT语句执行之前或之后执行。需要知道以下几点:
- 在INSERT触发器代码内,可引用一个名为NEW的虚拟表,访问被插入的行;
- 在BEFORE INSERT触发器中,NEW中的值也可以被更新(允许更改被插入的值);
对于AUTO_INCREMENT列,NEW在INSERT执行之前包含0,在INSERT执行之后包含新的自动生成值。
CREATE TRIGGER neworder AFTER INSERT ON orders FOR EACH ROW SELECT NEW.order_new;
DELETE触发器在DELETE语句执行之前或之后执行。需要知道以下两点:
- 在DELETE触发器代码内,你可以引用一个名为OLD的虚拟表,访问被删除的行;
OLD中的值全都是只读的,不能更新。
CREATE TRIGGER deleteorder BEFORE DELETE ON orders FOR EACH ROW BEGIN INSERT INTO archive_orders(order_num, order_date, cust_id) VALUE(OLD.order_num, OLD.order_date, OLD.cust_id); END;
UPDATE触发器在UPDATE语句执行之前或之后执行。需要知道以下几点:
- 在UPDATE触发器代码中,你可以引用一个名为OLD的虚拟表访问以前(UPDATE语句前)的值,引用一个名为NEW的虚拟表访问新更新的值;
- 在BEFORE UPDATE触发器中,NEW中的值可能也被更新(允许更改将要用于UPDATE语句中的值);
OLD中的值全都是只读的,不能更新。
CREATE TRIGGER updatevendor BEFORE UPDATE ON vendors FOR EACH ROW SET NEW.vend_state = UPPER(NEW.vend_state);
- MySQL触发器中不支持CALL语句。这表示不能从触发器内调用存储过程。
第26章 管理事务处理
事务处理(transaction processing)可以用来维护数据库的完整性,它保证成批的MySQL操作要么完全执行,要么完全不执行。
MyISAM和InnoDB是两种最常使用的引擎。前者不支持明确的事务处理管理,而后者支持。
相关术语:
- 事务(transaction) 指一组SQL语句;
- 回退(rollback) 指撤销指定SQL语句的过程;
- 提交(commit) 指将未存储的SQL语句结果写入数据库表;
- 保留点( savepoint ) 指事务处理中设置的临时占位符(placeholder),你可以对它发布回退(与回退整个事务处理不同)。
开始事务
START TRANSACTION;
回退事务
SELECT * FROM ordertotals; START TRANSACTION; DELETE FROM ordertotals; SELECT * FROM ordertotals; ROLLBACK; SELECT * FROM ordertotals;
事务处理用来管理INSERT、UPDATE和DELETE语句。你不能回退SELECT语句,也 不能回退CREATE或DROP操作 。事务处理块中可以使用这两条语句,但如果你执行回退,它们不会被撤销。
提交事务
START TRANSACTION; DELETE FROM orderitems WHERE order_num = 20010; DELETE FROM orders WHERE order_num = 20010; COMMIT;
当COMMIT或ROLLBACK语句执行后,事务会自动关闭(将来的更改会隐含提交)。
为了支持回退部分事务处理,必须能在事务处理块中合适的位置放 置保留点。这样,如果需要回退,可以回退到某个保留点。
SAVEPOINT delete1; /* 创建保留点 */ ROLLBACK TO delete1; /* 回退到保留点 */
保留点越多,就越能按自己的意愿灵活地进行回退,所以保留点越多越好。
保留点在事务处理完成(执行一条ROLLBACK或 COMMIT)后自动释放。也可以用RELEASE SAVEPOINT明确地释放保留点。
默认的MySQL行为是自动提交所有更改。可以关闭:
SET AUTOCOMMIT=0;
autocommit标志决定是否自动提交更改,不管有没有COMMIT 语句。设置autocommit为0(假)指示MySQL不自动提交更改 (直到autocommit被设置为真为止)。
autocommit标志是针对每个连接而不是服务器的。
第27章 全球化和本地化
重要术语:
- 字符集 为字母和符号的集合;
- 编码 为某个字符集成员的内部表示;
- 校对 为规定字符如何比较的指令。
查看所支持的字符集完整列表
SHOW CHARACTER SET;
查看所支持校对的完整列表
SHOW COLLATION;
许多校对出现两次,一次区分大小写(由_cs表一次不区分大小写(由_ci表示)。
通常系统管理在安装时定义一个默认的字符集和校对。
SHOW VARIABLES LIKE 'character%'; SHOW VARIABLES LIKE 'collation%';
可以在创建数据库时,指定默认的字符集和校对。
CREATE TABLE mytable ( c1 INT, c2 VARCHAR(10) ) DEFAULT CHARACTER SET hebrew COLLATE hebrew_general_ci;
一般,MySQL如下确定使用什么样的字符集和校对:
- 如果指定CHARACTER SET和COLLATE两者,则使用这些值。
- 如果只指定CHARACTER SET,则使用此字符集及其默认的校对(如SHOW CHARACTER SET的结果中所示)。
- 如果既不指定CHARACTER SET,也不指定COLLATE,则使用数据库默认。
MySQL还允许对每个列设置:
CREATE TABLE mytable ( c1 INT, c2 VARCHAR(10) CHARACTER SET latin1 COLLATE latin1_general_ci ) DEFAULT CHARACTER SET hebrew COLLATE hebrew_general_ci;
校对在对用ORDER BY子句检索出来的数据排序时起重要的作用。如果你需要用与创建表时不同的校对顺序排序特定的SELECT语句,可以在SELECT语句自身中进行:
SELECT * FROM customers ORDER BY lastname, firstname COLLATE latin1_general_cs;
除了ORDER BY子句,COLLATE还可以用于GROUP BY、HAVING、聚集函数、别名等。
如果绝对需要,串可以在字符集之间进行转换。为此,使用Cast()或Convert()函数。
第28章 安全管理
创建用户账号
CREATE USER ben IDENTIFIED BY '123';
重命名用户账号
RENAME USER ben TO kom;
删除用户账号
DROP USER kom;
在创建用户账号后,必须接着分配访问权限。新创建的用户账号没有访问权限。它们能登录MySQL,但不能看到数据,不能执行任何数据库操作。查看用户账号权限:
SHOW GRANTS FOR kom;
用户定义为user@host。 MySQL的权限用用户名和主机名结合定义。如果不指定主机名,则使用默认的主机名%(授予用户访问权限而不管主机名)。
为设置权限,使用GRANT语句。GRANT要求你至少给出以下信息:
- 要授予的权限;
- 被授予访问权限的数据库或表;
用户名。
GRANT SELECT ON crashcourse.* TO kom;
撤销特定的权限使用REVOKE:
REVOKE SELECT ON crashcourse.* FROM kom;
GRANT和REVOKE可在几个层次上控制访问权限:
- 整个服务器,使用GRANT ALL和REVOKE ALL;
- 整个数据库,使用ON database.*;
- 特定的表,使用ON database.table;
- 特定的列;
- 特定的存储过程。
更改密码
SET PASSWORD FOR kom = PASSWORD('456');
不指定用户名时,SET PASSWORD更新当前登录用户的口令。
SET PASSWORD = PASSWORD('789')
在使用GRANT和REVOKE时,用户账号必须存在,但对所涉及的对象没有这个要求。这允许管理员在创建数据库和表之前设计和实现安全措施。这样做的副作用是,当某个数据库或表被删除时(用DROP语句),相关的访问权限仍然存在。而且,如果将来重新创建该数据库或表,这些权限仍然起作用。
可通过列出各权限并用逗号分隔, 将多条GRANT语句串在一起。
GRANT SELECT, INSERT ON crashcourse.* TO kom
- 权限列表
权限 | 说明 |
---|---|
ALL | 除GRANT OPTION外的所有权限 |
ALTER | 使用ALTER TABLE |
ALTER ROUTINE | 使用ALTER PROCEDURE和DROP PROCEDURE |
CREATE | 使用CREATE TABLE |
CREATE ROUTINE | 使用CREATE PROCEDURE |
CREATE TEMPORARY TABLES | 使用CREATE TEMPORARY TABLE |
CREATE USER | 使用CREATE USER、DROP USER、RENAME USER和REVOKE ALL PRIVILEGES |
CREATE VIEW | 使用CREATE VIEW |
DELETE | 使用DELETE |
DROP | 使用DROP TABLE |
EXECUTE | 使用CALL和存储过程 |
FILE | 使用SELECT INTO OUTFILE和LOAD DATA INFILE |
GRANT OPTION | 使用GRANT和REVOKE |
INDEX | 使用CREATE INDEX和DROP INDEX |
INSERT | 使用INSERT |
LOCK TABLES | 使用LOCK TABLES |
PROCESS | 使用SHOW FULL PROCESSLIST |
RELOAD | 使用FLUSH |
REPLICATION CLIENT | 服务器位置的访问 |
REPLICATION SLAVE | 由复制从属使用 |
SELECT | 使用SELECT |
SHOW DATABASES | 使用SHOW DATABASES |
SHOW VIEW | 使用SHOW CREATE VIEW |
SHUTDOWN | 使用mysqladmin shutdown(用来关闭MySQL) |
SUPER | 使用CHANGE MASTER、KILL、LOGS、PURGE、MASTER 和SET GLOBAL。还允许mysqladmin调试登录 |
UPDATE | 使用UPDATE |
USAGE | 无访问权限 |
第29章 数据库维护
备份数据
- 使用命令行实用程序mysqldump转储所有数据库内容到某个外部文件。在进行常规备份前这个实用程序应该正常运行,以便能正确地备份转储文件。
- 可用命令行实用程序 mysqlhotcopy 从一个数据库复制所有数据 (并非所有数据库引擎都支持这个实用程序)。
可以使用MySQL的BACKUP TABLE或SELECT INTO OUTFILE转储所有数据到某个外部文件。这两条语句都接受将要创建的系统文件名,此系统文件必须不存在,否则会出错。数据可以用RESTORE TABLE来复原。
为了保证所有数据被写到磁盘(包括索引数据)可能需要在进行备份前使用FLUSH TABLES语句。
进行数据库维护
检查表键是否正确
ANALYZE TABLE orders;
CHECK TABLE用来针对许多问题对表进行检查。在MyISAM表上还对索引进行检查。CHECK TABLE支持一系列的用于MyISAM表的方式。 CHANGED检查自最后一次检查以来改动过的表。EXTENDED执行最彻底的检查,FAST只检查未正常关闭的表,MEDIUM检查所有被删除的链接并进行键检验,QUICK只进行快速扫描。
CHECK TABLE orders, orderitems FAST QUICK;
如果 MyISAM 表访问产生不正确和不一致的结果,可能需要用REPAIR TABLE来修复相应的表。这条语句不应该经常使用,如果需要经常使用,可能会有更大的问题要解决。
如果从一个表中删除大量数据,应该使用OPTIMIZE TABLE来收回所用的空间,从而优化表的性能。
第30章 改善性能
- 可使用
SHOW PROCESSLIST
显示所有活动进程(以及它们的线程ID和执行时间)。可以用KILL命令终结某个特定的进程(使用这个命令需要作为管理员登录)。 - 使用EXPLAIN语句让MySQL解释它将如何执行一条SELECT语句。
- 一般来说,存储过程执行得比一条一条地执行其中的各条MySQL 语句快。
- 有的操作(包括INSERT)支持一个可选的DELAYED关键字,如果使用它,将把控制立即返回给调用程序,并且一旦有可能就实际执行该操作。