开篇说明:1、本文中的SQL脚本来自于:http://forta.com/books/0672327120/
2、本文为《MySQL必知必会》的读书笔记,内容基本上源于书中
目录
第1章:了解SQL
第2章:MySQL简介
第3章:使用MySQL
第4章:检索数据
第5章:排序检索数据
第6章:过滤数据
第7章:数据过滤
第8章:用通配符进行过滤
第9章:用正则表达式进行搜索
第10章:创建计算字段
第11章:使用数据处理函数
第12章:汇总数据
第13章:分组数据
第14章:使用子查询
第15章:联结表
第16章:创建高级联结
第17章:组合查询
第18章:全文本搜索
第19章:插入数据
第20章:更新和删除表
第21章:创建和操纵表
第22章:使用视图
第23章:使用存储过程
第24章:使用游标
第25章:使用触发器
第26章:管理事务处理
第27章:全球化和本地化
第28章:安全管理
第29章:数据库维护
第30章:改善性能
附录:MySQL的数据类型
这一章节主要介绍了一些基本概念:
1、数据库(database):保存有组织的数据的容器。数据库是一个以某种有组织的方式存储的数据集合。理解数据库最简单的方法是将其想象为一个文件柜。此文件柜是一个存放数据的物理位置,不管数据是什么以及如何组织的。
2、表(table):某种特定类型数据的结构化清单。
3、列(column):表中的一个字段。所有表都是由一个或多个列组成的。
4、行(row):表中的一个记录。表中的数据是按行记录的,所保存的每个记录存储在自己的行内。
5、主键(primary key):一列(或一组列),其值能够唯一区分表中每个行。唯一标识表中的这个列(或这组列)称为主键。主键用来表示一个特定的行。没有主键,更新或删除表中特定行很困难,因为没有安全的方法保证只涉及相关的行。
6、SQL(Structured Query Language):结构化查询语言,是一种专门用来与数据库通信的语言。
1、MySQL是一种DBMS,即它是一种数据库软件。数据的所有存储、检索、管理和处理实际上都是由数据库软件----DBMS(数据库管理系统)完成的。
2、DBMS分类:
(1)基于共享文件系统的DBMS,主要用于桌面用途;
(2)基于客户机--服务器的DBMS,MySQL、Oracle以及Microsoft SQL Server等数据库都是基于客户机-服务器的数据库。
3、MySQL工具:
(1)mysql命令行实用程序;
(2)MySQL Administrator;
(3)MySQL Query Browser。
本章主要介绍了如何连接和登陆MySQL,如何用USE选择数据库,如何使用SHOW查看MySQL数据库、表和内部信息。
1、为了连接到MySQL,需要以下信息:主机名+端口+用户名+用户口令;
2、选择数据库:USE 数据库名;
3、了解数据库和表:
(1)SHOW DATABASES:返回可用数据库的一个列表。
(2)SHOW TABLES:返回当前选择的数据库内可用表的列表。
(3)SHOW COLUMNS FROM 表名:对表中的每一个字段返回一行,行中包含字段名、数据类型、是否允许null、键信息、默认值以及其他信息(如:auto_increment)。
(4)SHOW STATUS:用于显示广泛的服务器状态信息。
(5)SHOW CREATE DATABASE和SHOW CREATE TABLE:分别用来显示创建特定数据库或表的MySQL语句。
(6)SHOW GRANTS:用来显示授予用户的安全权限。
(7)SHOW ERRORS和SHOW WARNNINGS:用来显示服务器错误或者警告信息。
本章学习了如何使用SQL的SELECT语句来检索单个表列、多个表列以及所有表列。
1、SELECT语句
为了使用SELECT检索表数据,必须至少给出两条信息:想选择什么以及从什么地方选择。
2、检索单个列
SELECT prod_name FROM products; 从products表中检索一个名为prod_name的列
3、检索多个列
SELECT prod_id, prod_name, prod_price FROM products;
4、检索所有列
SELECT * FROM products;
5、检索不同的行
DISTINCT关键字指示MySQL只返回不同的值。
SELECT DISTINCT vend_id FROM products;
6、限制结果
SELECT prod_name FROM products LIMIT 5; // 返回不多于5行
SELECT prod_name FROM products LIMIT 5, 5; // 从第5行开始,返回5行
7、使用完全限定表名
SELECT prod_name FROM products;
可用用下面的完全限定表名替换:
SELECT products.prod_name FROM products;
SELECT products.prod_name FROM mysql_study.products;
本章主要讲如何使用SELECT语句的ORDER BY子句,根据排序检索出的数据。
1、排序数据:未进行排序时,检索出的数据并不是纯粹的随机顺序显示的。如果不排序,数据一般将以它在底层表中出现的顺序显示。这可以是数据添加到表中的顺序。但是,如果数据后来进行过更新或者删除,则此顺序将会受到MySQL重用回收存储空间的影响。因此,不明确控制的话,不能(也不应该)依赖该排序顺序。
2、按单个列排序
SELECT prod_name FROM products ORDER BY prod_name;
3、按多个列排序
SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price, prod_name;
说明:1、为了按多个列排序,只要指定列名,列名之间用逗号分开即可。
2、如上面的SQL语句所示:仅在多个行具有相同的prod_price值时才对产品按prod_name进行排序。如果prod_price值都是唯一的,则不会按prod_name排序。
4、按指定方向排序
默认的排序方式是升序(ASC),如果要倒序排列则需要在对应的列后面加上DESC;
SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price DESC;
说明:DESC只作用于直接位于其前面的列名,如下SQL语句中,只对prod_price列指定DESC,对prod_name列不指定。
SELECT prod_id, prod_price, prod_name FROM products ORDER BY prod_price DESC, prod_name;
5、ORDER BY 和LIMIT组合使用
找出一个列中最大或最小的值
SELECT prod_price FROM products ORDER BY prod_price DESC LIMIT 1;
6、ORDER BY子句的位置
在给出ORDER BY子句时,应该保证它位于FROM子句之后。如果使用LIMIT,它必须位于ORDER BY之后。
本章将介绍如何使用SELECT语句的WHERE子句过滤返回的数据。
1、使用WHERE子句,WHERE后面的称为过滤条件
SELECT prod_name, prod_price FROM products WHERE prod_price = 2.50;
2、WHERE子句操作符
操作符 | 说明 |
---|---|
= | 等于 |
<> | 不等于 |
!= | 不等于 |
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
BETWEEN | 在指的ing的两个值之间 |
3、检查单个值
SELECT prod_name, prod_price FROM products WHERE prod_name = 'fuses';
SELECT prod_name, prod_price FROM products WHERE prod_price < 10;
SELECT prod_name, prod_price FROM products WHERE prod_price <= 10;
4、不匹配检查
SELECT vend_id, prod_name FROM products WHERE vend_id <> 1003;
SELECT vend_id, prod_name FROM products WHERE vend_id != 1003;
5、范围值检查
SELECT prod_name, prod_price FROM products WHERE prod_price BETWEEN 5 AND 10;
6、空值检查
SELECT prod_name FROM products WHERE prod_price IS NULL;
本章讲解如何组合WHERE子句以建立功能更强的更高级的搜索条件,以及学习如何使用NOT和IN操作符。
1、组合WHERE子句
MySQL允许给出多个WHERE子句。这些子句可以有两种方式的使用:以AND子句的方式或OR子句的方式使用。
2、AND操作符
操作符(operator):用来联结或改变WHERE子句中的子句关键字。也称为逻辑操作符。
SELECT prod_id, prod_price, prod_name FROM products WHERE vend_id = 1003 AND prod_price <= 10;
AND:用在WHERE子句中的关键字,用来指示检索满足所有给的的条件。上诉SQL语句中是两个过滤条件,如果是多个则每添加一条就要使用一个AND。
3、OR操作符
SELECT prod_price, prod_name FROM products WHERE vend_id = 1002 OR vend_id = 1003;
OR:指示MySQL检索匹配任一条件的行。
4、计算次序
WHERE可以包含任意数目的AND和OR操作符,允许两者结合以进行复杂和高级的过滤。
下面写出这样一个业务需求的SQL语句:选择由供应商1002或1003制造的且价格都在10美元(含)以上的所有产品:
SELECT prod_name, prod_price FROM products WHERE vend_id = 1002 OR vend_id = 1003 AND prod_price >= 10;
说明:运行结果里会发现查询的结果里有价格小于10美元的记录,显然是错误的,这是因为AND的执行次次序优先级高于OR,所以上面的这条SQL语句代表的结果是:由供应商1003制造的价格为10美元(含)以上的产品,或者由供应商1002制造的任何产品而不管其价格如何。
那么正确的写法是,要使用括号明确分组操作符:
SELECT prod_name, prod_price FROM products WHERE (vend_id = 1002 OR vend_id = 1003) AND prod_price >= 10;
说明:任何时候使用具有AND和OR操作符的WHERE语句,都应该使用圆括号明确地分组操作符。不要过分依赖默认的计算次序,即使它确实是你想要的结果。使用圆括号没有什么坏处,它能消除歧义。
5、IN操作符
IN操作符用来指定条件范围,范围中的每个条件都可以进行匹配。它与OR的功能相同。
SELECT prod_name, prod_price FROM products WHERE vend_id IN (1002, 1003) ORDER BY prod_name;
SELECT prod_name, prod_price FROM products WHERE vend_id = 1002 OR vend_id = 1003 ORDER BY prod_name;
说明:为什么要使用IN操作符?
(1)在使用长的合法选项清单时,IN操作符的语法更清楚且直观,在括号里逗号隔开即可;
(2)在使用IN时,计算的次序更容易管理,因为操作符更少;
(3)IN操作符一般比OR操作符清单执行更快;
(4)IN的最大优点是可以包含其他SELECT语句,使得能够更动态地建立WHERE语句。
6、NOT操作符
SELECT prod_name, prod_price FROM products WHERE vend_id NOT IN (1002, 1003) ORDER BY prod_name;
说明:WHERE子句中的NOT操作符有且只有一个功能:否定它之后所跟的任何条件。
本章介绍什么是通配符、如何使用通配符以及怎样使用LIKE操作符进行通配搜索,以便对数据进行复杂过滤。
通配符(wildcard):用来匹配值的一部分特殊字符。
搜索模式(search pattern):由字面值、通配符或两者组合成的搜索条件。
1、百分号(%)通配符
// 检索任意以jet开头的词,jet之后任意字符
SELECT prod_id, prod_name FROM products WHERE prod_name LIKE 'jet%';
// 通配符可以在搜索模式中的任意位置使用,并且可以使用多个通配符
// 匹配任何位置包含anvil的值,不管它之前或之后是什么
SELECT prod_id, prod_name FROM products WHERE prod_name LIKE '%anvil%';
// 以s开头,以e结尾
SELECT prod_name FROM products WHERE prod_name LIKE 's%e';
注意:%不能匹配NULL,如:WHERE prod_name LIKE ‘%’ 不能匹配出值为NULL的产品。
2、下划线(_)通配符
%可以匹配任意多个字符(含0个),而_只能匹配一个字符,不能多也不能少。
SELECT prod_id, prod_name FROM products WHERE prod_name LIKE '_ ton anvil';
3、使用通配符的技巧:
(1)不要过度使用通配符。如果其他操作符能达到相同的目的,应该使用其他操作符;
(2)在确实需要通配符时,除非绝对有需要,否则不要把它们用在搜索模式的开始处,否则会很慢;
(3)注意通配符的位置。如果放错地方,则可能返回错误的数据。
1、正则表达式的介绍:正则表达式是用来匹配文本的特殊的串(字符集合)。
例如:如果你想从一个文本文件中提取电话号码,可以使用正则表达式;如果你想查找名字中间有数字的所有文件,可以使用正则表达式;如果你想在一个文本块中找到所有重复的单词,可以使用一个正则表达式......
2、基本字符匹配 REGEXP
SELECT prod_name FROM products WHERE prod_name REGEXP '1000' ORDER BY prod_name;
// . 是正则表达式语言中一个特殊的字符,表示匹配任意一个字符
SELECT prod_name FROM products WHERE prod_name REGEXP '.000' ORDER BY prod_name;
3、进行OR匹配
// | 表示匹配其中之一
SELECT prod_name FROM products WHERE prod_name REGEXP '1000|2000|3000' ORDER BY prod_name;
4、匹配几个字符之一
// 【123】定义一组字符,它的意思是匹配1或2或3
SELECT prod_name FROM products WHERE prod_name REGEXP '[123] Ton' ORDER BY prod_name;
// [^123] 表示匹配除这些字符以外的任何东西
SELECT prod_name FROM products WHERE prod_name REGEXP '[^123] Ton' ORDER BY prod_name;
5、匹配范围
集合用来定义要匹配的一个或多个字符。
例如:[0123456789]可以用[0-9]替换,其他表示如:[a-z]
SELECT prod_name FROM products WHERE prod_name REGEXP '[1-5] Ton' ORDER BY prod_name;
6、匹配特殊字符
说明:特殊字符需要用\\进行转义
SELECT vend_name FROM vendors WHERE vend_name REGEXP '\\.' ORDER BY vend_name;
7、匹配多个实例
元字符 | 说明 |
---|---|
* | 0个或多个匹配 |
+ | 1个或多个匹配(等于{1, }) |
? | 0个或1个匹配(等于{0, }) |
{n} | 指定数目的匹配 |
{n, } | 不少于指定数目的匹配 |
(n, m) | 匹配数目的范围(m不超过255) |
下面举几个例子:
// ?使s可选,出现0次或者1次
SELECT prod_name FROM products WHERE prod_name REGEXP '\\([0-9] sticks?\\)' ORDER BY prod_name;
8、定位符的使用
元字符 | 说明 |
---|---|
^ | 文本的开始 |
$ | 文本的结束 |
[ [:<:] ] | 词的开始 |
[ [:>:] ] | 词的结束 |
// 找出以数字(包括小数点)开始的所有产品名
SELECT prod_name FROM products WHERE prod_name REGEXP '^[0-9\\.]' ORDER BY prod_name;
9、关于LIKE和REGEXP的区别
LIKE匹配整个列。如果被匹配的文本仅在列值中出现,LIKE并不会找到它,相应的行也不会返回(当然,使用通配符除外)。
而REGEXP在列值内进行匹配,如果被匹配的匹配的文本在列值中出现,REGEXP将会找到它,相应的行将被返回,这时一个非常重要的差别(当然,如果适应定位符号^和$,可以实现REGEXP匹配整个列而不是列的子集)。
本章介绍什么是计算字段,如何创建计算字段以及怎样从应用程序中使用别名引用它们。
1、计算字段:存储在表中的数据都不是应用程序所需要的,我们需要直接从数据库中检索出转换、计算或格式化的数据;而不是检索出数据,然后再在客户机应用程序中进行格式化。
2、拼接字段:Concat()函数
SELECT CONCAT(vend_name, '(', vend_country, ')') FROM vendors ORDER BY vend_name;
// RTRIM():去掉右边的所有空格,LTrim():去掉左边的所有空格,Trim():去掉所有空格
SELECT CONCAT(vend_name, '(', RTRIM(vend_country), ')') FROM vendors ORDER BY vend_name;
3、使用别名(alias)
AS:关键字后跟别名,客户机可以按别名引用这个列
SELECT CONCAT(vend_name, '(', RTRIM(vend_country), ')') AS vend_title FROM vendors ORDER BY vend_name;
4、执行算术计算
SELECT prod_id, quantity, item_price, quantity*item_price AS total_price FROM orderitems WHERE order_num = 20005;
本章介绍什么是函数,MySQL支持何种函数,以及如何使用这些函数。
1、文本处理函数
// Upper()函数:将文本转换为大写
SELECT vend_name, UPPER(vend_name) AS vend_name_upcase FROM vendors ORDER BY vend_name;
常用的文本处理函数:
函数 | 说明 |
---|---|
Left() | 返回串左边的字符 |
Length() | 返回串的长度 |
Locate() | 找出串的一个字串 |
Lower() | 将串转化为小写 |
LTrim() | 去掉串左边的空格 |
Right() | 返回串右边的字符 |
RTrim() | 去掉串右边的空格 |
Soundex() | 返回串的SOUNDEX值 |
SubString() | 返回子串的字符 |
Upper() | 将串转化为大写 |
2、日期和时间处理函数
常见的日期和时间处理函数
函数 | 说明 |
---|---|
AddDate() | 增加一个日期(天、周等) |
AddTime() | 增加一个时间(时、分等) |
CurDate() | 返回当前日期 |
CurTime() | 返回当前时间 |
Date() | 返回日期时间的日期部分 |
DateDiff() | 计算两个日期之差 |
Date_Add() | 高度灵活的日期运算函数 |
Date_Format() | 返回一个格式化的日期或时间串 |
Day() | 返回一个日期的天数部分 |
DayOfWeek() | 对于一个日期,返回对应的星期几 |
Hour() | 返回一个时间的小时部分 |
Minute() | 返回一个时间的分钟部分 |
Month() | 返回一个日期的月份部分 |
Now() | 返回当前日期和时间 |
Second() | 返回一个时间的秒的部分 |
Time() | 返回一个日期时间的时间部分 |
Year() | 返回一个日期时间的年份部分 |
下面举两个例子:
// SQL中年月日的写法:2005-09-01
SELECT cust_id, order_num FROM orders WHERE order_date = '2005-09-01';
这里需要提醒的是:上面的SQL语句并不可靠,因为order_date的数据类型为datatime。这种数据类型存储日期和时间值,所以上面的SQL语句实际上代表的是:2015-09-01 00:00:00,实际中肯定很多都不是这样整点的记录,所以如果我们想要仅比较日期部分的话需要使用Date()函数,如下所示:
SELECT cust_id, order_num FROM orders WHERE DATE(order_date) = '2005-09-01';
如果你想要查询2005年9月的所有订单,可以使用下面两种SQL语句:
SELECT cust_id, order_num FROM orders WHERE DATE(order_date) BETWEEN '2005-09-01' AND '2005-09-30';
或者
SELECT cust_id, order_num FROM orders WHERE YEAR(order_date) = 2005 AND MONTH(order_date) = '09';
3、数值处理函数
常用的数值处理函数如下表所示:
函数 | 说明 |
---|---|
Abs() | 返回一个数的绝对值 |
Cos() | 返回一个角度的余弦 |
Exp() | 返回一个数的指数值 |
Mod() | 返回操作的余数 |
Pi() | 返回圆周率 |
Rand() | 返回一个随机数 |
Sin() | 返回一个角度的正弦 |
Sqrt() | 返回一个数的平方根 |
Tan() | 返回一个角度的正切 |
本章介绍什么是SQL的聚集函数以及如何使用它们汇总表的数据。
1、聚集函数(aggregate function):运行在行组上,计算和返回单个值的函数。
常用的5个SQL聚集函数如下所示:
函数 | 说明 |
---|---|
AVG() | 返回某列的平均值 |
COUNT() | 返回某列的行数 |
MAX() | 返回某列的最大值 |
MIN() | 返回某列的最小值 |
SUN() | 返回某列值之和 |
2、AVG()函数
AVG()是通过对表中行数计数并计算特定列值之和,求得该列的平均值。AVG()可用来返回所有列的平均值,也可以用来返回特定列或行的平均值。
SELECT AVG(prod_price) AS avg_price FROM products;
SELECT AVG(prod_price) AS avg_price FROM products WHERE vend_id = 1003;
说明:AVG()函数会忽略列值为NULL的行。
3、COUNT()函数
(1)COUNT(*):对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值。
(2)COUNT(column):对特定列中具有值得行进行计数,忽略值为NULL的行。
SELECT COUNT(*) AS num_cust FROM customers;
SELECT COUNT(cust_email) AS num_cust FROM customers;
4、MAX()函数
返回指定列中最大的值,忽略列值为NULL的行。
SELECT MAX(prod_price) AS max_price FROM products;
5、MIN()函数
返回指定列中最小的值,忽略列值为NULL的行。
SELECT MIN(prod_price) AS max_price FROM products;
6、SUM函数
用来返回指定列值的和(总计)。
SELECT SUM(quantity) AS items_ordered FROM orderitems WHERE order_num = 20005;
SELECT SUM(item_price * quantity) AS total_price FROM orderitems WHERE order_num = 20005;
7、聚集不同的值
ALL参数是默认的,不需要指定。如果不指定DISTINCT,则默认为ALL。
SELECT AVG(DISTINCT prod_price) AS avg_price FROM products WHERE vend_id = 1003;
8、组合聚集函数
SELECT语句可以根据需要包含多个聚集函数,如下所示:
SELECT COUNT(*) AS num_items,
MIN (prod_price) AS price_min,
MAX(prod_price) AS price_max,
AVG(prod_price) AS price_avg
FROM products;
本章介绍如何使用分组数据,以便能汇总表内容的子集。涉及到两个新的SELECT语句子句,分别是GROUP BY 和 HAVING子句。
1、创建分组
GROUP BY子句指示MySQL分组数据,然后对每个组而不是整个结果集进行聚集。
SELECT vend_id, COUNT(*) AS num_prods FROM products GROUP BY vend_id;
GROUP BY子句使用的相关规定:
(1)GROUP BY子句可以包含任意数目的列。
(2)如果在GROUP BY子句中嵌套了分组,数据将会在最后规定的分组上进行汇总。
(3)除聚集函数外,SELECT语句中的每个列都必须在GROUP BY子句中给出。
(4)如果分组中具有NULL值,则NULL将作为一个分组返回。如果列中有多行NULL值,它们将分为一组。
(5)GROUP BY子句必须出现在WHERE子句之后,ORDER BY子句之前。
2、过滤分组
目前为止,所学过所有类型的WHERE子句都可以用HAVING来替代。唯一的差别就是WHERE过滤行,而HAVING过滤分组。
SELECT cust_id, COUNT(*) AS orders FROM orders GROUP BY cust_id HAVING COUNT(*) >= 2;
WHERE和HAVING一起使用:
// 列出具有2个及以上、价格为10及以上的产品的供应商
SELECT vend_id, COUNT(*) AS num_prods FROM products WHERE prod_price >= 10 GROUP BY vend_id HAVING COUNT(*) >= 2;
说明:WHERE和HAVING的区别:WHERE在数据分组前进行过滤,HAVING在数据分组后进行过滤。这是一个很重要的区别,WHERE排除的行不包括在分组中。这可能会改变计算值,从而影响HAVING子句中基于这些值过滤掉的分组。
3、分组和排序
ORDER BY:排序产生输出
GROUP BY:分组行,但输出可能不是分组的顺序。
注意:一般在使用GROUP BY的时候也需要给出ORDER BY 子句,这是保证数据正确排序的唯一方法。千万不要仅依赖GROUP BY排序数据。
SELECT order_num, SUM(quantity * item_price) AS ordertotal
FROM orderitems
GROUP BY order_num
HAVING SUM(ordertotal) >= 50
ORDER BY ordertotal;
说明:上面的SQL语句中:GROUP BY子句用来按订单号(order_num列)分组数据,以便SUM(*)函数能够返回总计订单价格。HAVING子句过滤数据,使得只返回总计订单价格大于等于50的订单。最后,用ORDER BY子句排序输出。
本章介绍什么是子查询以及怎么使用它们。子查询最常见的使用是WHERE子句的IN操作符中,以及用来填充计算列。
1、利用子查询进行过滤
这里给出一个案例:需要列出订购物品TNT2的所有客户,应该怎样检索?大致分为三个步骤:
(1)检索包含物品TNT2的所有订单编号;
SELECT order_num FROM orderitems WHERE prod_id = 'TNT2'; // 结果是(20005,20007)
(2)检索具有前一步骤列出的订单编号的所有客户ID;
SELECT cust_id FROM orders WHERE order_num IN(20005,20007); // 结果是(10001,10004)
(3)检索前一步骤返回的所有客户ID的客户信息。
SELECT cust_name, cust_contact FROM customers WHERE cust_id IN(10001,10004);
下面将上面的三条SQL语句转换为子查询语句,子查询是由内向外处理的:
SELECT cust_name, cust_contact
FROM customers
WHERE cust_id IN(SELECT cust_id
FROM orders
WHERE order_num IN(SELECT order_num
FROM orderitems
WHERE prod_id = 'TNT2'));
2、作为计算字段使用子查询
看这样的一个案例:需要显示customers表中每个客户的订单总数。分为两个步骤:
(1)从customers表中检索客户列表;
(2)对于检索出的每个客户,统计其在orders表中的订单数目。
SELECT cust_name,
cust_state,
(SELECT COUNT(*)
FROM orders
WHERE orders.`cust_id` = customers.`cust_id`) AS orders
FROM customers
ORDER BY cust_name;
这里需要注意的是:这类查询称为相关子查询,WHERE orders.`cust_id` = customers.`cust_id`中必须使用全限定列名。
说明:逐渐增加子查询来建立查询:用子查询建立查询的最可靠的方法是逐渐进行,这与MySQL处理它们的方法非常相同。首先建立和测试最内层的查询,然后,用硬编码数据建立外层查询,并且仅在确认它正常后再嵌入子查询。对于增加的每个查询,重复此步骤。
本章将介绍什么是联结、为什么要使用联结、如何编写使用联结的SELECT语句。
1、外键(foreign key):外键为某个表中的一列,它包含另一个表的主键值,定义了两个表之间的关系。
2、为什么要使用联结?
分解数据为多个表能更有效地存储,更方便地处理,并且具有更大的伸缩性,但是这些好处是有代价的。比如:如果数据存储在多个表中,怎样用单条SELECT语句检索出数据呢?答案是使用联结。简单地说,联结是一种机制,用来在一条SELECT语句中关联表,因此称为联结。
3、创建联结
下面SQL语句中的vend_name位于vendors表中,而prod_name,prod_price位于products表中
SELECT vend_name, prod_name, prod_price
FROM vendors, products
WHERE vendors.`vend_id` = products.`vend_id`
ORDER BY vend_name, prod_name;
4、WHERE子句的重要性
在联结两个表时,你实际上做的是将第一个表中的每一行与第二个表中的每一行配对。WHERE子句作为过滤条件,他只包含那些匹配给定条件(这里是联结条件)的行,没有WHERE子句,第一个表中的每个行将与第二个表中的每个行配对,而不管它们逻辑上是否可以配在一起。
笛卡儿积:由没有联结条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。查询出的结果中很多都是重复的信息,不是我们想要的。
SELECT vend_name, prod_name, prod_price
FROM vendors, products
ORDER BY vend_name, prod_name;
5、内部联结
目前为止,所有的联结都是等值联结,它基于两个表之间的相等测试。这种联结也称为内部联结。其实对于这种联结可以用稍微不同的语法来明确指定联结的类型。
SELECT vend_name, prod_name, prod_price
FROM vendors INNER JOIN products
ON vendors.`vend_id` = products.`vend_id`
ORDER BY vend_name, prod_name;等同于:
SELECT vend_name, prod_name, prod_price
FROM vendors, products
WHERE vendors.`vend_id` = products.`vend_id`
ORDER BY vend_name, prod_name;
比较上面的两个SQL语句,查询结果是一样的,只不过FROM语句不同,用INNER JOIN指定两个表之间的关系,另外联结条件用ON子句而不是WHERE子句了。
说明:尽管使用WHERE子句定义联结的确比较简单,但是使用明确的联结语法能够确保不会忘记联结条件。
6、联结多个表
下面看两个例子:
(1)联结三个表:
SELECT prod_name, vend_name, prod_price, quantity
FROM orderitems, products, vendors
WHERE products.`vend_id` = vendors.`vend_id`
AND orderitems.`prod_id` = products.`prod_id`
AND order_num = 20005;
(2)返回订购产品TNT2的客户列表,之前用的是子查询,现在用联结查询:
1、子查询方式:
SELECT cust_name, cust_contact
FROM customers
WHERE cust_id IN(SELECT cust_id
FROM orders
WHERE order_num IN(SELECT order_num
FROM orderitems
WHERE prod_id = 'TNT2'));2、联结查询方式:
SELECT cust_name, cust_contact
FROM customers, orders, orderitems
WHERE customers.`cust_id` = orders.`cust_id`
AND orderitems.`order_num` = orders.`order_num`
AND prod_id = 'TNT2';
本章讲解另外一些联结类型以及介绍如何对被联结的表使用表别名和聚集函数。
1、使用表别名
(1)缩短SQL语句;
(2)允许在单条SELECT语句中多次使用相同的表。
2、自联结
自联结通常作为外部语句用来代替从相同表中检索数据时使用的子查询语句。
举个例子:假如你发现某物品(其中ID为DTNTR)存在问题,因此想知道生产该物品的供应商生产的其他物品是否也存在这些问题。
方案一:使用子查询
SELECT prod_id, prod_name
FROM products
WHERE vend_id = (SELECT vend_id
FROM products
WHERE prod_id = 'DTNTR');
方案二:使用联结查询
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';
说明:方案二中使用联结查询中使用的两个表其实都是products表,但对products的引用具有二义性,所以使用了别名。
3、自然联结
无论何时对表进行联结,应该至少有一个列出现在不止一个表中(被联结的列)。标准的联结(内联结)返回所有的数据,甚至相同的列出现多次。自然联结排除多次出现,使每个列只返回一次。
怎么完成这项工作呢?答案是:系统不完成这项工作,由你自己完成它。自然联结是这样一种联结,其中你只能选择那些唯一的列。这一般是通过对表使用通配符(SELECT *),对所有其他表的列使用明确的子集来完成的。
说明:目前为止,我们建立的每个内联结都是自然联结。
SELECT c.*, o.`order_num`, o.`order_date`, oi.`prod_id`, oi.`quantity`, oi.`item_price`
FROM customers AS c, orders AS o, orderitems AS oi
WHERE c.`cust_id` = o.`cust_id`
AND oi.`order_num` = o.`order_num`
AND prod_id = 'FB';
4、外部联结
联结包含了那些在相关表中没有关联行的行。这种类型的联结称为外部联结。
外联结特点:查询出的结果存在不满足条件的可能。
例如下面这些场景:1、对每个客户下了多少订单进行计数,包括那些至今未下订单的用户;2、列出所有产品以及订购数量,包括还没有人订购的产品.......
(1)左外联结
左外联结是先查出左表的所有记录(即以左表为主),然后查询右表,右表中满足条件的显示出来,不满足条件的显示为NULL。
-- 对每个客户下了多少订单进行计数,包括那些至今未下订单的用户
SELECT customers.`cust_id`, orders.`order_num`
FROM customers LEFT OUTER JOIN orders
ON customers.`cust_id` = orders.`cust_id`;
(2)右外联结
右外联结是先查出右表的所有记录(即以右表为主),然后查询左表,左表中满足条件的显示出来,不满足条件的显示为NULL。
-- 下面这段SQL语句采用的是右外联结,而右表是订单orders表,所以不会显示出至今未下订单的用户
SELECT customers.`cust_id`, orders.`order_num`
FROM customers RIGHT OUTER JOIN orders
ON orders.`cust_id` = customers.`cust_id`;
5、使用带聚集函数的联结
案例:如果要检索每个客户所下的订单数
SELECT customers.`cust_name`, customers.`cust_id`,COUNT(orders.`order_num`) AS num_ord
FROM customers INNER JOIN orders
ON customers.`cust_id` = orders.`cust_id`
GROUP BY customers.`cust_id`;
6、使用联结和联结条件总结
(1)注意所使用的联结类型。一般我们使用内部联结,但使用外部联结也是有效的;
(2)保证使用正确的联结条件,否则将返回不正确的数据;
(3)应该总是提供联结条件,否则会得出笛卡儿积;
(4)在一个联结中可以包含多个表,甚至对每个联结可以采用不同的联结类型。虽然这样做是合法的,一般也很有用,但应该在一起测试它们前,分别测试每个联结。这样使故障排除更加简单。
本章讲诉如何利用UNION操作符将多条SELECT语句合成一个结果,不管它的结果中包含还是不包含重复。使用UNION可以极大地简化复杂的WHERE子句,简化从多个表中检索数据的工作。
1、组合查询:MySQL允许执行多个查询(多条SELECT语句),并将结果作为单个查询结果集返回。这些组合查询通常称为并(union)或复合查询(compound query)。
下面这两种基本情况,需要使用组合查询:
(1)在单个查询中从不同的表返回类似结构的数据;
(2)对单个表执行多个查询,按单个查询返回数据。
2、使用UNION
UNION的使用非常简单。所做的就是给出每条SELECT语句,在各条语句之间放上关键字UNION。
案例:假如需要价格小于等于5的所有物品的一个列表以及供应商1001,1002生产的所有物品(不考虑价格)。
SELECT vend_id, prod_id, prod_price
FROM products
WHERE prod_price <=5
UNION
SELECT vend_id, prod_id, prod_price
FROM products
WHERE vend_id IN(1001, 1002)
ORDER BY vend_id, prod_price;
-- 或者使用多条WHERE子句:
SELECT vend_id, prod_id, prod_price
FROM products
WHERE prod_price <= 5
OR vend_id IN(1001, 1002)
ORDER BY vend_id, prod_price;
说明:
(1)UNION包含或取消重复的行:UNION从查询结果集中自动去除了重复的行,即它的行为与单条SELECT语句中使用多个WHERE子句条件一样。如果想返回所有的匹配行,包括重复的行,可以使用UNION ALL。
(2)对组合查询结果排序:在使用UNION组合查询时 ,只能使用一条ORDER BY子句,必须是在最后一个SELECT语句后面。虽然它似乎只是最后一条SELECT语句的组成部分,但实际上MySQL将用它来排序所有SELECT语句返回的所有结果。
本章介绍了为什么要使用全文本搜索、如何使用MySQL的Match()和Against()函数进行全文本搜索以及查询扩展(它能增加找到相关匹配的机会)和如何使用布尔方式进行更细致的查找控制。
在开篇之前,先推荐阅读索引相关的两篇文章:MySQL中的索引1、MySQL中的索引2
1、为了进行全文本索引,必须索引被搜索的列,而且要随着数据的改变不断地重新索引。
2、启用全文本搜索的支持
一般在创建表时启用全文本搜索,FULLTEXT子句
案例:下面SQL语句中,有一个note_text的列,为了进行全文本搜索,可以使用FULLTEXT(note_text)的指示对它进行索引。
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;
3、进行全文本搜索
使用Match()和Against()执行全文本搜索,其中Match()指定被搜索的列,Against()指定要使用的搜索表达式。
SELECT note_text
FROM productnotes
WHERE MATCH(note_text) AGAINST('rabbit');
说明:
(1)传递给MATCH()中的值必须与FULLTEXT()定义中的相同;
(2)包含搜索文本的每个行都有一个等级值,文本靠前的行的等级值要比词靠后行的等级值高;
4、使用查询扩展
查询扩展用来设法放宽所返回的全文本搜索结果的范围。
案例:如果你想找出所有提到anvils的注释。只有一个注释包含词anvils,但是你还想找出可能与你的搜索有关的所有其他行,即使它们不包含anvils。可以采用如下方法:
(1)进行一个基本的全文本搜索,找出与搜索条件匹配的所有有用的词;
(2)MySQL检查这些匹配行并选择所有有用的词;
(3)MySQL再次进行全文本搜索,这次不仅仅使用原来的条件,而且还使用所有有用的词;
SELECT note_text
FROM productnotes
WHERE MATCH(note_text) AGAINST('anvils' WITH QUERY EXPANSION);
5、布尔文本搜索
MySQL支持全文本搜索的另一种方式,称为布尔方式(boolean mode),具体细节如下:
(1)要匹配的词;
(2)要排斥的词;
(3)排列提示(指定某些词比其他词更重要,更重要词的等级要高);
(4)表达式分组。
全文本布尔操作符如下表所示:
布尔操作符 | 说明 |
---|---|
+ | 包含,词必须存在 |
- | 排除,词必须不出现 |
> | 包含,而且增加等级值 |
< | 包含,而且减少等级值 |
() | 把词组成子表达式 |
~ | 取消一个词的排序值 |
* | 词尾的通配符 |
'' '' | 定义一个短语 |
下面随便举几个布尔文本搜索的例子:
SELECT note_text
FROM productnotes
WHERE MATCH(note_text) AGAINST('heavy -rope*' IN BOOLEAN MODE);
SELECT note_text
FROM productnotes
WHERE MATCH(note_text) AGAINST('+rabit +bait' IN BOOLEAN MODE);
SELECT note_text
FROM productnotes
WHERE MATCH(note_text) AGAINST(''rabit bait'' IN BOOLEAN MODE);
6、全文本搜索的使用说明
(1)在索引全文本数据时,短词(3个及3个以下)被忽略且从索引中排除;
(2)MySQL带有一个内建的非用词(stopword)列表,这些词在索引全文本数据时总是被忽略;
(3)许多词出现的频率很高,搜索它们没有什么用处(返回的结果太多)。MySQL规定一个词出现在50%以上的行中,则将它作为一个非用词忽略。特殊说明:50%规则不适用于IN BOOLEAN MODE;
(4)忽略词中的单引号;
.................................
本章介绍如何利用SQL的INSERT语句将数据插入表中。
INSERT是用来插入行到数据库表的。插入的情况有以下的几种情况:
(1)插入完整的行;
(2)插入行的一部分;
(3)插入多行;
(4)插入某些查询的结果。
1、插入完整的行
INSERT INTO customers
VALUES(NULL,
'Pep E.LaPew',
'100 Main Street',
'Los Angels',
'CA',
'90046',
'USA',
NULL,
NULL);
说明:上面的SQL语句高度依赖于表中的定义次序,这样是不安全的,特别是表结构变化后,很容易出错。
推荐使用下面这种给出列名的SQL写法:
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 Angels',
'CA',
'90046',
'USA',
NULL,
NULL);
上面这样写的好处:
(1)VALUES按照前面指定列的顺序,而无需按照表中的顺序,这样即使表结构发生变化了,也没有关系;
(2)选择性的插入,有默认值或者允许为空的列如果无需插入值,则可以不写。
2、插入多个行
INSERT INTO customers(cust_name,
cust_address,
cust_city,
cust_state,
cust_zip,
cust_country)
VALUES('Pep E.LaPew',
'100 Main Street',
'Los Angels',
'CA',
'90046',
'USA'); -- 分号隔开
INSERT INTO customers(cust_name,
cust_address,
cust_city,
cust_state,
cust_zip,
cust_country)
VALUES('M.Martian',
'42 Main Street',
'New York',
'NY',
'11213',
'USA');
-- 或者:
INSERT INTO customers(cust_name,
cust_address,
cust_city,
cust_state,
cust_zip,
cust_country)
VALUES('Pep E.LaPew',
'100 Main Street',
'Los Angels',
'CA',
'90046',
'USA'), -- 逗号隔开
('M.Martian',
'42 Main Street',
'New York',
'NY',
'11213',
'USA');
说明:第二种SQL语句单条INSERT语句有多组值,每组值用一对圆括号括起来,多条用逗号隔开。这样可以提高数据库处理的性能。因为MySQL用单条INSERT语句处理多个插入比使用多条INSERT语句快。
3、插入检索出的数据 INSERT SELECT
INSERT INTO customers(cust_name,
cust_address,
cust_city,
cust_state,
cust_zip,
cust_country)
SELECT cust_name,
cust_address,
cust_city,
cust_state,
cust_zip,
cust_country
FROM custnew;
说明:这条数据插入多少行取决于custnew表中有多少行。而且不要求两个表中的字段相同,只管把从custnew表中查出的值作为value插入到customers中,但是实际上两个表的字段应该是一样的,至少有一些相同的字段。
本章介绍如何使用UPDATE和DELETE语句进一步操纵表数据。
1、更新数据
包括更新表中特定的行和更新表中所有的行。
UPDATE customers
SET cust_name = 'The Fudds',
cust_email = '[email protected]'
WHERE cust_id = 10005;
说明:UPDATE 和 SET
(1)SET命令后面可以跟多个需要更新的列值;
(2)一般的UPDATE语句在更新多行的时候,如果出现错误,则这整个UPDATE操作将会被取消,所有数据恢复到更新操作之前的数据。但是如果你想UPDATE发生错误的时候仍然继续进行更新操作,那么需要使用IGNORE关键字,UPDATE IGNORE customers.........
(3)使用UPDATE删除某个列的值,只需要将其设置为NULL即可,如下所示:
UPDATE customers
SET cust_email = NULL
WHERE cust_id = 10005;
2、删除数据
删除数据包括:从表中删除特定的行和删除所有的行。
DELETE FROM customers
WHERE cust_id = 10006;
说明:
(1)DELETE不需要列名或者通配符,因为删除的是整行而不是列,如果要删除列,使用UPDATE语句;
(2)注意WHERE语句的使用,如果不加WHERE语句,就是删除整张表里面的记录了;
(3)需要注意的是DELETE删除所有行时不是删除表的本身,而是删除表中的记录。
(4)如果想删除表中所有行,还可以使用TRUNCATE TABLE语句,速度比DELETE更快。TRUNCATE实际是删除原来的表并重新创建一个表,而不是逐行删除表中的数据。
本章主要讲表的创建、更改和删除的基本知识。CREATE TABLE创建表、ALTER TABLE更改表、DROP TABLE删除表。
1、创建表
CREATE TABLE customers
(
cust_id INT NOT NULL AUTO_INCREMENT,
cust_name CHAR(50) NOT NULL DEFAULT ok,
cust_adress CHAR(50) NULL,
cust_city CHAR(50) NULL,
cust_state CHAR(50) NULL,
cust_zip CHAR(50) NULL,
cust_country CHAR(50) NULL,
cust_contact CHAR(50) NULL,
cust_email CHAR(50) NULL,
PRIMARY KEY(cust_id)
ENGINE=INNODB;
)
说明:
(1)AUTO_INCRMENT为自增,一般都设置在主键上,需要说明的是每个表只允许有一个AUTO_INCREMENT的列,而且它必须被索引。但是自增的列有一个弊端就是,你不清楚它当前的值,所以需要一个函数:LAST_INSERT_ID()函数来获取
(2)DEFAULT关键字指定默认值,一般开发人员都是使用默认值而不是使用NULL;
(3)PRIMARY KEY说明:如果主键使用单个列,那么它的值必须唯一。如果使用多个列,则这些列的组合值必须唯一。
(4)NULL代表当前列可以为空,插入数据时可以不给值,NOT NULL代表当前列不能为空,插入数据时必须给值,或者设置默认值。
(5)引擎类型:MySQL中的常用引擎有InnoDB、MyISAM、MEMORY等等,而且可以混用,但是需要注意的是:外键不能跨引擎,即使用一个引擎的表不能引用具有使用不同引擎的表的外键。
2、更新表
-- 1、给表中增加一列
ALTER TABLE vendors
ADD vend_phone CHAR(20);
-- 2、删除刚才增加的列
ALTER TABLE vendors
DROP COLUMN vend_phone;
-- 3、定义外键(常用)
ALTER TABLE orderitems
ADD CONSTRAINT fk_orderitems_orders
FOREIGN KEY (order_num) REFERENCES orders (order_num);
这里需要说明的是复杂的表结构,一般无法通过简单的SQL语句进行更改,需要手动进行更改,涉及以下步骤:
(1)用新的列布局创建一个新表;
(2)使用INSERT SELECT语句从旧表中复制数据到新表。如果有必要,可以使用转换函数和计算字段;
(3)校验包含所需数据的新表;
(4)重新命名旧表(如果确定不需要,可以直接删除);
(5)用旧表的名字重命名新表;
(6)根据需要,重新创建触发器,存储过程,索引和外键。
3、删除表
DROP TABLE customers;
4、重命名表
RENAME TABLE backup_customers TO customers,
backup_vendors TO vendors;
本章将介绍试图是什么,怎样工作、如何使用以及试图简化之前的SQL操作。
1、视图:视图是虚拟的表。与包含数据的表不一样,视图只包含使用时动态检索数据的查询。一般视图主要用于检索数据,很少用于更新(INSERT UPDATE DELETE)。
2、为什么使用视图?
(1)重用SQL语句;
(2)简化复杂的SQL操作。在编写查询后,可以方便地重用它而不必知道它的基本查询细节;
(3)使用表的组成部分而不是整个表;
(4)保护数据。可以给用户授予表的特定部分的访问权限而不是整个表的访问权限;
(5)更改数据格式和表示。视图可以返回与底层表的表示和格式不同的数据。
3、视图的规则和限制
(1)与表一样,视图必须唯一命名;
(2)对于可以创建的视图数目没有限制;
(3)为了创建视图,必须具有足够的访问权限;
(4)视图可以嵌套,即可以利用从其他视图中检索数据的查询来构造一个视图;
(5)ORDER BY可以用在视图中,但是如果从该视图检索数据的SELECT语句中也包含ORDER BY,那么该视图中的ORDER BY将被覆盖;
(6)视图不能有索引,也不能有关联的触发器或者默认值;
(7)视图可以和表一起使用。
4、使用视图
(1)视图用CREATE VIEW语句来创建;
(2)使用SHOW CREATE VIEW viewname;来查看创建的视图语句;
(3)使用DROP VIEW viewname删除视图,
(4)更新视图:可以先DROP再CREATE,也可以使用CREATE OR REPLACE VIEW。
5、利用视图简化复杂的联结
视图最常见的应用之一就是隐藏复杂的SQL
-- 创建视图
CREATE VIEW productcustomers AS
SELECT cust_name, cust_contact, prod_id
FROM customers, orders, orderitems
WHERE customers.`cust_id` = orders.`cust_id`
AND orderitems.`order_num` = orders.`order_num`;
-- 使用视图,一次编写,多次使用,简化了多次编写基础SQL语句的任务
SELECT cust_name, cust_contact
FROM productcustomers
WHERE prod_id = 'TNT2';
6、用视图重新格式化检索出的数据
-- 创建视图
CREATE VIEW vendorlocations AS
SELECT CONCAT(RTRIM(vend_name), '(', RTRIM(vend_country),')')
AS vend_title
FROM vendors
ORDER BY vend_name;
-- 使用视图
SELECT * FROM vendorlocations;
7、用视图过滤不想要的数据
-- 创建一个视图,排除没有邮箱的用户
CREATE VIEW customeremaillist AS
SELECT cust_id, cust_name, cust_email
FROM customers
WHERE cust_email IS NOT NULL;
-- 使用视图
SELECT * FROM customeremaillist;
8、使用视图与计算字段
视图对于简化计算字段也特别有用。
-- 创建视图
CREATE VIEW orderitemsexpanded AS
SELECT order_num,
prod_id,
quantity,
item_price,
quantity * item_price AS expanded_price
FROM orderitems;
-- 使用视图
SELECT *
FROM orderitemsexpanded
WHERE order_num = 20005;
9、更新视图
如果在试图中定义了如下操作,则不能对视图进行更新:
分组(使用GROUP BY 和 HAVING),联结、子查询、并、聚集函数、DISTINCT、导出(计算)列...........
可以看出来很多视图都是无法更新的,但是实际上视图主要是用来数据检索的。
本章介绍什么是存储过程,为什么要使用存储过程以及如何使用存储过程,并且介绍了创建和使用存储过程的基本语法。
1、存储过程:简单点说就是为以后的使用而保存的一条或多条SQL语句的集合。
2、为什么要使用存储过程?
(1)通过把处理封装在容易使用的单元中,简化复杂的操作;
(2)由于不要求反复建立一系列处理步骤,这保证了数据的完整性。如果所有开发人员和应用程序都使用同一存储过程,则所使用的代码都是相同的。
(3)提高性能:使用存储过程要比使用单独的SQL语句快。
简单点说,使用存储过程的好处就是:简单、安全、高性能。
缺点:
(1)编写存储过程要比基本的SQL语句复杂;
(2)需要创建存储过程的安全访问权限,但是MySQL中将编写存储过程的权限和执行存储过程的权限分开了,所以即便你没有权限创建存储过程,但是可以使用已经存在的存储过程。
3、创建存储过程
-- 创建一个返回产品平均价格的存储过程,这里存储过程没有参数
CREATE PROCEDURE prodcutpricing()
BEGIN
SELECT AVG(prod_price) AS priceaverage
FROM products;
END;
-- 调用存储过程
CALL prodcutpricing();
4、删除存储过程
DROP PROCEDURE productpricing;
5、使用参数
变量:内存中一个特定的位置,用来临时存储数据。
-- 创建存储过程
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;
-- 调用用存储过程,必须也是三个参数
CALL productpricing(@pricelow,
@priceheigh,
@priceaverage);
-- 注意:调用存储过程时,不显示任何数据。它返回后可以显示的变量
-- 根据需要,显示变量:
SELECT @priceaverage;
SELECT @pricelow, @priceheigh, @priceaverage;
说明:此存储过程接受3个参数,pl存储产品最低价格,ph存储产品最高价格,pa存储产品平均价格
IN:传递给存储过程、OUT:从存储过程中传出、INOUT:对存储过程传入和传出
下面在列举一个使用IN接收参数的存储过程:
-- 创建存储过程
CREATE PROCEDURE ordertotal(
IN onumber INT,
OUT total DECIMAL(8,2)
)
BEGIN
SELECT SUM(item_price * quantity)
FROM orderitems
WHERE order_num = onumber
INTO ototal;
END;
-- 调用存储过程,要传两个参数
CALL ordertotal(20005, @total);
-- 显示参数
SELECT @total;
说明:onumber定义为IN,因为订单号被传入存储过程。ototal定义为OUT,因为要从存储过程返回合计。WHERE子句使用onumber选择正确的行,INTO使用ototal存储计算出来的合计。
6、建立智能存储过程
下面列举一个例子:你需要获得与以前一样的订单合计,但需要对合计增加营业税,不过只针对某些顾客,那么你需要做以下这几件事情:
(1)获得合计;
(2)把营业税有条件地添加到合计;
(3)返回合计。
完整的存储过程SQL代码如下所示:
--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
-- Yes, so add taxrate to the total
SELECT total+(total/100*taxrate) INTO total;
END IF;
-- And finally, save to out variable
SELECT total INTO ototal;
END;
7、检查存储过程
SHOW CREATE PROCEDURE 存储过程名;
SHOW PROCEDURE STATUS;
本章将讲解什么是游标,以及如何使用游标。
1、游标(cursor):是一个存储在MySQL服务器上的数据库查询,它不是一条SELECT语句,而是被该语句检索出来的结果集,需要在检索出来的行中前进或者后退一行或者多行。
需要说明的是:MySQL中的游标必须在存储过程中使用。
2、创建游标
CREATE PROCEDURE processorders()
BEGIN
DECLARE ordernumbers CURSOR
FOR
SELECT order_num FROM orders;
END;
3、打开和关闭游标
OPEN processorders;
CLOSE processorders;
4、使用游标的案例
说明:当一个游标被打开后,可以使用FETCH语句分别访问它的每一行。FETCH指定检索什么数据(所需的列),检索出来的数据存储在什么地方。它还向前移动游标内部行指针,使下一条FETCH语句检索下一行(不重复读取同一行)。
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 FOR SQLSTATE '02000' SET done=1;
-- 创建一个用于保存结果的新表
CREATE TABLE IF NOT EXISTS ordertotals
(order_num INT, total DECIMAL(8,2));
-- 打开游标
OPEN ordernumbers;
-- 开始循环
REPEAT
-- Get order number
FETCH ordernumbers INTO o;
-- Get the total for this order
CALL ordertotal(o,1,t);
-- Insert order and total into ordertotals
INSERT INTO ordertotals(order_num,total)
VALUES(o,t);
-- 结束循环
UNTIL done END REPEAT;
-- 关闭游标
CLOSE ordernumbers;
END;
说明:
(1)这个例子中的FETCH是在REPEAT内,因此它反复执行到done为真:由 UNTIL done END REPEAT规定。
(2)done何时才被设置为真呢?
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
这条语句定义了一个CONTINUE HANDLER,它是在条件出现时被执行的代码。这里,它指出当SQLSTATE '02000'出现时,SET done=1。SQLSTATE'02000'是一个未找到条件,当REPEAT由于没有更多的行可供循环的时候,出现这个条件,结束循环。
本书中的游标讲解内容不多,推荐三篇博客进行阅读:游标1、游标2、游标3
本章将讲解什么是触发器,为什么要使用触发器
1、触发器:如果想要某条语句在事件发生时(DELETE、INSERT、UPDATE)自动执行,可以使用触发器。
2、创建触发器
创建触发器时,需要给出4条信息
(1)唯一的触发器名;
(2)触发器关联的表;
(3)触发器应该响应的活动(DELETE、INSERT、UPDATE)
(4)触发器何时执行(处理之前或之后)
CREATE TRIGGER newproduct AFTER INSERT ON products
FOR EACH ROW SELECT 'Product added';
使用INSERT语句添加一行或多行到products中,成功后都会显示Products added消息。
说明:触发器按每个表每个事件每次地定义,每个表每个事件每次只允许一个触发器。因此,每个表最多支持6个触发器(每条INSERT、UPDATE、DELETE之前和之后)。
3、删除触发器
DROP TRIGGER newproduct;
触发器不能更新或覆盖。为了修改一个触发器,必须先删除它,然后再重新创建。
4、INSERT触发器
-- 对于orders的每次插入使用这个触发器将总是返回新的订单号
CREATE TRIGGER neworder AFTER INSERT ON orders
FOR EACH ROW SELECT new.order_new;
说明:
(1)在INSERT触发器代码内,可以引用一个名为NEW的虚拟表,访问被插入的行。
5、DELETE触发器
-- 使用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);
END;
说明:
(1)在DELETE触发器代码内,可以引用一个名为OLD的虚拟表,去访问被删除的行;
(2)OLD中的值全都是只读的,不能更新。
(3)该语句中的BEGIN AND模块是可选的,好处是触发器能够容纳多条SQL语句。
7、UPDATE触发器
-- 保证州名缩写都是大写
CREATE TRIGGER updatevendor BEFORE UPDATE ON vendors
FOR EACH ROW SET new.vend_state = UPPER(vend_state);
说明:在UPDATE代码中,可以引用一个名为OLD的虚拟表访问以前的值,还可以引用一个名为NEW的虚拟表访问新更新的值。
本章介绍什么是事务处理以及如何利用COMMIT和ROLLBACK语句来管理事务处理。
1、并非所有的引擎都支持事务处理,MyISAM和InnoDB是两种最常用的引擎,前者不支持明确的事务管理,而后者支持。
2、事务处理(Transaction Processing)可用来维护数据库的完整性,它保证成批的MySQL操作要么完全执行,要么完全不执行。
3、事务处理相关的几个术语:
(1)事务(transaction)指一组SQL语句;
(2)回退(rollback)指撤销指定SQL语句的过程;
(3)提交(commit)指将未存储的SQL语句结果写入数据库表;
(4)保留点(savepoint)指事务处理中设置的临时占位符(placeholder),它可以对它发布回退。
4、控制事务处理
管理事务处理的关键在于将SQL语句分解为逻辑块,并明确规定数据何时应该回退,何时不应该回退。
(1)使用ROLLBACK
MySQL的ROLLABCK命令来回退(撤销)MySQL语句,看下面的案例:
SELECT * FROM ordertotals;
-- 标识事务的开始
START TRANSACTION;
DELETE FROM ordertotals;
SELECT * FROM ordertotals;
ROLLBACK;
SELECT * FROM ordertotals;
说明:
<1>用START TRANSACTION标识事务的开始;
<2>ROLLBACK只能在一个事务处理内使用(在执行一条START TRANSACTION命令之后)
(2)使用COMMIT
一般的MySQL语句都是直接针对数据库表执行和编写的。这就是所谓的隐含提交,即提交(写或保存)操作是自动进行的。但是在事务处理模块中,提交不会隐含的进行。为了进行明确的提交,使用COMMIT语句,如下所示:
START TRANSACTION;
DELETE FROM orderitems WHERE order_num = 20010;
DELETE FROM orders WHERE order_num = 20010;
COMMIT;
说明:
<1>最后的COMMIT语句仅在不出错时写出更改。如果第一条DELETE起作用,但是第二条失败了,则DELETE不会提交(实际上,它是被自动撤销的)。
<2>隐含事务关闭:当COMMIT或者ROLLBACK语句执行后,事务会自动关闭。
<3>修改默认的提交行为:
SET autocommit = 0;
(3)使用保留点
简单的ROLLBACK和COMMIT可以写入或者撤销整个事务处理。但是很多时候遇到错误撤回时没必要撤销到事务开始的地方,可以通过设置保留点撤销到事务错误没影响到的位置。如下SQL代码所示:
-- 设置保留点
SAVEPOINT delete1;
-- 回滚到保留点
ROLLBACK TO delete1;
说明:
(1)可以在MySQL代码中设置任意多的保留点,越多越好。因为保留点越多,你就越能按照自己的意愿灵活的进行回退;
(2)释放保留点:保留点在事务完成之后(执行一条ROLLBACK或者COMMIT)后自动释放,MySQL5.0以后也可以使用RELEASE SAVEPOINT明确地释放保留点。
本章主要介绍MySQL处理不同的字符集和语言的基础知识。
1、数据库被用来存储和检索数据。不同的语言和字符集需要以不同的方式存储和检索。因此,MySQL需要适应不同的字符集(不同的字母和字符),适应不同的排序和检索数据的方法。
常见的术语:
(1)字符集:字母和符号的集合;
(2)编码:某个字符集成员的内部表示;
(3)校对:规定字符如何进行比较的指令。校对的规则直接影响排序的结果.......
2、使用字符集和校对顺序
-- 显示所有可用的字符集以及每个字符集的描述和默认校对
SHOW CHARACTER SET;
-- 显示所有可用的校对,以及它适用的字符集
SHOW COLLATION;
-- 查看当前所用的字符集和校对顺序
SHOW VARIABLES LIKE 'character%';
SHOW VARIABLES LIKE 'collation%';
(1)给表指定字符集和校对方式
CREATE TABLE mytable
(
column1 INT,
column2 VARCHAR(10)
)DEFAULT CHARACTER SET hebrew
COLLATE hebrew_general_ci;
(2)对列指定字符集和校对方式
CREATE TABLE mytable
(
column1 INT,
column2 VARCHAR(10)
column3 VARCHAR(10) CHARACTER SET latinl COLLATE
)DEFAULT CHARACTER SET hebrew
COLLATE hebrew_general_ci;
(3)在SELECT语句中指定字符集和校对方式
-- 区分大小写的校对
SELECT * FROM customers
ORDER BY lastname, firstname COLLATE latinl_general_cs;
本章将学习MySQL的访问控制和用户管理。数据库服务器通常包含关键的数据,确保这些数据的安全和完整需要利用访问控制权限。
1、MySQL服务器的安全基础是:用户应该对他们需要的数据具有适当的访问权,既不能多也不能少。
2、管理用户
-- 获取所有用户列表
USE mysql;
SELECT USER FROM USER;
3、创建用户账号
-- 使用IDENTIFIED BY 'password'给出了一个口令
CREATE USER ben IDENTIFIED BY 'password';
-- 重命名用户
RENAME USER ben TO ben1;
4、删除用户账号
-- 用户和相关权限都会被删除
DROP USER ben1;
5、设置访问权限
-- 查看用户已有的权限
SHOW GRANTS FOR ben1;
-- 允许ben1在crashcourse.*(crashcourse下的所有表)使用SELECT,只读权限
GRANT SELECT ON crashcourse.* TO ben1;
-- GRANT的反操作是REVOKE,撤销特定的权限
REVOKE SELECT ON crashcourse.* TO ben1;
GRANT和REVOKE可在以下几个层次上控制访问权限:
(1)整个服务器,使用GRANT ALL和REVOKE ALL;
(2)整个数据库,使用ON database.*;
(3)特定的表,使用ON database.table;
(4)特定的列;
(5)特定的存储过程。
6、更改口令
-- 新口令必须传到password()函数中进行加密
SET PASSWORD FOR ben1 = PASSWORD('newpwd');
1、备份数据
(1)使用命令行实用程序mysqldump转储所有数据库的内容到外部文件。
(2)可用命令行实用程序mysqlhotcopy从一个数据库复制所有数据(并非所有数据库引擎都支持这个实用程序)
(3)可用实用MySQL的BACKUP TABLE或SELECT INTO OUTFILE转储所有数据到某个外部文件。
注意:为了保证所有的数据都被写入到磁盘,备份前实用FLUSH TABLES语句。
2、进行数据库维护
-- 检查表键是否正确
ANALYZE TABLE orders;
-- Check Table发现和修复问题
CHECK TABLE orders, orderitems;
-- 说明:如果从一个表中删除大量数据,应该使用OPTIMIZE TABLE来收回所用的空间,从而优化表的性能。
3、诊断启动问题
(1)--help显示帮助;
(2)--safe-mode装载减去某些最佳配置的服务器;
(3)--verbose显示全文本消息;
(4)--version显示版本信息。
4、查看日志文件
(1)错误日志:hostname.err,可用--log-error命令行选项更改;
(2)查询日志:hostname.log,可用--log命令行选项更改;
(3)二进制日志:hostname-bin,可用--log-bin命令行选项更改;
(4)缓慢查询日志:hostname-slow.log,可用--log-solw-queries命令行选项更改。这个日志在确定数据库何处需要优化很有用。
1、可以使用SHOW PROCESSLIST显示所有活动进程,KILL命令终结某个特定的进程。
2、总是不止一种方法编写同一条SELECT语句。应该实验联结、并、子查询等,找出最佳的方法。
3、一般来说,存储过程执行的比一条一条地执行其中的各条SQL语句要快。
4、应该总是使用正确的数据类型。
5、绝不要检索比需求还要多的数据。换言之,不要使用SELECT*
6、在导入数据时,应该关闭自动提交。
7、必须索引数据库表以改善数据检索的性能。
8、使用UNION语句替换多个OR条件可以提高性能。
9、LIKE很慢,最好使用FULLTEXT。
................................
数据库优化是个重点的主题,这里只是列举了一些常用操作能够优化的方法,还需要找相关书籍或者博客做深层次的学习。
1、数值数据类型
数据类型 | 说明 |
---|---|
BIT | 位字段,1~64位 |
BIGINT | 整数值,支持:-9223372036854775808~9223372036854775807,如果是UNSIGNED,为0~18446744073709551615 |
BOOLEAN(或:BOOL) | 布尔标志 |
DECIMAL(或:DEC) | 精度可变的浮点值 |
DOUBLE | 双精度浮点值 |
FLOAT | 单精度浮点值 |
INT(或:INTEGER) | 整数值,支持:-2147483648~-2147483647,如果是UNSIGNED,为0~4294967295 |
MEDIUMINT | 整数值,支持:-8388608~8388607,如果是UNSIGNED,为0~16777215 |
REAL | 4字节的浮点值 |
SMALLINT | 整数值,支持-32768~32767,如果是UNSIGNED,为0~65535 |
TINYINT | 整数值,支持-128~127,如果是UNSIGNED,为0~255 |
2、串数据类型
数据类型 | 说明 |
---|---|
CHAR | 1~255个字符串 |
ENUM | 接受最多64K个串组成的一个预定义集合的某个串 |
LONGTEXT | 与TEXT相同,但最大长度为4GB |
MEDIUMTEXT | 与TEXT相同,但最大长度为16K |
SET | 接受最多64K个串组成的一个预定义集合的零个或多个串 |
TEXT | 最大长度为64K的边长文本 |
TINYTEXT | 与TEXT相同,但最大长度为255字节 |
VARCHAR | 长度可变,但是最大长度不超过255 |
3、二进制数据类型
二进制数据类型可以存储任何数据,如图像、多媒体以及字处理文档等。
数据类型 | 说明 |
---|---|
BLOB | Blob最大长度为64KB |
MEDIUBLOB | Blob最大长度为16MB |
LONGBLOB | Blob最大长度为4GB |
TINYBLOB | Blob最大长度为255字节 |
4、日期和时间数据类型
数据类型 | 说明 |
---|---|
DATE | 格式为:YYYY-MM-DD |
DATETIME | DATE和TIME的组合 |
TIMESTAMP | 功能和DATETIME相同,但范围较小 |
TIME | 格式为:HH:MM:SS |
YEAR | 年份,可以表示为2位或者4位 |
总体文章写的简单易懂,很适合新手入门,或者简单开发的工具书,但是不适合已有一定数据库知识的人使用,如果想深层次的了解,推荐阅读《高性能MySQL》
个人觉得不足之处:
1、没有讲诉索引(index)相关的内容;
2、在“联结”相关的章节讲诉不是很通俗易懂;
3、数据库备份、维护以及优化章节讲诉的不深。