MySQL初学者指南

发表时间: 2021-04-02 13:06

Mysql基础入门

一、Mysql介绍

1.1基础概念

数据库

数据库就是[存储数据的仓库],其本质是一个[文件系统],数据按照特定的格式将数据存储起来,用户可以通过SQL对数据库中的数据进行增加,修改,删除及查询操作。

关系型数据库

数据库中的[记录是有行有列的数据库]就是关系型数据库,与之相反的就是NoSQL数据库了。

数据库和表


数据库管理系统(DataBase Management System,DBMS):指一种[操作和管理数据库]的大型软件,用于建立、使用和维护数据库,对数据库进行统一管理和控制,以保证数据库的安全性和完整性。用户通过数据库管理系统访问数据库中表内的数据。(记录)

常见的数据库管理系统

- MYSQL :开源免费的数据库,小型的数据库.已经被Oracle收购了。MySQL5.5版本之后都是由Oracle发布的版本。- Oracle :收费的大型数据库,Oracle公司的产品。Oracle收购SUN公司,收购MYSQL。- DB2 :IBM公司的数据库产品,收费的。常应用在银行系统中。在中国的互联网公司,要求去IOE(IBM小型机、Oracle数据库、EMC存储设备)- SQLServer:MicroSoft 公司收费的中型的数据库。C#、.net等语言常使用。- SyBase :已经淡出历史舞台。提供了一个非常专业数据建模的工具PowerDesigner。- SQLite : 嵌入式的小型数据库,应用在手机端。

MySQL

MySQL 是最流行的【关系型数据库管理系统】,在WEB应用方面 MySQL是最好的RDBMS应用软件之一。

MySQL发展历程

- MySQL的历史可以追溯到1979年,一个名为Monty Widenius的程序员在为TcX的小公司打工,并且用BASIC设计了一个报表工具,使其可以在4MHz主频和16KB内存的计算机上运行。当时,这只是一个很底层的且仅面向报表的存储引擎,名叫Unireg。- 1990年,TcX公司的客户中开始有人要求为他的API提供SQL支持。Monty直接借助于mSQL的代码,将它集成到自己的存储引擎中。令人失望的是,效果并不太令人满意,决心自己重写一个SQL支持。- 1996年,MySQL 1.0发布,它只面向一小拨人,相当于内部发布。- 到了1996年10月,MySQL 3.11.1发布(MySQL没有2.x版本),最开始只提供Solaris下的二进制版本。一个月后,Linux版本出现了。在接下来的两年里,MySQL被依次移植到各个平台。- 【1999~2000年】,【MySQL AB】公司在瑞典成立。Monty雇了几个人与Sleepycat合作,开发出了【Berkeley DB引擎】, 由于BDB支持事务处理,因此MySQL从此开始支持事务处理了。- 2000,MySQL不仅公布自己的源代码,并采用GPL(GNU General Public License)许可协议,正式进入开源世界。同年4月,MySQL对旧的存储引擎ISAM进行了整理,将其命名为MyISAM。- 2001年,集成Heikki Tuuri的存储引擎【InnoDB】,这个引擎不仅能【支持事务处理,并且支持行级锁】。后来该引擎被证明是最为成功的MySQL事务存储引擎。【MySQL与InnoDB的正式结合版本是4.0】- 2003年12月,【MySQL 5.0】版本发布,提供了视图、存储过程等功能。- 【2008年1月】,【MySQL AB公司被Sun公司以10亿美金收购】,MySQL数据库进入Sun时代。在Sun时代,Sun公司对其进行了大量的推广、优化、Bug修复等工作。- 2008年11月,MySQL 5.1发布,它提供了分区、事件管理,以及基于行的复制和基于磁盘的NDB集群系统,同时修复了大量的Bug。- 【2009年4月】,Oracle公司以74亿美元收购Sun公司,自此MySQL数据库进入Oracle时代,而其第三方的存储引擎InnoDB早在2005年就被Oracle公司收购。- 2010年12月,【MySQL 5.5发布】,其主要新特性包括半同步的复制及对SIGNAL/RESIGNAL的异常处理功能的支持,【最重要的是InnoDB存储引擎终于变为当前MySQL的默认存储引擎】。MySQL 5.5不是时隔两年后的一次简单的版本更新,而是加强了MySQL各个方面在企业级的特性。Oracle公司同时也承诺MySQL 5.5和未来版本仍是采用GPL授权的开源产品。

