本文主要是MySQL的基础记录,学习过程中的随笔,主要是一些基础概念和查询方法。至于MySQL进阶的部分,包括数据的增删、触发器、事务和基本脚本语言的编写,很快就更新。
文中使用的图形化的数据库操作工具是DBeaver完全免费开源,可以直接去官网下载。但是笔者后面几张截图上使用的是 Navicat,笔者更喜欢Navicat,主要是因为界面更加的简洁美观,同时支持连接各种主流数据库以及云数据库的连接。Navicat的专业版是要收费的,但是这里有破解版的网盘资源 (doge)
链接:https://pan.baidu.com/s/15UvdfCxF57HyIa-Kr36_Ig
提取码:7mbg
数据库是一种以有组织的方式存储数据的数据集合。数据库保存的通常是一个文件或一组文件
在初学的时候有几个容易弄混淆的概念,那就是数据库和操作数据库的软件。不能将操作数据库的软件成为数据库,确切地说,数据库软件实际上应该称为DBMS(Database Management Sysment),数据库软件是用来进行数据库的数据的增删改查以及其他一系列操作的工具。
当我们将文件或是数据放入数据库的时候,并不是随意的将它放在数据库中。而是将它放在一个特定文件中。该文件由我们创建,然后将数据放入其中。
在数据库领域中,这种文件就称为表。表是一种结构化的文件,可以用来存储某种特定类型的数据。可以用来存储顾客清单,目录等其他信息清单。
表名:数据库中每一个表都有一个名字,用来进行标识。此名字是唯一的,但是在不同的数据库中,可以使用相同的表名。
上图所示的即为一个名为sql_store的数据库中包含的所有的表
列(column)表中的一个字段,所有的表都是由一个或多个列组成的。
该图展示的就是一个名为customer的表中的各列
数据类型(datatype)每个列都有相应的数据类型,他只能允许存放一种数据类型的数据
该图展示的就是每一列的数据类型
一个表中的数据是按行存储的,所保存的每个记录都存储在自己的行内。
如图所示的即为一行数据
唯一表示表中每一行的这个列叫做主键。主键用来表示一个特定的行。没有逐渐的话,进行更新和删除等操作就会变得很难,因为没有安全的方法保障只涉及相关的行
这个后面讲到如何创建一个表的时候会讲到
MySQL已经存在很久了,仍然是目前比较主流的数据库软件。他的主要优点如下
这里建议观看MOSH老师的安装视频,他讲解的非常的详细,而且包含了Windows和Mac两个版本的安装过程演示,对不同系统的使用者都非常友好。我的安装也是从这个视频学会的。
Mosh老师的B站视频分享
如果有条件翻墙的话,推荐上YouTube直接看Mosh老师的账号。它里面有很多讲解的很详细的视频
在成功安装了MySQL并配置了环境变量后,就能在命令行中进行连接数据库并进行数据库的操作。
值得重视的是,随着DBeaver和Navicat软件得到普及,在进行数据库的操作时,图形化操作尽管非常的方便和快捷,但是基本的命令行的操作还是必须要学会的。
下面就是几个常见的MySQL常见的命令行语句
SQL语句是由比较常见和简单的单词组成的语句。这些单词简称为关键字。在SQL中,最常用的关键字就是SELECT,因为在大多数的操作中都要先对表中的数据进行检索。
在使用SELECT的时候,要确定两个信息,一是选择什么,二是从哪里选择,也就是选则范围
首先,我们对下面的这个数据库进行操作
然后检索其中的一行
SELECT customer_id FROM customer
这样就得到到了只含有这一列数据的表
SELECT customer_id,first_name,last_name FROM customer
检索结果
在这里理论上可以将所有列的名字都放进检索条件中,但是实际开发中不会这么做,太麻烦了好吧。所以有一个通配符的概念——‘*’,就是这个星号。将他设置为检索条件,就不用输入所有的列了。
SELECT * FROM sql_store.customers
这样就将所有的列都检索出来了。
在实际的问题中,如一个进货清单,有很多行的某一个值可能是相同的,比如很多货物的供货商这一列,可能都是一样的。
如果我们准备统计所有出现的供货商,也能使用SELECT语句。
但是默认的SELECT会将所有匹配的全部输出,即使是重复的数据也会输出。这时候,我们就只需使用DISTINCT关键字就行
这是DISTINCT的中文意思,故名思意,使用这个关键字就可以只返回不同的值
SELECT distinct * FROM store.newtable
这就是使用DISTINCT后的输出结果,很明显没有输出重复数据
使用SELECT返回行的时候,能够根据需要返回具体数量的行,可使用LIMIT限制子句返回指定数量的行
SELECT customer_id,first_name FROM store.customers limit 6
可见,只返回了6条结果
前面我们虽然将返回了指定的列,但是在实际问题中,往往是要按照一定的顺序进行返回的。比如学生的成绩数据,往往是要返回一个总成绩排名的表的。所以,MySQL也提供了用来进行排序的语句。
ORDER BY,即为用来排序的子句,他后面跟随的是排序的条件。
下面请实例,这张customer的表默认是按照cuxtomer_id进行降序进行升序排列的
我们将其改为按照points进行升序排序就能像下面这样写
SELECT * FROM customers order by points;
查询结果(已经按照points升序排序了)
默认的ORDER BY子句是按照升序进行排列的,我们可以认为的将其改为降序排列,为了进行降序排列,必须使用DESC关键字
这是按照customer_id升序排列的表,
我们将其改为按照customer_id降序排列,可以像下面这样写
SELECT * FROM store.customers order by customer_id desc;
查询结果(实现了降序排列)
要注意的几个重点
4.3 一个联合使用子句的小技巧
使用ORDER BY和LIMIT子句就能直接查询出一个列中的最大值后最小值
代码如下
SELECT * FROM store.customers order by customer_id desc limit 1;
查询结果
字句位置的规范,使用ORDER BY的时候,他必须要置于FROM之后,使用LINIT子句时,必须置于ORDER BY之后
在进行数据查询的时候,有时尽管将数据进行了前面学习到的排序,但是数据的可读性还并不是最完美的。往往在庞大的项目中,一个表单的数据成千上万的,即使顺序是正常的,也并不即使得到我们需要的数据。
这时候就要进行数据的过滤,将查询的范围限制在我们需要的范围内,MySQL就提供了这样的进行数据过滤的子句——WHERE子句。
故名思意,WHERE就是在哪里的意思,使用这个子句的意思就是告诉程序你要查询的数据在哪里,方便进行过滤。
实例:
SELECT * FROM store.customers where customer_id=10
这样就通过WHERE子句找到了需要的数据
当同时使用ORDER BY和WHERE子句的时候,应该将ORDER BY位于WHERE之后
操作符 | 说明 |
---|---|
= | 等于 |
<> | 不等于 |
!= | 不等于 |
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
BETWEEN | 在两个值之间 |
下面是几个使用不同操作符实现的检索的实例演示
SELECT * FROM store.customers where first_name='Justin'
查询结果
2. 不匹配检查
将检索条件中的检索条件中的等于改为不等于就能够实现不匹配的检查,例如,检查customer_id不等于12的值
select * from sql_store.customers where customer_id <> 12
这样就检索到了customer_id不等于12的所有的数据
3. 范围值检查
进行范围值检索的时候,与其他的检索略有不同,要使用BETWEEN操作符。该语法使用的时候,要传入零个数值,因为是要确定一个范围的。
下面请看演示代码和运行结果,假设我们要检索customer_id在5到11之间的所有数据
SELECT * FROM customers WHERE customer_id BETWEEN 5 AND 11
运行结果
使用BETWEEN进行范围检索的时候,还要使用AND,这想想也知道吧,没啥好解释的了。。。。。。。
我们先看下面的表
可以看见,customer_id等于11这一行的用户是没有phone的,在实际项目中也出现这种情况的话,就需要检索到这一个数据栏,然后再添加数据
先演示如何查询空数据
SELECT * FROM customers WHERE birth_day is NULL
检索结果
当进行一些比较复杂的检索的时候,往往仅用一个子句是无法解决的。
所以组合一些常见的子句就能进行一些更高级和有效的检索
在进行数据检索的时候,往往只通过一列进行数据过滤是并不精确的。因为有时候限制条件有多个,可能涉及到两个或两个以上的列。
实例
比如对这个产品清单,我们要检索quantity_in_stock大于30且unit_price小于4的产品
SELECT * FROM sql_store.products WHERE quantity_in_stock>30 AND unit_price<4
运行结果
OR操作符与AND不一样,OR相当于逻辑中的或,只要满足其中一个条件就加入到检索结果中。也就是说,他指示MySQL检索匹配任一条件的行
还是针对上一个实例中的表,我们将检索条件中的AND换为OR
实例
SELECT * FROM sql_store.products where quantity_in_stock>30 OR unit_price<4
检索结果
可见,只要是满足其中一个条件的行都被放入了检索结果中
MySQL允许使用多个AND或OR进行高级检索,但是在实际问题中,当使用多个AND和OR的时候,检索的次序就成了问题,也就是说,MySQL究竟该先执行哪个AND或是OR操作符
所以在实际问题中可以使用括号来进行计算次序的调整。
比如下面这个例子,我们在下面的表中要检索一个居住在CA洲GA洲或并且用户id大于8的用户
代码示例
SELECT * FROM sql_store.customers where customer_id>8 and (state='CA' or state='GA')
检索结果
读者可以找更多的例子去练习这样的操作
IN操作符之所以要单独拿出来将,就是因为IN操作符非常的强大,也是使用比较多的一个操作符。IN操作符用来指定条件范围,范围中的每个条件都能进行匹配
请看下面的例子
SELECT * FROM sql_store.customers where customer_id in(5,6,12,7)
检索结果
值得注意的是,IN操作符括号中的条件并不是用来检索满足某一范围的,其中传递值的就是你要查询的值,并且,括号中的数据类型,数据数量都是不确定的,也就是所你可以将你需要的满足条件的检索值都放进去
从另一层面上来看,IN操作符和OR操作符的功能是比较类似的。到那时IN的优点相较于OR更加的突出
使用IN操作符的优点主要有以下几点:
NOT只有一个显而易见的作用,就是否定其后面的条件
比如下面的例子
SELECT * FROM sql_store.customers where customer_id not in(5,6,12,7)
检索结果
可见,NOT直接否定了IN的条件。NOT的主要优势是进行数据的排除,尤其在和IN结合使用的时候,能够快速的将一部分数据剔除。使得找出与条件不匹配的行非常简单
前面的检索方式都是针对已知值进行检索的。
但是在实际问题中,有时候并不能准确的记得要检索的值,尤其是进行文本检索的时候,我们可能只知道文本或字符串的开头或是结尾。这时候前面的方法就不适用了,就得使用通配符进行检索。
想要使用通配符就必须,在检索语句中就必须使用LIKE操作符。主要怎么使用还是看下面的演示比较直接易懂。
%是最常用的通配符,在搜索中,%表示任意字符出现任意次数。例如我们要寻找,first_name为A开头的人,就可以像下面这样写
SELECT * FROM sql_store.customers where first_name like 'A%'
检索结果
在这里就可以看出,%的作用就是匹配任意字符和任意出现次数。
SELECT * FROM sql_store.customers where first_name like '%n'
注意事项:
下划线通配符也是匹配任意字符,与%不同的是,一个_只能匹配一个字符。
示例:
SELECT * FROM sql_store.customers where first_name like 'A__n'
这里输了两个下划线,只是连起来了,看上去像一个
检索结果
记住:_永远只能匹配一个字符
首先声明:这一章非常非常非常重要!!!!!
先来介绍一些什么事正则表达式吧
在前面的检索中,使用的哪些方法,对于基本的过滤就足够了。但是随着过滤条件复杂度的增加,where子句本身的复杂程度也会逐渐增加。正则表达式的使用的目的就是降低where子句的复杂度。
正则表达式是用匹配文本的特殊的串(字符集合),在进行字符串或长串数字键过滤检索的时候非常的管用。
所有种类的程序设计语言,文本编辑器和操作系统都支持正则表达式。正则表达式由正则语言来建立。与其他所有语言一样,正则表达式有必须学习特殊的语法和指令。
这一块内容用下面的表进行演示:
先看下面的例子
SELECT * FROM store.customers where points REGEXP '.000'
检索结果
这里的REGEXP后面跟着的就是正则表达式,**其中的 **.表示的是匹配任意一个字符,因此1000,2000,3000都被返回。
有的人可能会觉得这一点使用LIKE也能实现,确实如此,甚至这个语句与LIKE写出来的语句看上去几乎差不多。但是,REGEXP与LIKE有着本质上的差别。
先看下面两条语句
SELECT * FROM store.customer
WHERE points LIKE ‘1000’这是用LIKE语句写出来的
再看用REGEXP写出来的SELECT * FROM store.customer
WNERE points REGEXP ‘1000’
看上去两条语句好像都是在检索points为1000的行,但是,实际运行会发现,第一条语句不返回数据,第二条语句返回一条数据。
这是因为,LIKE在匹配整个列的时候,如果匹配的文本在列值中出现,LIKE将不会找到他,同样也就不会返回这个行。但是REGEXP在列值内进行匹配,如果匹配的文本在列值中出现,REGEXP将找到他,同样也就会将这一行作为返回数据返回。
一个重点:
在MySQL中,正则表达式部分大小写。如果想要区分大小写就要使用BINARY关键字。只要在REGEXP后面加上BINARY关键字,就能区分大小写。
正则表达式中的OR使用 " | " 来表示,原理和作用还是和前面的OR关键字一样的,只是在正则表达式中换了个写法。
示例:
SELECT * FROM store.customers where points regexp '1000|2000'
REGEXP在这一块比较方便的一点就是在使用两个以上的OR条件的时候,书写起来比较方便,只要添加一个“|”号和一个条件就行。
这是一个很神奇的功能,只需要一个 “[]”(方括号) 就能代替OR的工作,甚至连OR运算符都不用写,还是先看示例吧
SELECT customer_id,first_name,last_name,points FROM sql_store.customers where points regexp '[123456]99'
检索结果
从结果就能看出来,这次的检索条件是将points为“*99”的数据检索出来。正如代码和运行结果图所见,[ ]是另一种形式的OR语句。正则表达式 [123456]99 实际上是 [1|2|3|4|5|6]99 的缩写
将上面的**[123456]简化一下,简化为[1-6]**就能进行范围匹配。其中," - "用来定义一个范围。所以上面两个表达方式的作用是相同的。
示例:
SELECT * FROM sql_store.customers where points regexp '[1-6]99'
检索结果
可以看出,检索的结果和7.3中的例子是一样的,这样写的优点是在进行范围匹配的时候,书写更加的简洁和方便。
除了常见的子母和数字在,在数据中还可能会出现一些其他的特殊字符。比如创建的括号、标点之类的字符。
比如我们要找包含name中含 " . "字符的值,应该怎样搜索。第一反应可能是下面的写法
SELECT * FROM store.products where name regexp '.'
检索结果
显然这样是不对的,因为前面说过 "."是匹配任意字符的,上面的代码会将所有的行都检索出来。
所以在这里要用到前导字符,就是 “\\”
例如 "\\-“就是查找-,”\\."就是查找.
下面我们用这种方法重新进行上面的检索
SELECT * FROM store.products where name regexp '\\.'
检索结果
这种处理就是所谓的转义(escaping),正则表达式中含有特殊意义的所有字符必须使用这种方式进行转义。
下面是引用元字符(常见字母,但是有特殊含义)的转义表
元字符 | 说明 |
---|---|
\\f | 换页 |
\\n | 换行 |
\\r | 回车 |
\\t | 制表 |
\\v | 纵向制表 |
保存下面这个表就行了,没啥花里胡哨的了
类 | 说明 |
---|---|
[:alnum:] | 任意字母和数字(同[a-zA-Z0-9]) |
[:alpha:] | 任意字符(同[a-zA-Z]) |
[:blank:] | 空格和制表(同[\t]) |
[:cntrl:] | ASCII控制字符(ASCII 0到31和127) |
[:digit:] | 任意数字(同[0-9]) |
[:graph:] | 纵向制表 |
[:lower:] | 任意小写字母(同[a-z]) |
[:print:] | 任意可打印字符 |
[:punct:] | 既不在[:alnum:]又不在[:cntrl:]中的任意字符 |
[:space:] | 包括空格在内的任意空白字符(同[\f\n\r\t\v]) |
[:upper:] | 任意大写字母(同[A-Z]) |
[:xdigit:] | 任意十六进制数字(同[a-fA-F0-9]) |
上面的是所有的用于匹配字符类的正则表达式,有的比较常用,有的不是很常用。
下面是一个演示,看一看是如何使用的
SELECT * FROM store.customers where address regexp '[[:digit:]]'
执行结果
这样就检索到了所有的address含数字的行
在实际项目中,存储在数据库表中的数据一般不是应用程序所需要的格式。
比如下面几个例子:
这里列举的是几种比较常见的问题,这时候就需要使用计算字段,将某些列或者是表进行重新的格式化。
下面的会使用到字段这个名词,实际上字段与列的意思基本上是相同的,大多时候是可以互换使用的。只不过数据库中的列一般称为列,二字段常用在计算字段的连接上。
这里有点理论了,后面直接看例子就行了
拼接:即将两个列拼接起来,组成一个新的列。
在MySQL的SELECT语句中,可以使用Concat() 函数来拼接两个列。
没错,MySQL中也有函数,后面会详细讲到
举一个简单的例子吧,把老外的名字的first_name和last_name拼接起来,成为一个full_name列
select concat(first_name,' ' ,last_name) from store.customers
执行结果
函数解析:
Concat()是拼接函数,用来将多个串拼接成一个长串。
函数传递的参数是一个或多个指定的串,不同的串之间用逗号隔开
进行其他的串拼接的操作的时候,照着我上面哪个写就行
看上面那个例子的运行结果图会发现,进行拼接的时候,不仅将数据进行了拼接,还将列名进习惯了拼接。这是很丑的,所以SQL支持使用别名。
别名用AS赋值,请看示例
select concat(first_name,' ' ,last_name) as full_name from store.customers
执行结果
使用方法如此简单。。。。。。。
算数计算很好理解,就是通过对存储数字的不同的列进行加减乘除或者是组合运算,得到一个新的结果列的处理过程
直接看示例,很简单
SELECT quantity_in_stock, unit_price, quantity_in_stock*unit_price as total FROM store.products
执行结果
至于运算符,就是最常见的加减乘除的运算符
同C语言,C++语言一样,SQL中也有很多已经封装好的函数,方便开发人员在进行数据处理和转化时候,能够更加的高效。
下面会列举一些常见的文本处理函数,只是为了介绍一下功能和函数调用的方法。其他的开发中用到的函数可以去官网查看使用文档。
函数 | 说明 |
---|---|
Left() | 返回串左边的字符 |
Length() | 返回串的长度 |
Locate() | 返回串的一个子串 |
Lower() | 将串转换为小写 |
LTrim() | 去掉串左边的空格 |
Right() | 返回串右边的字符 |
RTrim() | 去掉串右边的空格 |
Soundex() | 返回串的SOUNEEX值 |
SubString() | 返回子串的字符 |
Upper() | 将串转换为大写 |
SELECT first_name, upper(last_name) as uppername FROM store.customers
执行结果
在后面讲到时间和日期的时候会直到,日期和时间采用相应的数据类型和特殊的格式存储,以便于快速进行排序或过滤,并且能节省物理存储空间
常见的日期和时间处理函数
时间的查询也能使用范围查询,而且很简单
示例1:
SELECT * FROM store.customers where birth_date between '1985-00-00' and '1990-00-00'
执行结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sw0MWLnL-1635499523398)(https://img-blog.csdfnimg.cn/20c42e164cfc4e4486047b5a5504ed85.png)]
就是最常见的使用方法
示例2:
通过日期中的年进行范围匹配
SELECT * FROM store.customers where year(birth_date) between '1985' and '1990'
执行结果
数值处理函数的使用方法是统一的,都是传递参数,然后函数的返回值就是所求的结果,下面是一些常用的数值处理函数
函数的使用就不举例子了,非常的简单。
有时候要进行数据的汇总但是不需要将数据实际检索出来,就可以使用聚集函数进行处理
具体的使用场景有以下几种:
以下的函数又叫分组函数,不能使用在WHERE后面
SQL中的聚集函数 | 说明 |
---|---|
AVG() | 返回某列的 |
COUNT() | 返回某列的行数 |
MAX() | 返回某列的最大值 |
MIN() | 返回某列的最小值 |
SUM() | 返回某列之和 |
下面是各函数使用实例:
SELECT avg(unit_price) as avg_price FROM store.products
执行结果
SELECT count(unit_price) as number_of_row FROM store.products
SELECT max(unit_price) as max_price FROM store.products
执行结果
SELECT sum(unit_price) as sum FROM store.products
在实际的表单中,同一列中常常会出现多个相同的值。有时候不需要对相同的数据进行重复的统计,这时候既要用到DISTINCT参数进行不同值的聚集。
DISTINCT的使用非常简单,只要在需要进行不同值聚集的列名前面加上DISTINCT即可。
注意:如果指定列名,DISTINCT只能用于COUNT(),DISTINCT不能用于COUNT(*),因此不允许使用COUNT(DISTINCT),否则会产生错误。
目前的所有的聚集函数都只包含的那个函数,实际上可以根据需要包含多个聚集函数
比如下面的例子:
SELECT count(InvoiceId) as total_number, avg(InvoiceLineId) as avg_InvoiceLineId, min(UnitPrice) as avg_minprice FROM InvoiceLine
执行结果
在进行组合聚集的时候要记得给新的列取别名
为什么要进行数据分组?
在实际问题中,比如一张进货清单或者是一个服务器上的不同用户的信息。我们肯定不能将所有的数据放在一起,肯定是要将进货清单按照供货商进行分组,将服务器上的数据按照用户进行分组,等等。
这时候,分组就显得尤为重要。
直接看例子:
就比如这个表,我们将其中的数据按照order_id进行分组,就是下面这样的
SELECT order_id, sum(unit_price) as total_price FROM sql_store.order_items group by order_id
执行结果
其实上面的代码执行了两个步骤,一个是将行按照order_id进行分组,另一个就是将分组后的数据的unit_price进行求和
从上面就可以看出,要进行分组,使用的就是GROUP BY子句。
除了能够使用GROUP BY进行分组外,MYSQL还允许过滤分组,即规定包括哪些分组,排除哪些分类。但是过滤分组是基于已经分组的基础上的,所以才要先学习BROUP BY。
在进行过滤分组的时候使用的就已经不是WHERE子句了,因为WHERE子句默认的是对行进行过滤,而不能对组进行过滤。
在这里要使用新的子句——HAVING子句。HAVING的使用
与WHERE是比较类似的。
先看一个使用示例:
这是将要进行操作的表,我们将其按照order_id进行分组,然后过滤出组中总数居大于等于二的order_id
下面看代码和执行结果:
select order_id,count(*) as orders from sql_store.order_items group by order_id having count(*)>=2
执行结果
可以看出用HAVING子句很好地完成了将组过滤的工作。
HAVING:HAVING支持所有的WHERE子句支持的操作。这其中包括通配符条件和正则表达式的使用,句法是相同的,只是关键字略有v不同。
HVING和WHERE的区别:WHERE在数据分组前进行过滤,HAVING在数据分组后进行过滤,WHERE排除的行就不会在包含在分组中了。
WHERE和HAVING组合情况
检索目标:先将刚才的表中unit_price小于3的行过滤,再将表进行分组,并且将数据量小于等于2的组别过滤
看代码:
select order_id,count(*) as orders from sql_store.order_items where unit_price>=3 group by order_id having count(*)>=2
执行结果
使用组合子句的时候要注意的就是子句的出现的顺序。
注意:WHERE后面不能使用分组函数
截至分组的学习后,已经可以确定一个正常的DQL语句的书写顺序了,一般都是
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY
在同一个查询语句中,上面的子句并不一定都会出现,但是只要有其中的两个以上出现,及一定要遵循上述的顺序。
任何SQL语句都是查询语句,创建子查询就是在一个查询中嵌套其他的查询语句。
该案例中以及该博客中使用的表都是关系表,即每个表中都存在一定的关系联结。比如对于下面这个数据库中的所有的表,就存在下面的关系。
这是例子中的数据库以及其中包含的所有的表:
这就是所有表之间的实体关系:
这个图看不明白也没什么,因为后面会讲到
下面展示如何使用子查询将两条查询合并为一条查询:
查询一:检索order在4和8之间的行
SELECT order_id FROM store.order_items where order_id between 4 and 8
查询二:检索product_id为3或5的行
SELECT product_id FROM store.order_items where product_id in(3,5)
下面将第一个查询作为子查询组合两个查询
SELECT product_id FROM store.order_items where product_id in( select order_id from store.order_items where order_id between 4 and 8 )
执行结果:
原理:在上面的SELECT语句中,子查询总是从内向外处理。也就是说,先处理子查询的语句,也就是上面的刚开始的第一条查询。
在上面的查询中,处理过程是这样的
先执行子查询,然后将查询结果作为IN操作符的参数,返回给外部查询的语句。
下面是根据上面的原理写的一个三层的查询
SELECT product_id FROM store.order_items where product_id in( select order_id from store.order_items where order_id in ( select quantity from store.order_items where quantity between 3 and 7 ) )
使用子查询的另一个方法是创建计算字段。
查询目标:
一:从orders表中检索客户列表
二:对于检索出的数据,统计他在order_item表中的订单数目
在实际的数据存储的过程中,往往并不会将一堆数据直接放进一个表中,因为这样往往会造成数据的冗余,因为往往很多个数据的某一属性都是相同的,如果将其放在一个表中过于浪费存储空间。
既然数据存储在多个表中,进行检索的时候,多表联查也就是必不可少给的,连接查询就是解决多表联查的问题
一、等值连接
等值连接是将两个表中的选定的字段,拼接成一个新的查询结果,由于不在通过一个表中,这就要求两个表必须拥有至少一个相同的字段。
下面直接看对于客户信息customer和订单orders两张表的连接查询,两张表的相同字段是order_id.
SELECT first_name,last_name,order_Id from customers -- 第一个表 inner join orders -- 第二个表 on -- on后接连接条件 customers.customer_id=orders.customer_id
执行结果
inner join 前后分别是要进行连接查询的两个表,on后连接查询条件。这里的等值就是指on后面的条件是等值条件
二、非等值连接
非等值连接的语法和等值连接是一样的,因为都是来内连接查询。只不过,非等值连接on后面的条件语句,是非等值语句,就不做演示了。
三、自连接
自连接是将一张表看成两张表,比如一张公司的员工名单表,其中既包含员工又包含管理员,而管理员也属于员工,并且有更高级的管理员。当要检索出一张员工的管理者的表的时候,就可以使用自连接
使用自连接的关键就是将一张表视为两张表,这就需要给表取别名。在前面已经讲过给字段取别名了,要使用AS关键字。在对表名取别名的时候,不需要使用AS关键字,直接在表名后面加上别名即可。
这时候想要将一张表视为两张表就只需要给表取两个别名就可以,然后利用这两个表进行上面的等值连接即可。
比如下面的员工表,其中估计包含员工包含各个员工的直接领导
就可以使用内连接查询出各个员工的直接领导人是谁
SELECT CONCAT( a.first_name, ' ' ) AS '员工', CONCAT( b.first_name, ' ' ) AS '老板' FROM employees a INNER JOIN employees b ON a.boss = b.employee_id
运行结果
先看以下内连接和外连接的区别
内连接:假设A和B两张表进行连接,使用内连接的话,凡是A和B能够匹配上的记录都会被查询出来。两张表是平等的,没有主副之分
外连接:假设A和B两张表进行连接,使用外连接的话,AB两张表中有一张是主表,一张是副表,进行查询的时候主要查询主表中的数据,顺带查询副表中的数据,当副表中的数据没有和主表中的数据匹配上,副表就会自动模拟出NULL与主表匹配
与内连接使用的INNER JOIN不同的是,外连接使用的是LEFT JOIN
下面是另一张员工信息表,利用外连接进行查询,即使没有老板的员工,也会自动进行NULL匹配
SELECT CONCAT( a.first_name, ' ' ) AS '员工', CONCAT( b.first_name, ' ' ) AS '老板' FROM employees a left JOIN employees b ON a.boss = b.employee_id
上述代码中,a表就是主表,b表就是副表,所以一定会将主表中的数据全部查询出来,至于副表中不能与之进行匹配的就会自动使用NULL进行匹配
运行结果