MySQL的历史就是整个互联网的发展史。互联网业务从社交领域、电商领域到金融领域的发展,推动着应用对数据库的需求提升,对传统的数据库服务能力提出了挑战。高并发、高性能、高可用、轻资源、易维护、易扩展的需求,促进了MySQL的长足发展。
MySQL从5.7版本直接跳跃发布了8.0版本 ,可见这是一个令人兴奋的里程碑版本。MySQL 8版本在功能上做了显著的改进与增强,开发者对MySQL的源代码进行了重构,最突出的一点是多MySQL Optimizer优化器进行了改进。不仅在速度上得到了改善,还为用户带来了更好的性能和更棒的体验。
为什么如此多的厂商要选用MySQL?大概总结的原因主要有以下几点:
“订单”表和“产品”表有一种多对多的关系,这种关系是通过与“订单明细”表建立两个一对多关系来
定义的。一个订单可以有多个产品,每个产品可以出现在多个订单中。
在卸载之前,先停止MySQL8.0的服务。按键盘上的“Ctrl + Alt + Delete”组合键,打开“任务管理器”对话框,可以在“服务”列表找到“MySQL8.0”的服务,如果现在“正在运行”状态,可以右键单击服务,选择“停 止”选项停止MySQL8.0的服务,如图所示。
卸载MySQL8.0的程序可以和其他桌面应用程序一样直接在“控制面板”选择“卸载程序”,并在程序列表中找到MySQL8.0服务器程序,直接双击卸载即可,如图所示。这种方式删除,数据目录下的数据不会跟着删除。
删除残余文件的清理
如果再次安装不成功,可以卸载后对残余文件进行清理后再安装。
服务目录:mysql服务的安装目录
数据目录:默认在C:\ProgramData\MySQL(如果自己单独指定了数据目录,找到指定的位置进行删除)
清理注册表(选)
如果前面几步做了,再次安装还是失败,可以清理注册表
打开方式:在系统的搜索框中输入regedit
注册表中的ControlSet001,ControlSet002,不一定是001和002,可能是ControlSet005、006之类
删除环境变量配置
找到python环境变量,将其中关于mysql的环境变量删除,切记不要全部删除
官方还提供了 MySQL Workbench (GUITOOL)一款专为MySQL设计的 图形界面管理工具 。 MySQLWorkbench又分为两个版本,分别是 社区版 (MySQL Workbench OSS)、 商用版 (MySQL WorkbenchSE)。
MySQL安装之后,需要对服务器进行配置。具体的配置步骤如下。
Server Machine(服务器) :该选项代表服务器,MySQL服务器可以同其他服务器应用程序一起运行,例如Web服务器等。MySQL服务器配置成适当比例的系统资源。
Dedicated Machine(专用服务器) :该选项代表只运行MySQL服务的服务器。MySQL服务器配置成使用所有可用系统资源。
步骤3:单击“Next”(下一步)按钮,打开设置授权方式窗口。其中,上面的选项是MySQL8.0提供的新的授权方式,采用SHA256基础的密码加密方法;下面的选项是传统授权方法(保留5.x版本兼容性)。
- 步骤4:单击“Next”(下一步)按钮,打开设置服务器root超级管理员的密码窗口,如图所示,需要输入两次同样的登录密码。也可以通过“Add User”添加其他用户,添加其他用户时,需要指定用户名、允许该用户名在哪台/哪些主机上登录,还可以指定用户角色等。此处暂不添加用户,用户管理在MySQL高级特性篇中讲解。
步骤5:单击“Next”(下一步)按钮,打开设置服务器名称窗口,如图所示。该服务名会出现在Windows服务列表中,也可以在命令行窗口中使用该服务名进行启动和停止服务。本书将服务名设置为“MySQL80”。如果希望开机自启动服务,也可以勾选“Start the MySQL Server at System Startup”选项(推荐)。
下面是选择以什么方式运行服务?可以选择“Standard System Account”(标准系统用户)或者“Custom User” (自定义用户)中的一个。这里推荐前者。
步骤7:完成配置,如图所示。单击“Finish”(完成)按钮,即可完成服务器的配置。
步骤8:如果还有其他产品需要配置,可以选择其他产品,然后继续配置。如果没有,直接选择“Next”(下一步),直接完成整个安装和配置过程。
步骤9:结束安装和配置。
如果不配置MySQL环境变量,就不能在命令行直接输入MySQL登录命令。下面说如何配置MySQL的环境变量:
# 启动 MySQL 服务命令:
net start MySQL服务名
# 停止 MySQL 服务命令:
net stop MySQL服务名
说明:
开始菜单 → 所有程序 → MySQL → MySQL 8.0 Command Line Client
仅限root用户
mysql -h 主机名 -P 端口号 -u 用户名 -p密码
mysql -h localhost -P 3306 -u root -proot # 这里设置的root密码是root
mysql -h localhost -P 3306 -u root -p
Enter password:****
3.客户和服务器在同一台机器上,所以输入localhost 或者IP地址127.0.0.1。同时因为是连接本机:-h
localhost 可以省略,如果端口没有修改:-P3306也可以省略。
show database;
“information_schema”是 MySQL 系统自带的数据库,主要保存 MySQL 数据库服务器的系统信息,比如数据库的名称、数据表的名称、字段名称、存取权限、数据文件 所在的文件夹和系统使用的文件夹,等等
“performance_schema”是 MySQL 系统自带的数据库,可以用来监控 MySQL 的各类性能指标。
“sys”数据库是 MySQL 系统自带的数据库,主要作用是以一种更容易被理解的方式展示 MySQL 数据库服务器的各类性能指标,帮助系统管理员和开发人员监控 MySQL 的技术性能。
“mysql”数据库保存了 MySQL 数据库服务器运行时需要的系统信息,比如数据文件夹、当前使用的字符集、约束检查信息,等等
创建自己数据库
create database 数据库名;
# 创建的数据库名不能已经存在
use 数据库名;
说明:如果没有使用use语句,后面针对数据库的操作也没有加“数据名”的限定,会报ERROR1046(3D000): No database selected”(没有选择数据库)
使用完use语句之后,如果接下来的SQL都是针对一个数据库操作的,那就不用重复use了,如果要针对另一个数据库操作,那么要重新use。
查看某个库的所有表格
show tables from 数据库名;
create table 表名称(
字段名 数据类型,
字段名 数据类型
)
说明:如果是最后一个字段,后面就不用加逗号,因为逗号的作用是分割每个字段
create table student (
id int,
name varchar(20) # 说明字最长不超过20个字符
)
select * from 数据库表名称;
# 查看学生表的数据
select * from student;
insert into 表名称 values(值列表);
# 添加记录进student
inster into student values(1,'张三');
符号需要英文符号,中文的会报错
查看表的创建信息
show create table 表名称;
# 查看student表的详细创建信息
show create table student;
#MySQL5.7
Table: student
Create Table: CREATE TABLE `student` (
`id` int(11) DEFAULT NULL,
`name` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
在MySQL5.7中默认字符集是“latin1”不支持中文可以修改mv.ini文件,MySQL8.0默认是utf-8
show create database 数据库名;
drop table 表名称;
drop database 数据库名;
-因为查询语句使用的非常频繁,所有很多人把查询语句单拎出来一类:DQL(数据查询语句),还有单独将COMMIT,ROLLBACK取出来称为TCL(Transaction Control Language,事务控制语言)。
单行注释 :# 注释文字(MySQL特有的方式)
单行注释:-- 注释文字(-- 后面必须包含一个空格)
多行注释:/* 注释文字 */
数据库、表名不得超过30个字符,变量名限制为29个
必须只能包含 A–Z, a–z, 0–9, _共63个字符
数据库名、表名、字段名等对象名中间不要包含空格
同一个MySQL软件中,数据库不能同名;同一个库中,表不能重名;同一个表中,字段不能重名
必须保证你的字段没有和保留字、数据库系统或常用方法冲突。如果坚持使用,请在SQL语句中使用`(着重号)引起来
保持字段名和类型的一致性,在命名字段并为其指定数据类型的时候一定要保证一致性。假如数据类型在一个表里是整数,那在另一个表里可就别变成字符型了
举例
# 以下俩句是一样的,不区分大小写
show database;
show DATABASE;
# 创建表格
# create table student info(……);# 表名错误,因为表名有空格
create table student_info(……)
# 其中order 使用 ``飘号,因为order和系统关键字或系统函数名等定义标识符重名了
CREATE TABLE `order`(……)
select id as “编号”,`name` as "姓名” from t_stu;# 起别名时,as 可以省略
select id as 编号,`name` as 姓名 from t_stu; # 如果字段名中没有空格,可以省略“”
select id as 编号,`name` as 姓 名 from t_stu; # 错误,如果字段名中有空格,不可以省略“”
方式一:在命令行客户端登录MySQL,使用sorce指令导入
mysql> source d:\mysqldb.sql
方式二:基于具体的图形化界面的工具可以导入数据
如:SQLYog中,选择“工具”—“执行SQL脚本”—“选择xxx.sql文件,执行”
SELECT 1;没有任何子句
SELECT 9/2;没有任何子句
SELECT 标识选择哪些列
FROM 标识从哪个表中选择
SELECT *
FROM departments;
一般情况下,除非需要使用表中所有的字段数据,最好不要使用通配符 “*”。通配符虽然可以节省输入查询语句的时间,但是获取不需要的列数据通常会降低查询和所使用的应用程序的效率。通配符的优势当不知所需要的列名称的时候,可以通过它获取。
生产环境下,不推荐直接使用select * 进行查询。
选择特定的列
SELECT department_id, loaction_id
FROM departements;
SELECT last_name AS name,commission_pct comm
FROM employees;
SELECT department_id
FROM employees;
SELECT DISTINCT department_id
FROM employees;
SELECT DISTINCT department_id,salary
FROM employees;
SELECT employee_id,salary,commission_pct,12 * salary * (1 + commission_pct) "annual_sal"
FROM employees;
mysql> SELECT * FROM ORDER;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right
syntax to use near 'ORDER' at line 1
mysql> SELECT * FROM `ORDER`;
+----------+------------+
| order_id | order_name
| +----------+------------+
| 1 | shkstart |
| 2 | tomcat |
| 3 | dubbo |
+----------+------------+
3 rows in set (0.00 sec)
mysql> SELECT * FROM `order`;
+----------+------------+
| order_id | order_name |
+----------+------------+
| 1 | shkstart |
| 2 | tomcat |
| 3 | dubbo |
+----------+------------+
3 rows in set (0.00 sec)
DESCRIBE employees;
或
DESC employees;
mysql> desc employees;
+----------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-------------+------+-----+---------+-------+
| employee_id | int(6) | NO | PRI | 0 | |
| first_name | varchar(20) | YES | | NULL | |
| last_name | varchar(25) | NO | | NULL | |
| email | varchar(25) | NO | UNI | NULL | |
| phone_number | varchar(20) | YES | | NULL | |
| hire_date | date | NO | | NULL | |
| job_id | varchar(10) | NO | MUL | NULL | |
| salary | double(8,2) | YES | | NULL | |
| commission_pct | double(2,2) | YES | | NULL | |
| manager_id | int(6) | YES | MUL | NULL | |
| department_id | int(4) | YES | MUL | NULL | |
+----------------+-------------+------+-----+---------+-------+
11 rows in set (0.00 sec)
SELECT 字段1,字段2
FROM 表名
WHERE 过滤条件
使用WHERE 子句,将不能满足条件的行过滤掉
WHERE子句紧随FROM子句
例子:查询部门id=90 的
SELECT employee_id,last_name,job_id,department_id
FROM employees
WHERE department_id=90;
算术运算符主要用于数学运算,其可以连接运算符前后的两个数值或表达式,对数值或表达式进行加(+)、减(-)、乘(*)、除(/)和取模(%)运算。
- 加法与减法运算符
由运算结果可以得出如下结论
一个整数类型的值对整数进行加法和减法操作,结果还是一个整数;
一个整数类型的值对浮点数进行加法和减法,结果是一个浮点数
加法和减法的优先级相同,进行先加后减操作与进行先减后加操作结果一样
在java中,+ 的左右两边如果都是字符串,那么表示字符串的拼接,但是在MySQL中+只表示数值相加,如果遇到非数值类型,先尝试转成数值,如果转失败,按照0计算(补充:MySQL中字符串拼接要使用字符串函数CONCAT()实现)
一个数乘以整数1和除以整数1仍然是原数
一个数乘以浮点数1和除以浮点数1后变成浮点数,数值与原数相等;
一个数除以整数后,不管是否能除尽,结果都为一个浮点数;
一个数除以另一个数,除不尽时,结果为一个浮点数,并保留到小数点后4位;
乘法和除法的优先级相同,进行先乘后除操作与先除后乘操作,得出的结果相同。
在数学运算中,0不能用作除数,在MySQL中,一个数除以0为NULL。
比较运算符用来对表达式左边的操作数和右边的操作数进行比较,比较的结果为真则返回1,比较的结果为假则返回0,其他情况则返回NULL。
比较运算符经常被用来作为SELECT查询语句的条件来使用,返回符合条件的结果记录。
安全等于运算符(<=>)与等于运算符(=)的作用是相似的, 唯一的区别 是‘<=>’可
以用来对NULL进行判断。在两个操作数均为NULL时,其返回值为1,而不为NULL;当一个操作数为NULL时,其返回值为0,而不为NULL。
可以看到,使用安全等于运算符时,两边的操作数的值都为NULL时,返回的结果为1而不是NULL,其他返回结果与等于运算符相同。
不等于运算符
不等于运算符(<>和!=)用于判断两边的数字、字符串或者表达式的值是否不相等,
如果不相等则返回1,相等则返回0。不等于运算符不能判断NULL值。如果两边的值有任意一个为NULL,或两边都为NULL,则结果为NULL。 SQL语句示例如下:
空运算符
空运算符(IS NULL或者ISNULL)判断一个值是否为NULL,如果为NULL则返回1,否则返回
0。 SQL语句示例如下:
非空运算符
非空运算符(IS NOT NULL)判断一个值是否不为NULL,如果不为NULL则返回1,否则返
回0。 SQL语句示例如下:
- 最小值运算符
LEAST(值1,值2,……,值n)。其中值n表示参数列表中有n个值,在有多个参数的情况下,返回最小值、
由结果可以看到,当参数是整数或者浮点数时,LEAST将返回其中最小的值;当参数为字符串时,返回字母表中顺序最靠前的字符;当比较值列表中有NULL时,不能判断大小,返回值为NULL。
最大值运算符
语法格式为:GREATEST(值1,值2,…,值n)。其中,n表示参数列表中有n个值。当有两个或多个参数时,返回值为最大值。假如任意一个自变量为NULL,则GREATEST()的返回值为NULL。
由结果可以看到,当参数中是整数或者浮点数时,GREATEST将返回其中最大的值;当参数为字符串时,返回字母表中顺序最靠后的字符;当比较值列表中有NULL时,不能判断大小,返回值为NULL。
BETWEEN AND运算符
BETWEEN运算符使用的格式通常为SELECT D FROM TABLE WHERE C BETWEEN A AND B,此时,当C大于或等于A,并且C小于或等于B时,结果为1,否则结果为0。
"%":匹配0个或多个字符
"_":只能匹配一个字符
回避特殊符号的:使用转义符。例如:将[%]转为[ %]、[]转为[ ],然后再加上[ESCAPE‘$’]即可。
REGEXP运算符
REGEXP运算符用来匹配字符串,语法格式为: expr REGEXP 匹配条件 。如果expr满足匹配条件,返回1;如果不满足,则返回0。若expr或匹配条件任意一个为NULL,则结果为NULL。
REGEXP运算符在进行匹配时,常用的有下面几种通配符
(1)‘^’匹配以该字符后面的字符开头的字符串。
(2)‘$’匹配以该字符前面的字符结尾的字符串。
(3)‘.’匹配任何一个单字符。
(4)“[…]”匹配在方括号内的任何字符。例如,“[abc]”匹配“a”或“b”或“c”。为了命名字符的范围,使用一 个‘-’。“[a-z]”匹配任何字母,而“[0-9]”匹配任何数字。
(5)‘*’匹配零个或多个在它前面的字符。例如,“x*”匹配任何数量的‘x’字符,“[0-9]*”匹配任何数量的数字, 而“*”匹配任何数量的任何字符。
-逻辑异或运算符
位运算符是在二进制数上进行计算的运算符。位运算符会先将操作数变成二进制,然后进行为运算,最后将计算结果从二进制转换成十进制。
按位与运算符
按位与(&)运算符将给定值对应的二进制数逐位进行逻辑与运算,当给定值对应的二进制位的数值都为1是,则该位返回1,否则返回0。
1的二进制数为0001,10的二进制数为1010,所以1 & 10的结果为0000,对应的十进制数为0。20的二进制数为10100,30的二进制数为11110,所以20 & 30的结果为10100,对应的十进制数为20。
按位或运算符
按位或(|)运算符将给定的值对应的二进制数逐位进行逻辑或运算。当给定值对应的二进制位的数值有一个或两个为1时,则该为返回1,否则返回0。
1的二进制数为0001,10的二进制数为1010,所以1 | 10的结果为1011,对应的十进制数为11。20的二进制数为10100,30的二进制数为11110,所以20 | 30的结果为11110,对应的十进制数为30。
按位异或运算符
按位异或(^)运算符将给定的值对应的二进制数逐位进行逻辑异或运算。当给定值
对应的二进制位的数值不同时,则该位返回1,否则返回0。
1的二进制数为0001,10的二进制数为1010,所以1 ^ 10的结果为1011,对应的十进制数为11。20的二进制数为10100,30的二进制数为11110,所以20 ^ 30的结果为01010,对应的十进制数为10。
按位取反运算符
按位取反(~)运算符将给定的值的二进制数逐位进行取反操作,即将1变为0,将0变 为1。
由于按位取反(~)运算符的优先级高于按位与(&)运算符的优先级,所以10 & ~1,首先,对数字1进行按位取反操作,结果除了最低位为0,其他位都为1,然后与10进行按位与操作,结果为10。
按位右移运算符
按位右移(>>)运算符将给定的值的二进制数的所有位右移指定的位数。右移指定的位数后,右边低位的数值被移出并丢弃,左边高位空出的位置用0补齐。
1的二进制数为0000 0001,右移2位为0000 0000,对应的十进制数为0。4的二进制数为0000 0100,右移2位为0000 0001,对应的十进制数为1。
按位左移运算符
按位左移(<<)运算符将给定的值的二进制数的所有位左移指定的位数。左移指定的位数后,左边高位的数值被移出并丢弃,右边低位空出的位置用0补齐。
1的二进制数为0000 0001,左移两位为0000 0100,对应的十进制数为4。4的二进制数为0000 0100,左移两位为0001 0000,对应的十进制数为16。
SELECT * FROM fruits WHERE f_name REGEXP '^b';
SELECT * FROMfruits WHERE f_name REGEXP 'y$';
SELECT * FROM fruits WHERE f_name REGEXP 'a.g';
SELECT * FROM fruits WHERE f_name REGEXP '^ba*';
SELECT * FROM fruits WHERE f_name REGEXP '^ba+';
SELECT * FROM fruits WHERE f_name REGEXP 'on';
SELECT * FROM fruits WHERE f_name REGEXP 'on|ap';
LIKE运算符也可以匹配指定的字符串,但与REGEXP不同,LIKE匹配的字符串如果在文本中间出现,则找不到它,相应的行也不会返回。REGEXP在文本内进行匹配,如果被匹配的字符串在文本中出现,REGEXP将会找到它,相应的行也会被返回。
匹配指定字符中的任意一个
方括号“[]”指定一个字符集合,只匹配其中任何一个字符,即为所查找的文本。在fruits表中,查找f_name字段中包含字母‘o’或者‘t’的记录,SQL语句如下:
SELECT * FROM fruits WHERE f_name REGEXP '[ot]';
SELECT * FROM fruits WHERE s_id REGEXP '[456]';
SELECT * FROM fruits WHERE f_id REGEXP '[^a-e1-2]';
SELECT * FROM fruits WHERE f_name REGEXP 'x{2,}';
SELECT * FROM fruits WHERE f_name REGEXP 'ba{1,3}';
SELECT last_name, job_id, department_id, hire_date
FROM employees
ORDER BY hire_date;
SELECT last_name, department_id, salary
FROM employees
ORDER BY department_id, salary DESC;
LIMIT [位置偏移量,]行数
第一个“位置偏移量”参数指示MYSQL从哪一行开始显示,是一个可选参数,如果不指定“位置偏移量”,将会从表中的第一条记录开始(第一条记录的位置偏移量是0,第二条的位置偏移量是1,以此类推);第二个参数“行数”指示返回的记录条数。
-- 前10条记录:
SELECT * FROM 表名 LIMIT 0,10;
-- 或
SELECT * FROM 表名 LIMIT 10;
-- 第11 条至第20条
SELECT * FROM 表名 LIMIT 10,10;
MySQL 中可以使用“LIMIT 3 OFFSET 4”,意思是获取从第五条记录开始后面的3条记录,和“LIMIT 4,3;”返回结果一样。
分页显示公式:(当前页数-1) 每页条数,每页条数*
SELECT * FROM table
LIMIT (PageNo-1)*PageSize,PageSize;
注意:LIMIT子句必须放在整个SELECT 语句的最后!
使用LIMIT的好处:
拓展
在不同的 DBMS 中使用的关键字可能不同。在 MySQL、PostgreSQL、MariaDB 和 SQLite 中使用 LIMIT 关键字,而且需要放到 SELECT 语句的最后面。
在不同的DBMS中使用的关键字可能不同,在MYSQL、PostgreSQL、MariaDB 和 SQLite 中使用 LIMIT 关键字,而且需要放到 SELECT 语句的最后面。
如果是SQL Server 和Access,需要使用TOP 关键字,比如:
SELECT TOP 5 name, hp_max FROM heros ORDER BY hp_max DESC;
SELECT name, hp_max FROM heros ORDER BY hp_max DESC FETCH FIRST 5 ROWS ONLY;
SELECT rownum, last_name,salary FROM employees WHERE rownum<5 ORDER BY salary DESC;
SELECT rownum,last_name,salary
FROM (
SELECT last_name,salary
FROM employees
ORDER BY salary DESC)
WHERE rownum<10;
-- 案例:查询员工的姓名及其部门名称
SELECT last_name, department_name
FROM employees, departments;
笛卡尔乘积是一个数学运算。假设有俩个集合X和Y,那么X和Y的笛卡尔积就是X和Y的所有可能组合,也就是一个对象来自X,一个对象来自Y的所有可能。组合的个数即为两个集合中元素的个数乘积。
# 查询员工姓名和所在部门名称
SELECT last_name, department_name FROM employees, departments;
SELECT last_name, department_name FROM employees CROSS JOIN departments;
SELECT last_name, department_name FROM employees INNER JOIN departments;
SELECT last_name, department_name FROM employees JOIN departments;
SELECT table1.column, table2.column
FROM table1, table2
WHERE table1.column = table2.column; # 连接条件
-- 查询员工的姓名及其部门名称
SELECT last_name, department_name
FROM employees, departments
WHERE employees.department_id = departments.department_id;
SELECT employees.employee_id, employees.last_name,
employees.department_id, departments.department_id,departments.location_id
FROM employees, departments
WHERE employees.department_id = departments.department_id;
表名
加以区分。SELECT employees.last_name, departments.department_name,employees.department_id
FROM employees,departments
WHERE employees.department_id = departments.department_id;
SELECT e.employee_id, e.last_name, e.department_id,
d.department_id, d.location_id
FROM employees e, departments d
WHERE e.department_id = d.department_id;
阿里开发规范:
【强制】对于数据库中表记录的查询和变更,只要涉及多个表,都需要在列名钱加上表的别名
(或表名)进行限定。
说明:对多表进行查询记录、更新记录、删除记录时,如果对操作列没有限定表的别名(或表名),
并且操作列在多个表中存在时,会抛出异常。
SELECT e.last_name, e.salary, j.grade_level
FROM employees, job_grades j
WHERE e.salary BETWEEN j.lowest_sal AND j.highest_sal;
- 当table1 和table2本质上是同一张表,只是用取别名的方式虚拟成两张表以代表不同的意义,然后两张表再进行内连接,外连接等查询。
SELECT CONCAT(worker.last__name, 'works for' ,manager.last_name)
FROM employees worker, employees manager
WHERE worker.manager_id = manager.employee_id;
** 分类3:内连接VS外连接**
内连接:合并具有同一列的两个以上的表的行,结果集中不包含一个表与另外一个表不匹配的行
外连接: 两个表在连接过程中除了返回满足连接条件的行以外还返回左(或右)表中不满足条件的行 ,这种连接称为左(或右) 外连接。没有匹配的行时, 结果表中相应的列为空(NULL)。
如果是左外连接,则连接条件中左边的表也称为 主表 ,右边的表称为 从表 。
如果是右外连接,则连接条件中右边的表也称为 主表 ,左边的表称为 从表 。
SQL92:使用(+)创建连接
在 SQL92 中采用(+)代表从表所在的位置。即左或右外连接中,(+) 表示哪个是从表。
Oracle 对 SQL92 支持较好,而 MySQL 则不支持 SQL92 的外连接。
-- 左外连接
SELECT last_name,department_name
FROM employees, departments
WHERE employees.department_id = departments.department_id(+);
-- 右外连接
SELECT last_name,department_name
FROM employees, departments
WHERE employees.department_id(+) = departments.department_id;
SELECT table1.column, table2.column, table3.column
FROM table1
JOIN table2 ON table1和table2的连接条件
JOIN table3 ON table2 和table3的连接条件
SELECT 字段列表
FROM 表A INNER JOIN 表B
ON 关联条件
WHERE 等其它子句;
SELECT e.employee_id,e.last_name,e.department_id,d.department_id, d.location_id
FROM empolyees e JOIN departments d
ON (e.department_id = d.department_id);
SELECT employee_id, city, department_name
FROM employees e
JOIN departments d
ON d.department_id = e.department_id
JOIN locations l
ON d.location_id = l.location_id;
左外连接(LEFT OUTER JOIN)
-- 实现查询结果是A
SELECT 字段列表
FROM A表 LEFT JOIN B表
ON 关联条件
WHERE等其它子句
SELECT e.last_name, e.department_id, d.department_name
FROM employees e
LEFT OUTER JOIN departments d
ON (e.department_id = d.department_id);
-- 实现查询结果是B
SELECT 字段列表
FROM A表 RIGHT JOIN B表
ON 关联条件
WHERE 等其它子句
SELECT e.last_name, e.department_id,d.department_name
FROM employees e
RIGHT OUTER JOIN departments d
ON (e.department_id = d.department_id);
- 需要注意的是:LEFT JOIN 和RIGHT JOIN 只存在于SQL99 及以后的标准中,在SQL92中不存在,只能用(+)表示
SELECT column,... FROM table1
UNION [ALL]
SELECT column,... FROM table2;
UNION 操作符返回两个查询结果集的并集,去除重复记录
UNION ALL 操作符返回两个查询的结果集的并集,对两个结果集的重复部分,不去重
举例:查询部门编号大于90 或邮箱包含a的员工信息
-- 方式一
SELECT * FROM employees WHERE email LIKE '%a%' OR department_id>90;
-- 方式二
SELECT * FROM employees WHERE email LIKE '%a%
UNION
SELECT * FROM employees WHERE department_id>90;
SELECT id, cname FROM t_chinamale WHERE csex='男'
UNION ALL
SELECT id, tname FROM t_usmale WHERE tGender='male';
SELECT employee_id, last_name, department_name
FROM employees e JOIN departments d
ON e.department_id = d.department_id;
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id;
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id;
SELECT employee_id, last_name, department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id IS NULL;
SELECT employee_id, last_name, department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id
WHERE e.department_id IS NULL;
SELECT employee_id, last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id IS NULL
UNION ALL # 没有去重操作,效率高
SELECT employee_id,last_name,department_name
FROM employees e RIGTH JOIN departments d
On e.department_id = d.department_id;
SELECT employee_id, last_name, department_name
FROM employees e LEFT JOIN departments d
ON e.department_id = d.department_id
WHERE d.department_id IS NULL
UNION ALL
SELECT employee_id, last_name, department_name
FROM employees e RIGHT JOIN departments d
ON e.department_id = d.department_id
WHERE e.department_id IS NULL;
select 字段列表
from A表
left join B表
on 关联条件
where 从表关联字段 is null and 等其他子句;
select 字段列表
from A表
right join B表
on 关联条件
where 从表关联字段 is null and 等其他子句;
select 字段列表
from A表
left join B表
on 关联条件
where 等其他子句
union
select 字段列表
from A表
right join B表
on 关联条件
where 等其他子句;
select 字段列表
from A表
left join B表
on 关联条件
where 从表关联字段 is null and 等其他子句
union
select 字段列表
from A表
right join B表
on 关联条件
where 从表关联字段 is null and 等其他子句
SQL99 在SQL92的基础上提供了一些特殊的语法,比如NATURAL JOIN 用来表示自然连接,可以把自然连接为SQL92中的等值连接,它会自动查询俩张连接表中所有相同字段,然后进行等值连接。
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
ON e.`department_id` = d.`department_id`
AND e.`manager_id` = d.`manager_id`;
SELECT employee_id,last_name,department_name
FROM employees e NATURAL JOIN departments d;
SELECT employee_id, last_name, department_name
FROM employees e JOIN departments d
USING(department_id);
与自然连接NATURAL JOIN 不同的是,USING指定了具体的相同的字段名称,需要在USING的括号()中填入要指定的同名字段,同时使用JOIN…USING 可以简化 JOIN ON 的等值连接,与下面的SQL查询结果相同
SELECT employee_id, last_name, department_name
FROM employees e, departments d
WHERE e.department_id = de.department_id;
表连接的约束条件可以有三种方式:WHERE, ON, USING
#关联条件
#把关联条件写在where后面
SELECT last_name,department_name
FROM employees,departments
WHERE employees.department_id = departments.department_id;
#把关联条件写在on后面,只能和JOIN一起使用
SELECT last_name,department_name
FROM employees INNER JOIN departments
ON employees.department_id = departments.department_id;
SELECT last_name,department_name
FROM employees CROSS JOIN departments
ON employees.department_id = departments.department_id;
SELECT last_name,department_name
FROM employees JOIN departments
ON employees.department_id = departments.department_id;
#把关联字段写在using()中,只能和JOIN一起使用
#而且两个表中的关联字段必须名称相同,而且只能表示= #查询员工姓名与基本工资
SELECT last_name,job_title
FROM employees INNER JOIN jobs
USING(job_id);
#n张表关联,需要n-1个关联条件
#查询员工姓名,基本工资,部门名称
SELECT last_name,job_title,department_name
FROM employees,departments,jobs
WHERE employees.department_id = departments.department_id
AND employees.job_id = jobs.job_id;
SELECT last_name,job_title,department_name
FROM employees INNER JOIN departments INNER JOIN jobs
ON employees.department_id = departments.department_id
AND employees.job_id = jobs.job_id;
注意:要控制连接表的数量。多表连接相当于嵌套for循环应用,非常消耗资源,会让SQL查询性能下降得很严重,因此不要连接不必要的表,在许多DSMS中,也会有最大连接表的限制。
【强制】超过三个表静止JOIN。需要JOIN的字段,数据类型保持绝对一致,多表关联查询是,保证被关联的字段需要有索引
说明:即使双表JOIN也要注意表索引、SQL性能
附录:常用SQL标准
在正式开始讲连接表的种类时,我们首先需要知道 SQL 存在不同版本的标准规范,因为不同规范下的表连接操作是有区别的。
SQL 有两个主要的标准,分别是 SQL92 和 SQL99 。92 和 99 代表了标准提出的时间,SQL92 就是 92 年提出的标准规范。当然除了 SQL92 和 SQL99 以外,还存在 SQL-86、SQL-89、SQL:2003、SQL:2008、 SQL:2011 和 SQL:2016 等其他的标准。这么多标准,到底该学习哪个呢?
实际上最重要的 SQL 标准就是 SQL92 和 SQL99。一般来说 SQL92 的形式更简单,但是写的 SQL 语句会比较长,可读性较差。而 SQL99 相比于 SQL92 来说,语法更加复杂,但可读性更强。我们从这两个标准发布的页数也能看出,SQL92 的标准有 500 页,而 SQL99 标准超过了1000 页。实际上从 SQL99 之后,很少有人能掌握所有内容,因为确实太多了。就好比我们使用Windows、Linux 和 Office 的时候,很少有人能掌握全部内容一样。我们只需要掌握一些核心的功能,满足日常工作的需求即可。
SQL92 和 SQL99 是经典的 SQL 标准,也分别叫做 SQL-2 和 SQL-3 标准。也正是在这两个标准发布之后,SQL 影响力越来越大,甚至超越了数据库领域。现如今 SQL 已经不仅仅是数据库领域的主流语言,还是信息领域中信息处理的主流语言。在图形检索、图像检索以及语音检索中都能看到 SQL 语言的使用。
MySQL提供了丰富的内置函数,这些函数使得数据的维护与管理更加方便,能够更好地提供数据的分析与统计功能,在一定程度上提高开发人员进行数据分析与统计的效率。
MySQL提供的内置函数从实现的功能角度可以分为数值函数、字符串函数、日期函数和时间函数、流程控制函数,加密与解密函数、获取MySQL信息函数、聚合函数等。这里将这些丰富的内置函数分为:单行函数、聚合函数(或分组函数)
单行函数
操作数据对象
接收参数返回一个结果
只对一行进行变换
每行返回一个结果
可以嵌套
参数可以是一列或一个值
SELECT
ABS(-123),ABS(32),SIGN(-23),SIGN(43),PI(),CEIL(32.32),CEILING(-43.23),FLOOR(32.32), FLOOR(-43.23),MOD(12,5)
FROM DUAL;
SELECT RAND(),RAND(),RAND(10),RAND(10),RAND(-1),RAND(-1)
FROM DUAL;
SELECT
ROUND(12.33),ROUND(12.343,2),ROUND(12.324,-1),TRUNCATE(12.66,1),TRUNCATE(12.66,-1)
FROM DUAL;
ATAN2(M,N)函数的使用示例如下:
SELECT
SIN(RADIANS(30)),DEGREES(ASIN(1)),TAN(RADIANS(45)),DEGREES(ATAN(1)),DEGREES(ATAN2(1,1) )
FROM DUAL;
聚合函数是对一组数据进行汇总的函数,输入的是一组数据的集合,输入的是单个值
聚合函数
聚合函数语法
可以对数值数据使用AVG和SUM函数
SELECT AVG(salary),MAX(salary),MIN(salary),SUM(salary)
FROM employees
WHERE job_id LIKE "%PEP%";
SELECT MIN(hire_date),MAX(hire_date)
FROM employees;
SELECT COUNT(*)
FROM employees
WHERE department_id =50;
SELECT COUNT(commission_pct)
FROM employees
WHERE department_id =50;
问题1:用COUNT(*),COUNT(1),COUNT(列名)谁更好?
其实,对于MyISAM引擎的表是没有区别的,这种引擎内部有一计数器在维护着行数。
Innodb引擎的表用COUNT(*),COUNT(1),直接读取行,复杂度O(n),因为Innodb真的要去数一遍。但好于具体的COUNT(列名)。
能不能使用COUNT(列名)替代COUNT(*)?
不要使用COUNT(列名)代替COUNT(*),COUNT(*)是SQL92定义的标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关
COUNT(*) 会统计值为NULL的行,而COUNT(列名) 不会统计此列为NULL值的行。
SELECT cloumn,group_function(cloumn)
FROM table
[WHERE condition]
[GROUP BY group_by_expression]
[ORDER BY cloumn]
明确:WHERE一定放在FROM后面
在SELECT列表中所未包含在组函数中的列都应该包含在GROUP BY子句中
SELECT department_id,AVG(salary)
FROM employees
GROUP BY department_id;
- 包含在GROUP BY子句中的列不必包含在SELECT列表中
SELECT AVG(salary)
FROM employees
GROUP BY department_id;
SELECT department_id, dept_id, job_id,SUM(salary)
FROM employees
GROUP BY department_id,job_id;
使用WITH ROLLUP 关键字后,在所有查询出的分组记录之后添加一条记录,该记录计算查询出的所有记录的总和,即统计记录数量。
SELECT department_id,AVG(salary)
FROM employees
WHERE department_id>80
GROUP BY department_id WITH ROLLUP ;
SELECT department_id,MAX(salary)
FROM employees
GROUP BY department_id
HAVING MAX(salary)>10000;
SELECT department_id,AVG(salary)
FROM employees
WHERE AVG(salary)>8000
GROUP BY department_id;
区别1:WHERE可以直接使用表中的字段作为筛选条件,但不能使用分组中的计算函数作为筛选条件;HAVING必须要与GROUP BY配合使用,可以把分组计算的函数和分组字段作为筛选条件。
这决定了,在需要对数据进行分组统计的时候,HAVING可以完成WHERE不能完成的任务,这是因为,在查询语法结构中,WHERE在GROUPBY之前,所以无法对分组结果进行筛选。HAVING 在GROUP BY之后,可以使用分组字段和分组中的计算函数,对分组的结果集进行筛选,这个功能是WHERE无法完成的。另外,WHERE排除的记录不在包括在分组中。
区别2:如果需要通过连接从关联表中获取需要的数据,WHERE是先筛选后连接,而HAVING是先连接后筛选的
这点,决定了在关联查询中,WHERE 比HAVING更高效。因为WHERE可以先筛选,用一个筛选后的较小数据集合关联表进行连接,这样占用的资源比较少,执行效率也比较高。HAVING则需要先把结果集准备好,也就是用未被筛选的数据集进行关联,然后对这个大的数据集进行筛选,这样占用的资源就比较多,执行效率也低。
开发中选择
WHERE和HAVING也不是相互排斥的,可以在一个查询里面同时使用WHERE和HAVING。包含分组统计函数的条件用HAVING,普通条件用WHERE。这样就利用了WHERE条件的高效快速,又发挥了HAVING可使用包含分组统计函数的查询条件的优先。当 数据量特别大的时候,运行效率会有很大差别。
-- 方式一
SELECT .......
FROM ...,...,...
WHERE 多表的连接条件
AND 不包含组函数的过滤条件
GROUP BY ...,...
HAVING 包含组函数的过滤条件
ORDER BY ... ASC/DESC
LIMT ...,...
-- 方式二
SELECT ...,...,...
FROM ...JOIN ....
ON 多表连接条件
JOIN...
ON...
WHERE 不包含组函数的过滤条件
AND/OR 不包含组函数的过滤条件
GROUP BY ...,...
HAVING 包含组函数的过滤条件
ORDER BY ...ASC/DESC
LIMINT ...,...
----
-- 其中:
-- 1.FROM :从哪张表中筛选
-- 2.ON:关联多表查询是,取出笛卡尔积
-- 3.WHERE:从表中筛选的条件
-- 4.GROUP BY:分组依据
-- 5.HAVING:在统计结果中再次筛选
-- 6.ORDER BY:排序
-- 7.LIMIT:分页
需要记住SELECT查询时的俩个顺序:
SELECT ... FROM ... WHERE ...GROUP BY ...HAVING ...ORDER BY ... LIMINT ...
SELECT DISTINCT player_id,player_name,count(*) as num #顺序5
FROM player JOIN team ON player.team_id = team.team_id #顺序1
WHERE height>1.80 # 顺序2
GROUP BY player.team_id #顺序3
HAVING num>2 #顺序4
ORDER BY num DESC #顺序 6
LIMIT 2 #顺序7
在SELECT语句执行这些步骤时候,每个步骤都会产生一个虚拟表,然后将这个虚拟表传入下一个步骤中作为输入。需要注意的是,这些步骤隐含在SQL的执行过程中,对我们来说是不可见的。
SELECT 是首先执行FROM这一步的,在这个阶段,如果是多张表联合查询,会经历下面几个步骤:
当如果操作的是两张以上的表,还会重复上面的步骤,直到所有表都被处理完为止。这个过程得到是我们的原始数据。