SQL

【SQL是Structured Query Language的缩写】,它的前身是著名的关系数据库原型系统System R所采用的SEQUEL语言。作为一种访问【关系型数据库的标准语言】,SQL自问世以来得到了广泛的应用,不仅是著名的大型商用数据库产品Oracle、DB2、Sybase、SQL Server支持它,很多开源的数据库产品如PostgreSQL、MySQL也支持它,甚至一些小型的产品如Access也支持SQL。近些年蓬勃发展的NoSQL系统最初是宣称不再需要SQL的,后来也不得不修正为Not Only SQL,来拥抱SQL。蓝色巨人IBM对关系数据库以及SQL语言的形成和规范化产生了重大的影响,第一个版本的SQL标准SQL86就是基于System R的手册而来的。Oracle在1979年率先推出了支持SQL的商用产品。随着数据库技术和应用的发展,为不同RDBMS提供一致的语言成了一种现实需要。对SQL标准影响最大的机构自然是那些著名的数据库产商,而具体的制订者则是一些非营利机构,例如【国际标准化组织ISO、美国国家标准委员会ANSI】等。各国通常会按照 ISO标准和ANSI标准(这两个机构的很多标准是差不多等同的)制定自己的国家标准。中国是ISO标准委员会的成员国,也经常翻译一些国际标准对应的中文版。标准为了避免采用具体产品的术语,往往会抽象出很多名词,从而增加了阅读和理解的难度,翻译成中文之后更容易词不达意。对于数据库系统实现者和用户而言,很多时候还不如直接读英文版本为好。虽然正式的标准不像RFC那样可以从网络上免费获得,标准草案还是比较容易找到的(例如:
http://www.jtc1sc32.org/doc/)。待批准的标准草案和最终的标准也没有什么实质上的区别,能够满足日常工作的需要。下面是SQL发展的简要历史:1986年,ANSI X3.135-1986,ISO/IEC 9075:1986,SQL-861989年,ANSI X3.135-1989,ISO/IEC 9075:1989,SQL-891992年,ANSI X3.135-1992,ISO/IEC 9075:1992,SQL-92(SQL2)1999年,ISO/IEC 9075:1999,SQL:1999(SQL3)2003年,ISO/IEC 9075:2003,SQL:20032008年,ISO/IEC 9075:2008,SQL:20082011年,ISO/IEC 9075:2011,SQL:2011如果要了解标准的内容,比较推荐的方法是【泛读SQL92】(因为它涉及了SQL最基础和最核心的一些内容),然后增量式的阅读其他标准。


不只是mysql还有其他数据库,在SQL92或者SQL99这些国际SQL标准基础之上,它们还扩展了自己的一些SQL语句,比如MySQL中的limit关键字

SQL语言分类

- 数据定义语言:简称【DDL】(Data Definition Language),用来定义数据库对象:数据库,表,列等。关键字:create,alter,drop等- 数据操作语言:简称【DML】(Data Manipulation Language),用来对数据库中表的记录进行更新。关键字:insert,delete,update等- 数据控制语言:简称【DCL】(Data Control Language),用来定义数据库的访问权限和安全级别,及创建用户;关键字:grant等- 数据查询语言:简称【DQL】(Data Query Language),用来查询数据库中表的记录。关键字:select,from,where等

1.2MySQL安装

操作系统:CentOS 7

MySQL:5.6

#查看是否安装rpm -qa|grep mysqlyum repolist all | grep mysql

1、卸载MySQL

yum remove -y mysql mysql-libs mysql-common #卸载mysqlrm -rf /var/lib/mysql #删除mysql下的数据文件rm /etc/my.cnf #删除mysql配置文件yum remove -y
mysql-community-release-el6-5.noarch #删除组件

2、安装MySQL5.6

#下载rpm文件wget
http://repo.mysql.com/mysql-community-release-el6-5.noarch.rpm#执行rpm源文件rpm -ivh
mysql-community-release-el6-5.noarch.rpm#执行安装文件yum install mysql-community-server

启动MySQL

systemctl start mysqld

设置root用户密码

例如:为 root 账号设置密码为 root :/usr/bin/mysqladmin -u root password 'root' #没有密码 有原来的密码则加/usr/bin/mysqladmin -u root -p '123' password 'root'

登录MySQL

mysql -uroot -proot-u:指定数据库用户名-p:指定数据库密码,记住-u和登录密码之间没有空格

配置MySQL

vim /etc/my.cnf[mysqld]# MySQL设置大小写不敏感:默认:区分表名的大小写,不区分列名的大小写# 0:大小写敏感 1:大小写不敏感lower_case_table_names=1# 默认字符集character-set-server=utf8

MySQL远程连接授权

授权命令grant 权限 on 数据库对象 to 用户GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;FLUSH PRIVILEGES;--刷新权限说明ALL PRIVILEGES :表示授予所有的权限,此处可以指定具体的授权权限。*.* :表示所有库中的所有表'root'@'%' : myuser是数据库的用户名,%表示是任意ip地址,可以指定具体ip地址。IDENTIFIED BY 'mypassword' :mypassword是数据库的密码。

  • 授权命令grant 权限 on 数据库对象 to 用户GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;FLUSH PRIVILEGES;--刷新权限说明ALL PRIVILEGES :表示授予所有的权限,此处可以指定具体的授权权限。*.* :表示所有库中的所有表'root'@'%' : myuser是数据库的用户名,%表示是任意ip地址,可以指定具体ip地址。IDENTIFIED BY 'mypassword' :mypassword是数据库的密码。
  • 授权命令grant 权限 on 数据库对象 to 用户GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;FLUSH PRIVILEGES;--刷新权限说明ALL PRIVILEGES :表示授予所有的权限,此处可以指定具体的授权权限。*.* :表示所有库中的所有表'root'@'%' : myuser是数据库的用户名,%表示是任意ip地址,可以指定具体ip地址。IDENTIFIED BY 'mypassword' :mypassword是数据库的密码。
  • 授权命令grant 权限 on 数据库对象 to 用户GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;FLUSH PRIVILEGES;--刷新权限说明ALL PRIVILEGES :表示授予所有的权限,此处可以指定具体的授权权限。*.* :表示所有库中的所有表'root'@'%' : myuser是数据库的用户名,%表示是任意ip地址,可以指定具体ip地址。IDENTIFIED BY 'mypassword' :mypassword是数据库的密码。
  • 授权命令grant 权限 on 数据库对象 to 用户GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;FLUSH PRIVILEGES;--刷新权限说明ALL PRIVILEGES :表示授予所有的权限,此处可以指定具体的授权权限。*.* :表示所有库中的所有表'root'@'%' : myuser是数据库的用户名,%表示是任意ip地址,可以指定具体ip地址。IDENTIFIED BY 'mypassword' :mypassword是数据库的密码。


关闭linux的防火墙

systemctl stop firewalld(默认)systemctl disable firewalld.service(设置开启不启动)

3、MySQL5.7安装

安装方式:通过rmp离线包的方式安装。

卸载Linux自带mariadb

rpm -qa | grep mariabdrpm -e mariadb… --nodepsrpm -qa | grep mariabd

解压rpm包

tar -xvf 包名;

安装

#安装公共包rpm -ivh
mysql-community-common-5.7.28-1.el7.x86_64.rpmrpm -ivh
mysql-community-libs-5.7.28-1.el7.x86_64.rpmrpm -ivh
mysql-community-libs-compat-5.7.28-1.el7.x86_64.rpm#安装服务端和客户端rpm -ivh
mysql-community-client-5.7.28-1.el7.x86_64.rpmrpm -ivh
mysql-community-server-5.7.28-1.el7.x86_64.rpm#安装工具包rpm -ivh
mysql-community-devel-5.7.28-1.el7.x86_64.rpm#初始化mysql 并创建一个user的用户mysqld --initialize --user=mysql

登录设置密码

#启动mysqlsystemctl start mysqld#查看默认密码cat /var/log/mysqld.log | grep password #登录mysql -uroot -p9Nurpw%Ff/w##修改密码set password=password('root');


二、Mysql基本语法

2.1 DDL语句

1、数据库操作

创建数据库

create database 数据库名;create database 数据库名 character set 字符集;

查看数据库

show databases;show create database 数据库名;

删除数据库慎用

drop database 数据库名称;

其他数据库操作命令

切换数据库use 数据库名;查看正在使用的数据库select database();

2、表操作

常用的类型

数字型:int浮点型:double字符型:varchar(可变长字符串)日期类型:date(只有年月日,没有时分秒) datetime(年月日,时分秒)boolean类型:不支持,一般使用tinyint替代(值为0和1)



创建表

create table 表名( 字段名 类型(长度) 约束, 字段名 类型(长度) 约束);

单表约束:

- 主键约束:primary key- 唯一约束:unique- 非空约束:not null主键约束 = 唯一约束 + 非空约束

查看表

show tables;desc 表名;【查看表结构】

修改表

alter table 表名 add 列名 类型(长度) 约束; --修改表添加列.alter table 表名 modify 列名 类型(长度) 约束; --修改表修改列的类型长度及约束.alter table 表名 change 旧列名 新列名 类型(长度) 约束; --修改表修改列名.alter table 表名 drop 列名; --修改表删除列.rename table 表名 to 新表名; --修改表名alter table 表名 character set 字符集; --修改表的字符集

2.2DML语句

1、Insert语句

insert into 表 (列名1,列名2,列名3..) values (值1,值2,值3..); -- 向表中插入某些列insert into 表 values (值1,值2,值3..); --向表中插入所有列insert into 表 (列名1,列名2,列名3..) values select (列名1,列名2,列名3..) from 表insert into 表 values select * from 表

注意:

1. 列名数与values后面的值的个数相等

2. 列的顺序与插入的值得顺序一致

3. 列名的类型与插入的值要一致.

4. 插入值得时候不能超过最大长度.

5. 值如果是字符串或者日期需要加引号’’ (一般是单引号)

例子:

INSERT INTO sort(sid,sname) VALUES('s001', '电器');INSERT INTO sort(sid,sname) VALUES('s002', '服饰');INSERT INTO sort VALUES('s003', '化妆品');INSERT INTO sort VALUES('s004','书籍');

2、update

update 表名 set 字段名=值,字段名=值;update 表名 set 字段名=值,字段名=值 where 条件;

注意:

1. 列名的类型与修改的值要一致.

2. 修改值得时候不能超过最大长度.

3. 值如果是字符串或者日期需要加’’.

3、delete

delete from 表名 [where 条件];

面试题:

删除表中所有记录使用【delete from 表名】,还是用【truncate table 表名】?删除方式:- delete :一条一条删除,不清空auto_increment记录数。- truncate :直接将表删除,重新建表,auto_increment将置为零,从新开始。

2.3DQL语句

1、准备

案例演示:#商品表CREATE TABLE product (pid INT PRIMARY KEY AUTO_INCREMENT, # 自增加 AUTO_INCREMENTpname VARCHAR(20),#商品名称price DOUBLE, #商品价格pdate DATE, # 日期cid int #分类ID);#目录表create table category( id INT PRIMARY KEY , cname varchar(100));INSERT INTO product VALUES(NULL,'泰国大榴莲', 98, NULL, 1);INSERT INTO product VALUES(NULL,'泰国大枣', 38, NULL, 1);INSERT INTO product VALUES(NULL,'新疆切糕', 68, NULL, 2);INSERT INTO product VALUES(NULL,'十三香', 10, NULL, 2);INSERT INTO product VALUES(NULL,'泰国大枣', 20, NULL, 2);insert into product values(null,'泰国大枣',98,null,20); #没有对应insert into product values(null,'iPhone手机',800,null,30);#没有对应INSERT INTO category VALUES(1,'国外食品');INSERT INTO category VALUES(2,'国内食品');INSERT INTO category VALUES(3,'国内服装'); #没有对应

2、完整DQL语法顺序

SELECT DISTINCT < select_list >FROM < left_table > < join_type >JOIN < right_table > ON < join_condition >WHERE < where_condition >GROUP BY < group_by_list >HAVING < having_condition >ORDER BY < order_by_condition >LIMIT < limit_number >

3、简单查询

查询所有的商品

select * from product;

查询商品名称和商品价格

select pname,price from product;

别名查询,使用的as关键字,as可以省略的

表别名:

select * from product as p;

列别名:

select pname as pn from product;

去掉重复值.

select distinct price from product;

查询结果是表达式(运算查询):将所有商品的价格+10元进行显示.

select pname,price+10 from product;

4、条件查询

查询商品名称为十三香的商品所有信息:

select * from product where pname = '十三香';

查询商品价格>60元的所有的商品信息:

select * from product where price > 60;

where后的条件写法:

> ,<,=,>=,<=,<>like 使用占位符 _ 和 % _代表一个字符 %代表任意个字符. select * from product where pname like '%新%';in在某个范围中获得值(exists). select * from product where pid in (2,5,8);


5、排序

查询所有的商品,按价格进行排序.(asc-升序,desc-降序)

select * from product order by price;

查询名称有新的商品的信息并且按价格降序排序.

select * from product where pname like '%新%' order by price desc;

6、聚合函数(组函数)

特点:只对单列进行操作

常用的聚合函数:

sum():求某一列的和avg():求某一列的平均值max():求某一列的最大值min():求某一列的最小值count():求某一列的元素个数

获得所有商品的价格的总和:

select sum(price) from product;

获得所有商品的平均价格:

select avg(price) from product;

获得所有商品的个数:

select count(*) from product;

6、分组

根据cno字段分组,分组后统计商品的个数.

select cid,count(*) from product group by cid;

根据cno分组,分组统计每组商品的平均价格,并且平均价格> 60;

select cid,avg(price) from product group by cid having avg(price)>60;

注意事项:

1. select语句中的列(非聚合函数列),必须出现在group by子句中2. group by子句中的列,不一定要出现在select语句中3. 聚合函数只能出现select语句中或者having语句中,一定不能出现在where语句中。

7、分页查询

lIMIT 关键字不是 SQL92 标准提出的关键字,它是 MySQL 独有的语法。

通过 limit 关键字, MySQL 实现了物理分页。

分页分为逻辑分页和物理分页:

逻辑分页:将数据库中的数据查询到内存之后再进行分页。物理分页:通过LIMIT关键字,直接在数据库中进行分页,最终返回的数据,只是分页后的数据。

格式:

ELECT * FROM table LIMIT [offset,] rows

案例:分页查询商品表,每页3条记录,查第一页

select * from product limit 0,3

8、子查询

子查询允许把一个查询嵌套在另一个查询当中。子查询,又叫内部查询,相对于内部查询,包含内部查询的就称为外部查询。子查询可以包含普通select可以包括的任何子句,比如:distinct、 group by、order by、limit、

join和union等;但是对应的外部查询必须是以下语句之一:select、insert、update、delete。

子查询位置:select中、from 后、where 中.

案例:查询“化妆品”分类下的商品信息

select * from product where type = (select type from table2 )

9、其他查询语句

union 集合的并集(不包含重复记录)

unionall 集合的并集(包含重复记录)

三、SQL解析

3.1SQL解析顺序

SELECT DISTINCT < select_list >FROM < left_table > < join_type >JOIN < right_table > ON < join_condition >WHERE < where_condition >GROUP BY < group_by_list >HAVING < having_condition >ORDER BY < order_by_condition >LIMIT < limit_number >

解析顺序

-- 行过滤1 FROM <left_table>2 ON <join_condition>3 <join_type> JOIN <right_table> 第二步和第三步会循环执行4 WHERE <where_condition> 第四步会循环执行,多个条件的执行顺序是从左往右的。5 GROUP BY <group_by_list>6 HAVING <having_condition>--列过滤7 SELECT 分组之后才会执行SELECT8 DISTINCT <select_list>--排序9 ORDER BY <order_by_condition>-- MySQL附加10 LIMIT <limit_number> 前9步都是SQL92标准语法。limit是MySQL的独有语法。

虽然自己没想到是这样的,不过一看还是很自然和谐的,从哪里获取,不断的过滤条件,要选择一样或不一样的,排好序,那才知道要取前几条呢。既然如此了,那就让我们根据案例一步步来看看其中的细节吧。

3.2SQL解析详细步骤

1、FROM

对FROM的左边的表和右边的表计算 笛卡尔积(CROSS JOIN) 。产生 虚表VT1

mysql> select * from product,category;+-----+-----------------+-------+-------+------+----+--------------+| pid | pname | price | pdate | cid | id | cname |+-----+-----------------+-------+-------+------+----+--------------+| 1 | 泰国大榴莲 | 98 | NULL | 1 | 1 | 国外食品 || 1 | 泰国大榴莲 | 98 | NULL | 1 | 2 | 国内食品 || 1 | 泰国大榴莲 | 98 | NULL | 1 | 3 | 国内服装 || 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 || 2 | 泰国大枣 | 38 | NULL | 1 | 2 | 国内食品 || 2 | 泰国大枣 | 38 | NULL | 1 | 3 | 国内服装 || 3 | 新疆切糕 | 68 | NULL | 2 | 1 | 国外食品 || 3 | 新疆切糕 | 68 | NULL | 2 | 2 | 国内食品 || 3 | 新疆切糕 | 68 | NULL | 2 | 3 | 国内服装 || 4 | 十三香 | 10 | NULL | 2 | 1 | 国外食品 || 4 | 十三香 | 10 | NULL | 2 | 2 | 国内食品 || 4 | 十三香 | 10 | NULL | 2 | 3 | 国内服装 || 5 | 泰国大枣 | 20 | NULL | 2 | 1 | 国外食品 || 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 || 5 | 泰国大枣 | 20 | NULL | 2 | 3 | 国内服装 || 6 | 泰国大枣 | 98 | NULL | 20 | 1 | 国外食品 || 6 | 泰国大枣 | 98 | NULL | 20 | 2 | 国内食品 || 6 | 泰国大枣 | 98 | NULL | 20 | 3 | 国内服装 || 7 | iPhone手机 | 800 | NULL | 30 | 1 | 国外食品 || 7 | iPhone手机 | 800 | NULL | 30 | 2 | 国内食品 || 7 | iPhone手机 | 800 | NULL | 30 | 3 | 国内服装 |+-----+-----------------+-------+-------+------+----+--------------+21 rows in set (0.00 sec)

2、ON过滤

对虚表VT1 进行ON筛选,只有那些符合的行才会被记录在虚表VT2中。

注意:这里因为语法限制,使用了'WHERE'代替,从中读者也可以感受到两者之间微妙的关系;

mysql> select * from product a , category b where a.cid=b.id;+-----+-----------------+-------+-------+------+----+--------------+| pid | pname | price | pdate | cid | id | cname |+-----+-----------------+-------+-------+------+----+--------------+| 1 | 泰国大榴莲 | 98 | NULL | 1 | 1 | 国外食品 || 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 || 3 | 新疆切糕 | 68 | NULL | 2 | 2 | 国内食品 || 4 | 十三香 | 10 | NULL | 2 | 2 | 国内食品 || 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 |+-----+-----------------+-------+-------+------+----+--------------+5 rows in set (0.00 sec)

3、OUTER JOIN添加外部列

如果指定了 OUTER JOIN(比如left join、 right join) ,那么 保留表中未匹配的行 就会作为外部行添加 到 虚拟表VT2 中,产生 虚拟表VT3 。

如果FROM子句中包含两个以上的表的话,那么就会对上一个join连接产生的结果VT3和下一个表重复执行步骤1~3这三个步骤,一直到处理完所有的表为止。

mysql> select * from product a left outer join category b on a.cid=b.id; # 以左表数据为准+-----+-----------------+-------+-------+------+------+--------------+| pid | pname | price | pdate | cid | id | cname |+-----+-----------------+-------+-------+------+------+--------------+| 1 | 泰国大榴莲 | 98 | NULL | 1 | 1 | 国外食品 || 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 || 3 | 新疆切糕 | 68 | NULL | 2 | 2 | 国内食品 || 4 | 十三香 | 10 | NULL | 2 | 2 | 国内食品 || 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 || 6 | 泰国大枣 | 98 | NULL | 20 | NULL | NULL || 7 | iPhone手机 | 800 | NULL | 30 | NULL | NULL |+-----+-----------------+-------+-------+------+------+--------------+7 rows in set (0.00 sec)mysql> select * from product a right outer join category b on a.cid=b.id; #以右表数据为准+------+-----------------+-------+-------+------+----+--------------+| pid | pname | price | pdate | cid | id | cname |+------+-----------------+-------+-------+------+----+--------------+| 1 | 泰国大榴莲 | 98 | NULL | 1 | 1 | 国外食品 || 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 || 3 | 新疆切糕 | 68 | NULL | 2 | 2 | 国内食品 || 4 | 十三香 | 10 | NULL | 2 | 2 | 国内食品 || 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 || NULL | NULL | NULL | NULL | NULL | 3 | 国内服装 |+------+-----------------+-------+-------+------+----+--------------+6 rows in set (0.00 sec)

4、WHERE

对虚拟表VT3 进行WHERE条件过滤。只有符合的记录才会被插入到 虚拟表VT4 中。

注意:

此时因为分组,不能使用聚合运算;也不能使用SELECT中创建的别名;

与ON的区别:

如果有外部列,ON针对过滤的是关联表,主表(保留表)会返回所有的列;

如果没有添加外部列,两者的效果是一样的;

应用:

对主表的过滤应该放在WHERE;

对于关联表,先条件查询后连接则用ON,先连接后条件查询则用WHERE;

mysql> select * from product a left outer join category b on a.cid=b.id wherea.pname='泰国大枣';+-----+--------------+-------+-------+------+------+--------------+| pid | pname | price | pdate | cid | id | cname |+-----+--------------+-------+-------+------+------+--------------+| 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 || 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 || 6 | 泰国大枣 | 98 | NULL | 20 | NULL | NULL |+-----+--------------+-------+-------+------+------+--------------+3 rows in set (0.00 sec)

5、GROUP BY

根据group by子句中的列,对VT4中的记录进行分组操作,产生 虚拟表VT5 。

注意:

其后处理过程的语句,如SELECT,HAVING,所用到的列必须包含在GROUP BY中。对于没有出现的,得用聚合函数;

原因:

GROUP BY改变了对表的引用,将其转换为新的引用方式,能够对其进行下一级逻辑操作的列会减少;

我的理解是:

根据分组字段,将具有相同分组字段的记录归并成一条记录,因为每一个分组只能返回一条记录,除非是被过滤掉了,而不在分组字段里面的字段可能会有多个值,多个值是无法放进一条记录的,所以必须通过聚合函数将这些具有多值的列转换成单值;

mysql> select * from product a left outer join category b on a.cid=b.id wherea.pname='泰国大枣' group by a.price;+-----+--------------+-------+-------+------+------+--------------+| pid | pname | price | pdate | cid | id | cname |+-----+--------------+-------+-------+------+------+--------------+| 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 || 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 || 6 | 泰国大枣 | 98 | NULL | 20 | NULL | NULL |+-----+--------------+-------+-------+------+------+--------------+3 rows in set (0.01 sec)

6、HAVING

对 虚拟表VT5 应用having过滤,只有符合的记录才会被 插入到 虚拟表VT6 中。

mysql> select * from product a left outer join category b on a.cid=b.id wherea.pname='泰国大枣' group by a.price having b.id <=2;+-----+--------------+-------+-------+------+------+--------------+| pid | pname | price | pdate | cid | id | cname |+-----+--------------+-------+-------+------+------+--------------+| 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 || 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 |+-----+--------------+-------+-------+------+------+--------------+2 rows in set (0.00 sec)

7、SELECT

这个子句对SELECT子句中的元素进行处理,生成VT5表。

(5-J1)计算表达式 计算SELECT 子句中的表达式,生成VT5-J1

8、DISTINCT

寻找VT5-1中的重复列,并删掉,生成VT5-J2

如果在查询中指定了DISTINCT子句,则会创建一张内存临时表(如果内存放不下,就需要存放在硬盘

了)。这张临时表的表结构和上一步产生的虚拟表VT5是一样的,不同的是对进行DISTINCT操作的列增

加了一个唯一索引,以此来除重复数据。

mysql> select distinct a.pname from product a left outer join category b ona.cid=b.id where a.pname='泰国大枣' group by a.price ;+--------------+| pname |+--------------+| 泰国大枣 |+--------------+1 row in set (0.00 sec)

9、ORDER BY

从 VT5-J2 中的表中,根据ORDER BY 子句的条件对结果进行排序,生成VT6表。

注意:

唯一可使用SELECT中别名的地方;

mysql> select * from product a left outer join category b on a.cid=b.id wherea.pname='泰国大枣' group by a.price having b.id <=2 order by b.id;+-----+--------------+-------+-------+------+------+--------------+| pid | pname | price | pdate | cid | id | cname |+-----+--------------+-------+-------+------+------+--------------+| 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 || 5 | 泰国大枣 | 20 | NULL | 2 | 2 | 国内食品 |+-----+--------------+-------+-------+------+------+--------------+2 rows in set (0.00 sec)

10、LIMIT

LIMIT子句从上一步得到的 VT6虚拟表 中选出从指定位置开始的指定行数据。

注意:

offset 和 rows 的正负带来的影响;

当偏移量很大时效率是很低的,可以这么做:

采用子查询的方式优化 ,在子查询里先从索引获取到最大id,然后倒序排,再取N行结果集

采用INNER JOIN优化 ,JOIN子句里也优先从索引获取ID列表,然后直接关联查询获得最终结果

mysql> select * from product a left outer join category b on a.cid=b.id wherea.pname='泰国大枣' group by a.price having b.id <=2 order by b.id limit 1; +-----+--------------+-------+-------+------+------+--------------+| pid | pname | price | pdate | cid | id | cname |+-----+--------------+-------+-------+------+------+--------------+| 2 | 泰国大枣 | 38 | NULL | 1 | 1 | 国外食品 |+-----+--------------+-------+-------+------+------+--------------+1 row in set (0.00 sec)

3.3SQL解析顺序总结



1、流程分析:

1. FROM(将最近的两张表,进行笛卡尔积)---VT1

2. ON(将VT1按照它的条件进行过滤)---VT2

3. LEFT JOIN(保留左表的记录)---VT3

4. WHERE(过滤VT3中的记录)--VT4…VTn

5. GROUP BY(对VT4的记录进行分组)---VT5

6. HAVING(对VT5中的记录进行过滤)---VT6

7. SELECT(对VT6中的记录,选取指定的列)--VT7

8. ORDER BY(对VT7的记录进行排序)--VT8

9. LIMIT(对排序之后的值进行分页)--MySQL特有的语法

2、流程说明:

  • 单表查询:根据 WHERE 条件过滤表中的记录,形成中间表(这个中间表对用户是不可见的);然后根据 SELECT 的选择列选择相应的列进行返回最终结果。
  • 两表连接查询:对两表求积(笛卡尔积)并用 ON 条件和连接连接类型进行过滤形成中间表;然后根据WHERE条件过滤中间表的记录,并根据 SELECT 指定的列返回查询结果。【笛卡尔积:行相乘、列相加。】
  • 多表连接查询:先对第一个和第二个表按照两表连接做查询,然后用查询结果和第三个表做连接查询,以此类推,直到所有的表都连接上为止,最终形成一个中间的结果表,然后根据WHERE条件过滤中间表的记录,并根据SELECT指定的列返回查询结果。

3、WHERE条件解析顺序

1. MySQL :从左往右去执行 WHERE 条件的。

2. Oracle :从右往左去执行 WHERE 条件的。

写WHERE条件的时候,优先级高的部分要去编写过滤力度最大的条件语句。