MySQL--最基礎的MySQL學習(適合初學者)

查询数据

学习目标 | Objective

数据库管理系统的一个重要功能就是数据查询,数据查询不应只是简单返回数据库中存储的数据,还应该根据需要对数据进行筛选以及确定数据以什么样的格式显示。MySQL提供了功能强大、灵活的语句来实现这些操作,本章将介绍如何使用SELECT语句查询数据表中的一列或多列数据、使用集合函数显示查询结果、连接查询、子查询以及使用正则表达式进行查询等。

内容导航 | Navigation

● 了解基本查询语句
● 掌握表单查询的方法
● 掌握如何使用几何函数查询
● 掌握连接查询的方法
● 掌握如何使用子查询
● 熟悉合并查询结果
● 熟悉如何为表和字段取别名
● 掌握如何使用正则表达式查询
● 掌握综合案例中数据表的查询操作技巧和方法

一、基本查询语句

1.1 基礎

MySQL从数据表中查询数据的基本语句为SELECT语句。
SELECT语句的基本格式是:
在这里插入图片描述
其中,各条子句的含义如下:
● {* | <字段列表>}包含星号通配符和字段列表,表示查询的字段。其中,字段列表至少包含一个字段名称,如果要查询多个字段,多个字段之间用逗号隔开,最后一个字段后不加逗号。
● FROM <表1>,<表2>…,表1和表2表示查询数据的来源,可以是单个或者多个。
● WHERE子句是可选项,如果选择该项,将限定查询行必须满足的查询条件。● GROUP BY <字段>,该子句告诉MySQL如何显示查询出来的数据,并按照指定的字段分组。
● [ORDER BY <字段>],该子句告诉MySQL按什么样的顺序显示查询出来的数据,可以进行的排序有升序(ASC)、降序(DESC)。
● [LIMIT [< offset>,] < row count>],该子句告诉MySQL每次显示查询出来的数据条数。

下面以一个例子说明如何使用SELECT从单个表中获取数据。首先定义数据表,输入语句如下:
创建表

CREATE TABLE fruits (
  f_id CHAR(10) NOT NULL,
  s_id INT(11) NOT NULL,
  f_name CHAR(255) NOT NULL,
  f_price DECIMAL(8,2) NOT NULL,
  PRIMARY KEY (f_id)
) 

插入数据

INSERT INTO fruits (f_id,s_id,f_name,f_price)
VALUES ('a1',101,'apple',5.2),
('b1',101,'blackberry',10.2),
('bs1',102,'orange',8.2),
('bs2',108,'banana',2.2),
('t1',103,'cocount',3.2),
('t2',102,'berry',5.2),
('o2',103,'xxxx',1.2),
('c0',104,'lemn',5.2),
('a2',102,'mango',3.2),
('l2',101,'jorya',2.2),
('b2',104,'Liu',3.2),
('m1',103,'double',6.2),
('m2',105,'think',9.2),
('m4',102,'about',7.2),
('t5',107,'pric',8.2),
('m3',105,'bababa',6.2),
('b9',108,'abble',9.2);

使用SELECT语句查询f_id,f_name字段的数据:

SELECT f_id ,f_name FROM fruits

在这里插入图片描述
该语句的执行过程是,SELECT语句决定了要查询的列值,在这里查询f_id和f_name两个字段的值,FROM子句指定了数据的来源,这里指定数据表fruits,因此返回结果为fruits表中f_id和f_name两个字段下所有的数据。其显示顺序为添加到表中的顺序。

1.2 单表查询

单表查询是指从一张表数据中查询所需的数据。本节将介绍单表查询中的各种基本的查询方式,主要有查询所有字段、查询指定字段、查询指定记录、查询空值、多条件的查询、对查询结果进行排序等。

1.2.1 查询所有字段

1.在SELECT语句中使用星号()通配符查询所有字段
SELECT查询记录最简单的形式是从一个表中检索所有记录,实现的方法是使用星号(
)通配符指定查找所有列的名称。语法格式如下:

select * from 表名

select * from fruits

可以看到,使用星号(*)通配符时,将返回所有列,列按照定义表时候的顺序显示。

2.在SELECT语句中指定所有字段
下面介绍另外一种查询所有字段值的方法。根据前面SELECT语句的格式,SELECT关键字后面的字段名为将要查找的数据,因此可以将表中所有字段的名称跟在SELECT子句后面,如果忘记了字段名称,可以使用DESC命令查看表的结构。有时候,表中的字段可能比较多,不一定能记得所有字段的名称,因此该方法会很不方便,不建议使用。例如,查询fruits表中的所有数据,SQL语句也可以书写如下
SELECT F_ID,s_id,f_name,f_price FROM fruits;

查询结果与【例1.1】相同。

提示

一般情况下,除非需要使用表中所有的字段数据,最好不要使用通配符‘*’。使用通配符虽然可以节省输入查询语句的时间,但是获取不需要的列数据通常会降低查询和所使用的应用程序的效率。通配符的优势是,当不知道所需要的列的名称时,可以通过它获取它们。

1.2.2 查询指定字段

1.查询单个字段查询表中的某一个字段,语法格式为:

SELECT 列名  FROM 表名;

【例1.2】查询fruits表中f_name列所有的水果名称,SQL语句如下:

SELECT f_name  FROM fruits;

该语句使用SELECT声明从fruits表中获取名称为f_name字段下的所有水果名称,指定字段的名称紧跟在SELECT关键字之后。

输出结果显示了fruits表中f_name字段下的所有数据。
2.查询多个字段
使用SELECT声明,可以获取多个字段下的数据,只需要在关键字SELECT后面指定要查找的字段的名称,不同字段名称之间用逗号(,)分隔开,最后一个字段后面不需要加逗号,语法格式如下:

SELECT 字段名1,字段名2,,,,字段名N FROM 表名;

【例1.3】从fruits表中获取f_name和f_price两列,SQL语句如下:

SELECT f_name,f_price FROM fruits;

该语句使用SELECT声明从fruits表中获取名称为f_name和f_price两个字段下的所有水果名称和价格,两个字段之间用逗号分隔开,查询结果如下:

表三

输出结果显示了fruits表中f_name和f_price两个字段下的所有数据。

提示

MySQL中的SQL语句是不区分大小写的,因此SELECT和select的作用是相同的,但是,许多开发人员习惯将关键字大写、数据列和表名小写,读者也应该养成一个良好的编程习惯,这样写出来的代码更容易阅读和维护。

1.2.3 查询指定记录

数据库中包含大量的数据,根据特殊要求,可能只需要查询表中的指定数据,即对数据进行过滤。在SELECT语句中,通过WHERE子句可以对数据进行过滤,语法格式为:

SELECT 字段名1,字段名2,,,,字段名N 
FROM 表名
WHERE 查詢條件;

在WHERE子句中,MySQL提供了一系列的条件判断符,查询结果如表4所示。

表四

【例1.4】查询价格为10.2元的水果的名称,SQL语句如下:

SELECT f_name,f_price  FROM fruits WHERE f_price=10.2;

该语句使用SELECT声明从fruits表中获取价格等于10.2的水果的数据,从查询结果可以看到,价格是10.2的水果的名称是blackberry,其他的均不满足查询条件,查询结果如下:

表五

本例采用了简单的相等过滤,查询一个指定列f_price具有值10.20。相等还可以用来比较字符串,下面给出一个例子:
【例1.5】查找名称为“apple”的水果的价格,SQL语句如下:

SELECT f_name,f_price  FROM fruits WHERE f_name=’apple’;

该语句使用SELECT声明从fruits表中获取名称为“apple”的水果的价格,从查询结果可以看到只有名称为“apple”行被返回,其他的均不满足查询条件。

【例1.6】查询价格小于10的水果的名称,SQL语句如下:

SELECT f_name,f_price  FROM fruits WHERE  f_price<10;

该语句使用SELECT声明从fruits表中获取价格低于10的水果名称,即f_price小于10的水果信息被返回,查询结果如下:

可以看到查询结果中所有记录的f_price字段的值均小于10.00元,而大于等于10.00元的记录没有被返回。

1.2.4 带IN关键字的查询

IN操作符用来查询满足指定范围内的条件的记录,使用IN操作符,将所有检索条件用括号括起来,检索条件之间用逗号分隔开,只要满足条件范围内的一个值即为匹配项。
【例1.7】查询s_id为101和102的记录,SQL语句如下:

Select s_is,f_name,f_price from fruits where s_id in(101,102) order by f_name;

查询结果如下:

相反,可以使用关键字NOT来检索不在条件范围内的记录。
【例1.8】查询所有s_id不等于101也不等于102的记录,SQL语句如下:

Select s_is,f_name,f_price from fruits where s_id not in(101,102) order by f_name;

查询结果如下:

可以看到,该语句在IN关键字前面加上了NOT关键字,这使得查询的结果与前面一个的结果正好相反,前面检索了s_id等于101和102的记录,而这里所要求查询的记录中s_id字段值不等于这两个值中的任何一个。

1.2.5 带BETWEEN AND的范围查询

BETWEEN AND用来查询某个范围内的值,该操作符需要两个参数,即范围的开始值和结束值,如果字段值满足指定的范围查询条件,则这些记录被返回。
【例1.9】查询价格在2.00元到10.20元之间的水果名称和价格,SQL语句如下:

Select f_name,f_price from fruits where f_price Between 2.00 AND 10.20;

可以看到,返回结果包含了价格从2.00元到10.20元之间的字段值,并且端点值10.20也包括在返回结果中,即BETWEEN匹配范围中的所有值,包括开始值和结束值。
BETWEEN AND操作符前可以加关键字NOT,表示指定范围之外的值,如果字段值不满足指定的范围内的值,则这些记录被返回。

【例1.10】查询价格在2.00元到10.20元之外的水果名称和价格,SQL语句如下:

Select f_name,f_price from fruits where f_price  NOT Between 2.00 AND 10.20;

由结果可以看到,返回的记录只有f_price字段大于10.20的,其实,f_price字段小于2.00的记录也满足查询条件。因此,如果表中有f_price字段小于2.00的记录,也应当作为查询结果。

1.2.6 带LIKE的字符匹配查询

在前面的检索操作中讲述了如何查询多个字段的记录,如何进行比较查询或者是查询一个条件范围内的记录,如果要查找所有包含字符“ge”的水果名称,该如何查找呢?简单的比较操作在这里已经行不通了,需要使用通配符进行匹配查找,通过创建查找模式对表中的数据进行比较。执行这个任务的关键字是LIKE。
通配符是一种在SQL的WHERE条件子句中拥有特殊意思的字符。SQL语句中支持多种通配符,可以和LIKE一起使用的通配符有‘%’和‘_’。
1.百分号通配符‘%’,匹配任意长度的字符,甚至包括零字符
【例1.11】查找所有以‘b’字母开头的水果,SQL语句如下:

Select f_id,f_name from fruits where f_name Like ‘%b’;

该语句查询的结果返回所有以‘b’开头的水果的id和name,‘%’告诉MySQL,返回所有以字母‘b’开头的记录,不管‘b’后面有多少个字符。在搜索匹配时通配符‘%’可以放在不同位置,如【例1.12】所示。
【例1.12】在fruits表中,查询f_name中包含字母‘g’的记录,SQL语句如下:

Select f_id,f_name from fruits where f_name Like ‘%g’;

该语句查询字符串中包含字母‘g’的水果名称,只要名字中有字符‘g’,不管前面或后面有多少个字符,都满足查询的条件。

【例1.13】查询以‘b’开头并以‘y’结尾的水果的名称,SQL语句如下:

Select f_name from fruits where f_name Like ‘b%y’;

通过以上查询结果可以看到,‘%’用于匹配在指定位置的任意数目的字符。

2.下划线通配符‘’,一次只能匹配任意一个字符
另一个非常有用的通配符是下划线通配符‘ _ ’。该通配符的用法和‘%’相同,区别是‘%’可以匹配多个字符,而‘ _ ’只能匹配任意单个字符。如果要匹配多个字符,则需要使用相同个数的‘
’。
【例1.14】在fruits表中,查询以字母‘y’结尾,且‘y’前面只有4个字母的记录,SQL语句如下:

Select f_id,f_name from fruits where f_name like ‘_ _ _ _y’;

从结果可以看到,以‘y’结尾且前面只有4个字母的记录只有一条。其他记录的f_name字段也有以‘y’结尾的,但其总的字符串长度不为5,因此不在返回结果中。

1.2.7 查询空值

数据表创建的时候,设计者可以指定某列中是否包含空值(NULL)。空值不同于0,也不同于空字符串。空值一般表示数据未知、不适用或将在以后添加数据。在SELECT语句中使用ISNULL子句,可以查询某字段内容为空记录。

下面在数据库中创建数据表customers,该表中包含了本章中需要用到的数据。

CREATE TABLE `customers` (
  `c_id` int(11) NOT NULL AUTO_INCREMENT,
  `c_name` char(50) NOT NULL,
  `c_address` char(50) DEFAULT NULL,
  `c_city` char(50) DEFAULT NULL,
  `c_zip` char(10) DEFAULT NULL,
  `c_contact` char(50) DEFAULT NULL,
  `c_email` char(255) DEFAULT NULL,
  PRIMARY KEY (`c_id`)
)

为了演示,需要插入数据,执行以下语句:

insert into `customers` (`c_id`, `c_name`, `c_address`, `c_city`, `c_zip`, `c_contact`, `c_email`) values('10001','redHook','200 street','Tianjian','300000','Li ming','LMING@163.com');
insert into `customers` (`c_id`, `c_name`, `c_address`, `c_city`, `c_zip`, `c_contact`, `c_email`) values('10002','Stars','333 fromage lane ','Dalian','212311','zhaobo','aJoAo@qq.com');
insert into `customers` (`c_id`, `c_name`, `c_address`, `c_city`, `c_zip`, `c_contact`, `c_email`) values('10003','neyeya','1 sundao','qindao','21121','luocong','null');

【例1.15】查询customers表中c_email为空的记录的c_id、c_name和c_email字段值,SQL语句如下:

Select c_id ,c_name,c_email from customers where c_email is null;

可以看到,显示customers表中字段c_email的值为NULL的记录,满足查询条件。与IS NULL相反的是NOT NULL,该关键字查找字段不为空的记录。
【例1.16】查询customers表中c_email不为空的记录的c_id、c_name和c_email字段值,SQL语句如下:

Select c_id ,c_name,c_email from customers where c_email not is null;

可以看到,查询出来的记录的c_email字段都不为空值。

1.2.8 带AND的多条件查询

使用SELECT查询时,可以增加查询的限制条件,这样可以使查询的结果更加精确。MySQL在WHERE子句中使用AND操作符限定只有满足所有查询条件的记录才会被返回。可以使用AND连接两个甚至多个查询条件,多个条件表达式之间用AND分开。
【例1.17】在fruits表中查询s_id = 101并且f_price大于等于5的水果id、价格和名称,SQL语句如下:

Select f_id,f_price,f_name from fruits where s_id=101’ and f_price>=5;

前面的语句检索了s_id=101的水果供应商所有价格大于等于5元的水果名称和价格。WHERE子句中的条件分为两部分,AND关键字指示MySQL返回所有同时满足两个条件的行。id=101的水果供应商提供的水果如果价格小于5,或者是id不等于‘101’的水果供应商里的水果(不管其价格为多少),均不是要查询的结果。

提示

上述例子的WHERE子句中只包含了一个AND语句,把两个过滤条件组合在一起。实际上可以添加多个AND过滤条件,增加条件的同时增加一个AND关键字。
【例1.18】在fruits表中查询s_id = 101或者102,并且f_price大于等于5、f_name='apple’的水果价格和名称,SQL语句如下:

Select f_id,f_price,f_name from fruits where s_id in (101,102) and f_price>=5 and f_name=’apple’;

1.2.9 带OR的多条件查询

与AND相反,在WHERE声明中使用OR操作符,表示只需要满足其中一个条件的记录即可返回。OR也可以连接两个甚至多个查询条件,多个条件表达式之间用OR分开。
【例1.19】查询s_id=101或者s_id=102的水果供应商的f_price和f_name,SQL语句如下:

Select s_id ,f_name,f_price from fruits where s_id=101 or s_id=102;

结果显示了s_id=101和s_id=102的水果供应商的水果名称和价格,OR操作符告诉MySQL检索的时候只需要满足其中的一个条件,不需要全部都满足。如果这里使用AND的话,将检索不到符合条件的数据。
在这里,也可以使用IN操作符实现与OR相同的功能,下面的例子可进行说明。
【例1.20】查询s_id=101或者s_id=102的水果供应商的f_price和f_name,SQL语句如下:

Select s_id,f_name ,f_price from fruits where s_id in(101,102)

在这里可以看到,OR操作符和IN操作符使用后的结果是一样的,它们可以实现相同的功能,但是使用IN操作符使得检索语句更加简洁明了,并且IN执行的速度要快于OR。更重要的是,使用IN操作符可以执行更加复杂的嵌套查询(后面章节将会讲述)。

提示

OR可以和AND一起使用,但是在使用时要注意两者的优先级,由于AND的优先级高于OR,因此先对AND两边的操作数进行操作,再与OR中的操作数结合。

1.2.10 查询结果不重复

从前面的例子可以看到,SELECT查询返回所有匹配的行。例如,查询fruits表中所有的s_id,其结果为:
可以看到查询结果返回了16条记录,其中有一些重复的s_id值。有时出于对数据分析的要求,需要消除重复的记录值,该如何操作呢?在SELECT语句中,可以使用DISTINCT关键字指示MySQL消除重复的记录值。语法格式为:
Select DISTINCT 字段名 FROM 表名;
【例1.21】查询fruits表中s_id字段的值,返回s_id字段值且不得重复,SQL语句如下:

Select distinct  s_id  from fruits;

可以看到,这次查询结果只返回了7条记录的s_id值,且不再有重复的值,SELECT DISTINCTs_id告诉MySQL只返回不同的s_id行。

1.2.11 对查询结果排序

从前面的查询结果,读者会发现有些字段的值是没有任何顺序的,MySQL可以通过在SELECT语句中使用ORDER BY子句对查询的结果进行排序。

1.单列排序

例如,查询f_name字段,查询结果如下:

Select f_name from fruits ;

可以看到,查询的数据并没有以一种特定的顺序显示,如果没有对它们进行排序,就将根据它们插入到数据表中的顺序来显示。下面使用ORDER BY子句对指定的列数据进行排序。
【例1.22】查询fruits表的f_name字段值,并对其进行排序,SQL语句如下:

Select f_name from fruits order by f_name;

该语句查询的结果和前面的语句相同,不同的是,通过指定ORDER BY子句,MySQL对查询的name列的数据按字母表的顺序进行了升序排列。

2.多列排序

有时,需要根据多列值进行排序。比如,如果要显示一个学生列表,可能会有多个学生的姓氏是相同的,因此还需要根据学生的名进行排序。对多列数据进行排序,要将需要排序的列之间用逗号隔开。
【例1.23】查询fruits表中的f_name和f_price字段,先按f_name排序,再按f_price排序,SQL语句如下:

Select f_name,f_price from fruits order by f_name,f_price;

提示

在对多列进行排序的时候,首先排序的第一列必须有相同的列值,才会对第二列进行排序。如果第一列数据中所有值都是唯一的,将不再对第二列进行排序。

3.指定排序方向

默认情况下,查询数据按字母升序进行排序(A~Z),但数据的排序并不仅限于此,还可以使用ORDER BY对查询结果进行降序排序(Z~A)。这可以通过关键字DESC实现,下面的例子表明了如何进行降序排列。
【例1.24】查询fruits表中的f_name和f_price字段,对结果按f_price降序方式排序,SQL语句如下:

Select f_name,f_price from fruits order by f_price DESC;

提示

与DESC相反的是ASC(升序),将字段列中的数据按字母表顺序升序排列。实际上,在排序的时候ASC是默认的排序方式,所以加不加都可以。也可以对多列进行不同的顺序排序,如【例1.25】所示。
【例1.25】查询fruits表,先按f_price降序排列,再按f_name字段升序排列,SQL语句如下:

Select f_price,f_name from fruits order by f_price DESC  ,f_name;

提示

DESC排序方式只应用到直接位于其前面的字段上,由结果可以看出。提示DESC关键字只对其前面的列进行降序排列,在这里只对f_price排序,而并没有对f_name进行排序,因此,f_price按降序排列,而f_name列仍按升序排列。如果要对多列都进行降序排列,必须要在每一列的列名后面加DESC关键字。

1.2.12 分组查询

分组查询是对数据按照某个或多个字段进行分组。MySQL中使用GROUP BY关键字对数据进行分组,基本语法形式为:

[GROUP BY 字段]    [ HAVING <条件表达式>]

字段值为进行分组时所依据的列名称;“HAVING <条件表达式>”指定满足表达式限定条件的结果将被显示。

1.创建分组

GROUP BY关键字通常和集合函数一起使用,比如MAX()、MIN()、COUNT()、SUM()、AVG()。例如,要返回每个水果供应商提供的水果种类,这时就要在分组过程中用到COUNT()函数,把数据分为多个逻辑组,并对每个组进行集合计算。【例7.26】根据s_id对fruits表中的数据进行分组,SQL语句如下:

Select s_id,count(*) AS total from fruits GROUP BY s_id;

查询结果显示,s_id表示供应商的ID,Total字段使用COUNT()函数计算得出,GROUP BY子句按照s_id排序并对数据分组,可以看到ID为101、102、105的供应商分别提供3种水果,ID为103、104、107的供应商分别提供2种水果,ID为106的供应商只提供1种水果。
如果要查看每个供应商提供的水果的种类名称,该怎么办呢?在MySQL中,可以在GROUP BY子句中使用GROUP_CONCAT()函数,将每个分组中各个字段的值显示出来。
【例1.27】根据s_id对fruits表中的数据进行分组,将每个供应商的水果名称显示出来,SQL语句如下:

Select s_id,GROUP_CONCAT(f_name) AS names  from fruits GROUP BY s_id;

由结果可以看到,GROUP_CONCAT()函数将每个分组中的名称显示出来了,其名称的个数与COUNT()函数计算出来的相同。

2.使用HAVING过滤分组

GROUP BY可以和HAVING一起限定显示记录所需满足的条件,只有满足条件的分组才会被显示。
【例1.28】根据s_id对fruits表中的数据进行分组,并显示水果种类大于1的分组信息,SQL语句如下:

Select s_id,GROUP_CONCAT(f_name) AS names  from fruits GROUP BY s_id HAVING COUNT(f_name)>1;

由结果可以看到,ID为101、102、103、104、105、107的供应商提供的水果种类大于1,满足HAVING子句条件,因此出现在返回结果中;而ID为106的供应商的水果种类等于1,不满足限定条件,因此不在返回结果中。

提示

HAVING关键字与WHERE关键字都是用来过滤数据的,两者有什么区别呢?其中重要的一点是,HAVING在数据分组之后进行过滤来选择分组,而WHERE在分组之前来选择记录。另外,WHERE排除的记录不再包括在分组中。

3.在GROUP BY子句中使用WITH ROLLUP

使用WITH ROLLUP关键字之后,在所有查询出的分组记录之后增加一条记录,该记录计算查询出的所有记录的总和,即统计记录数量。
【例1.29】根据s_id对fruits表中的数据进行分组,并显示记录数量,SQL语句如下:

Select s_id,count(*) AS total from fruits GROUP BY s_id WITH ROLLUP;

由结果可以看到,通过GROUP BY分组之后,在显示结果的最后面新添加了一行,该行Total列的值正好是上面所有数值之和。

4.多字段分组

使用GROUP BY可以对多个字段进行分组,GROUP BY关键字后面跟需要分组的字段,MySQL根据多字段的值来进行层次分组,分组层次从左到右,即先按第1个字段分组,然后在第1个字段值相同的记录中再根据第2个字段的值进行分组,以此类推。
【例1.30】根据s_id和f_name字段对fruits表中的数据进行分组,SQL语句如下:

Select * from fruits group by s_id,f_name;

由结果可以看到,查询记录先按照s_id进行分组,再对f_name字段按不同的取值进行分组。

1.2.13 使用LIMIT限制查询结果的数量

SELECT返回所有匹配的行,有可能是表中所有的行,若仅仅需要返回第一行或者前几行,可使用LIMIT关键字,基本语法格式如下:

LIMIT  [位置偏移量,]行数

第一个“位置偏移量”参数指示MySQL从哪一行开始显示,是一个可选参数,如果不指定“位置偏移量”,将会从表中的第一条记录开始(第一条记录的位置偏移量是0,第二条记录的位置偏移量是1,以此类推);第二个参数“行数”指示返回的记录条数。
【例1.32】显示fruits表查询结果的前4行,SQL语句如下:

Select* from fruits limit 4;

由结果可以看到,该语句没有指定返回记录的“位置偏移量”参数,显示结果从第一行开始,“行数”参数为4,因此返回的结果为表中的前4行记录。如果指定返回记录的开始位置,那么返回结果为从“位置偏移量”参数开始的指定行数,“行数”参数指定返回的记录条数。
【例1.33】在fruits表中,使用LIMIT子句,返回从第5个记录开始的行数长度为3的记录,SQL语句如下:

Select* from fruits limit 4,3;

由结果可以看到,该语句指示MySQL返回从第5条记录行开始之后的3条记录。第一个数字‘4’表示从第5行开始(位置偏移量从0开始,第5行的位置偏移量为4),第二个数字3表示返回的行数。
所以,带一个参数的LIMIT指定从查询结果的首行开始,唯一的参数表示返回的行数,即“LIMIT n”与“LIMIT 0,n”等价。带两个参数的LIMIT可以返回从任何一个位置开始的指定的行数。
返回第一行时,位置偏移量是0。因此,“LIMIT 1, 1”将返回第二行,而不是第一行。
提示
MySQL 8.0中可以使用“LIMIT 4 OFFSET 3”,意思是获取从第5条记录开始后面的3条记录,和“LIMIT 4,3;”返回的结果相同。

1.3 使用集合函数查询

有时候并不需要返回实际表中的数据,而只是对数据进行总结。MySQL提供一些查询功能,可以对获取的数据进行分析和报告。这些函数的功能有:计算数据表中记录行数的总数、计算某个字段列下数据的总和,以及计算表中某个字段下的最大值、最小值或者平均值。本节将介绍这些函数以及如何使用它们。这些聚合函数的名称和作用如表7.2所示。
在这里插入图片描述
接下来,将详细介绍各个函数的使用方法。

1.3.1 COUNT()函数

COUNT()函数统计数据表中包含的记录行的总数,或者根据查询结果返回列中包含的数据行数。其使用方法有两种:
● COUNT(*)计算表中总的行数,不管某列是否有数值或者为空值。
● COUNT(字段名)计算指定列下总的行数,计算时将忽略空值的行。
【例1.34】查询customers表中总的行数,SQL语句如下:

Select count(*) as cust_num from customers;

由查询结果可以看到,COUNT(*)返回customers表中记录的总行数,不管其值是什么,返回的总数的名称为cust_num。
【例1.35】查询customers表中有电子邮箱的顾客的总数,SQL语句如下:

Select count(c_email) as email_num from customers;

由查询结果可以看到,表中5个customer只有3个有email,customer的email为空值NULL的记录没有被COUNT()函数计算。

提示

两个例子中不同的数值说明了两种方式在计算总数的时候对待NULL值的方式不同:指定列的值为空的行被COUNT()函数忽略;如果不指定列,而在COUNT()函数中使用星号“*”,则所有记录都不忽略。
前面介绍分组查询的时候,介绍了如何用COUNT()函数与GROUP BY关键字一起来计算不同分组中的记录总数。
【例1.36】在orderitems表中,使用COUNT()函数统计不同订单号中订购的水果种类,SQL语句如下:

Select o_num,count(f_id)from orderitems Group by o_num;

从查询结果可以看到,GROUP BY关键字先按照订单号进行分组,然后计算每个分组中的总记录数。

1.3.2 SUM()函数

SUM()是一个求总和的函数,返回指定列值的总和。
【例1.37】在orderitems表中查询30005号订单一共购买的水果总量,SQL语句如下:

Select sum(quantity) as items_total from orderitems where o_num=3305;

由查询结果可以看到,GROUP BY按照订单号o_num进行分组,SUM()函数计算每个分组中订购的水果的总量。

提示

SUM()函数在计算时,忽略列值为NULL的行。

Select o_num,sum(quantity) as items_total from orderitems Group by o_num;

1.3.3 AVG()函数

AVG()函数通过计算返回的行数和每一行数据的和,求得指定列数据的平均值。
【例1.39】在fruits表中,查询s_id=103的供应商的水果价格的平均值,SQL语句如下:

Select avg(f_price) as avg_price from fruits where s_id=103;

该例中,查询语句增加了一个WHERE子句,并且添加了查询过滤条件,只查询s_id = 103的记录中的f_price。因此,通过AVG()函数计算的结果只是指定的供应商水果的价格平均值,而不是市场上所有水果价格的平均值。
AVG()可以与GROUP BY一起使用,来计算每个分组的平均值。
【例1.40】在fruits表中,查询每一个供应商的水果价格的平均值,SQL语句如下:

Select s_id,avg(f_price) as avg_price from fruits group by s_id;

GROUP BY关键字根据s_id字段对记录进行分组,然后计算出每个分组的平均值,这种分组求平均值的方法非常有用,例如求不同班级学生成绩的平均值、求不同部门工人的平均工资、求各地的年平均气温等。
提示
AVG()函数使用时,其参数为要计算的列名称,如果要得到多个列的多个平均值,则需要在每一列上使用AVG()函数。

1.3.4 MAX()函数

MAX()返回指定列中的最大值。
【例1.41】在fruits表中查找市场上价格最高的水果值,SQL语句如下:

Select MAX(f_price) AS max_price from fruits;

由结果可以看到,MAX()函数查询出了f_price字段的最大值15.70。

MAX()也可以和GROUP BY关键字一起使用,求每个分组中的最大值。
【例1.42】在fruits表中查找不同供应商提供的价格最高的水果值,SQL语句如下:

Select s_id,MAX(f_price) AS max_price from fruits group by s_id;

由结果可以看到,GROUP BY关键字根据s_id字段对记录进行分组,然后计算出每个分组中的最大值。
MAX()函数不仅适用于查找数值类型,也可应用于字符类型。
【例1.43】在fruits表中查找f_name的最大值,SQL语句如下:

Select MAX(f_name) AS max_price from fruits;

由结果可以看到,MAX()函数可以对字母进行大小判断,并返回最大的字符或者字符串值。

提示

MAX()函数除了用来找出最大的列值或日期值之外,还可以返回任意列中的最大值,包括返回字符类型的最大值。在对字符类型数据进行比较时,按照字符的ASCII码值大小进行比较,从a~z,a的ASCII码最小,z的最大。在比较时,先比较第一个字母,如果相等,继续比较下一个字符,一直到两个字符不相等或者字符结束为止。例如,‘b’与‘t’比较时,‘t’为最大值;“bcd”与“bca”比较时,“bcd”为最大值。

1.3.5 MIN()函数

MIN()返回查询列中的最小值。
【例1.44】在fruits表中查找市场上价格最低的水果值,SQL语句如下:

Select MIN(f_price) AS min_price from fruits;

由结果可以看到,MIN ()函数查询出了f_price字段的最小值2.20。
MIN()也可以和GROUP BY关键字一起使用,求出每个分组中的最小值。
【例1.45】在fruits表中查找不同供应商提供的价格最低的水果值,SQL语句如下:

Select s_id,MIN(f_price) AS min_price from fruits group by s_id;

由结果可以看到,GROUP BY关键字根据s_id字段对记录进行分组,然后计算出每个分组中的最小值。
MIN()函数与MAX()函数类似,不仅适用于查找数值类型,也可应用于字符类型。

1.4 连接查询

连接是关系数据库模型的主要特点。连接查询是关系数据库中最主要的查询,主要包括内连接、外连接等。通过连接运算符可以实现多个表查询。在关系数据库管理系统中,表建立时各数据之间的关系不必确定,常把一个实体的所有信息存放在一个表中。当查询数据时,通过连接操作查询出存放在多个表中的不同实体的信息。当两个或多个表中存在相同意义的字段时,便可以通过这些字段对不同的表进行连接查询。本节将介绍多表之间的内连接查询、外连接查询以及复合条件连接查询。

1.4.1 内连接查询

内连接(INNER JOIN)使用比较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行,组合成新的记录,也就是说,在内连接查询中,只有满足条件的记录才能出现在结果关系中。为了演示的需要,首先创建数据表suppliers,SQL语句如下:

create table `suppliers` (
	`s_id` int (11),
	`s_name` char (150),
	`s_city` char (150),
	`s_zip` char (30),
	`s_call` char (150)
); 
insert into `suppliers` (`s_id`, `s_name`, `s_city`, `s_zip`, `s_call`) values('101','FastFruits','tianjin','3000','48075');
insert into `suppliers` (`s_id`, `s_name`, `s_city`, `s_zip`, `s_call`) values('102','Lt supplies','chongqing','14141','4141');
insert into `suppliers` (`s_id`, `s_name`, `s_city`, `s_zip`, `s_call`) values('103','adada','zhongsan','54545','141414');
insert into `suppliers` (`s_id`, `s_name`, `s_city`, `s_zip`, `s_call`) values('104','faf  isn','taiyuan','4141','41414');
insert into `suppliers` (`s_id`, `s_name`, `s_city`, `s_zip`, `s_call`) values('105','sajao ','beijing','41','4141');
insert into `suppliers` (`s_id`, `s_name`, `s_city`, `s_zip`, `s_call`) values('106','daaDa','zhengzhou','414141','41142');

【例1.46】在fruits表和suppliers表之间使用内连接查询。查询之前,查看两个表的结构:
在这里插入图片描述
由结果可以看到,fruits表和suppliers表中都有相同数据类型的字段s_id,两个表通过s_id字段建立联系。接下来从fruits表中查询f_name、f_price字段,从suppliers表中查询s_id、s_name,SQL语句如下:

Select suppliers.s_id ,s_name,f_name,f_price from fruits ,suppliers where  fruits.s_id=Suppliers.s_id;

在这里,SELECT语句与前面所介绍的一个最大的差别是:SELECT后面指定的列分别属于两个不同的表,(f_name,f_price)在表fruits中,而另外两个字段在表suppliers中;同时FROM子句列出了两个表fruits和suppliers。WHERE子句在这里作为过滤条件,指明只有两个表中的s_id字段值相等的时候才符合连接查询的条件。从返回的结果可以看到,显示的记录是由两个表中的不同列值组成的新记录。

提示

因为fruits表和suppliers表中有相同的字段s_id,因此在比较的时候需要完全限定表名(格式为“表名.列名”),如果只给出s_id,MySQL将不知道指的是哪一个,并返回错误信息。
下面的内连接查询语句返回与前面完全相同的结果。
【例1.47】在fruits表和suppliers表之间,使用INNER JOIN语法进行内连接查询,SQL语句如下:

Select suppliers.s_id ,s_name,f_name,f_price from fruits INNER JOIN suppliers On fruits.s_id=Suppliers.s_id;

在这里的查询语句中,两个表之间的关系通过INNER JOIN指定。使用这种语法的时候,连接的条件使用ON子句而不是WHERE,ON和WHERE后面指定的条件相同。
提示
使用WHERE子句定义连接条件比较简单明了,而INNER JOIN语法是ANSI SQL的标准规范,使用INNER JOIN连接语法能够确保不会忘记连接条件,而且WHERE子句在某些时候会影响查询的性能。

如果在一个连接查询中,涉及的两个表都是同一个表,这种查询称为自连接查询。自连接是一种特殊的内连接,它是指相互连接的表在物理上为同一张表,但可以在逻辑上分为两张表。
【例1.48】查询f_id= 'a1’的水果供应商提供的水果种类,SQL语句如下:

Select f1.f_id,f1.f_name from fruits AS f1,fruits As f2 where f1.S_id =f2.s_id and f2.f_id=’a1’;

此处查询的两个表是相同的表,为了防止产生二义性,对表使用了别名,fruits表第1次出现的别名为f1,第2次出现的别名为f2,使用SELECT语句返回列时明确指出返回以f1为前缀的列的全名,WHERE连接两个表,并按照第2个表的f_id对数据进行过滤,返回所需数据。

1.4.2 外连接

查询外连接查询将查询多个表中相关联的行,内连接时,返回查询结果集合中仅是符合查询条件和连接条件的行。有时候需要包含没有关联的行中数据,即返回查询结果集合中不仅包含符合连接条件的行,还包括左表(左外连接或左连接)、右表(右外连接或右连接)或两个边接表(全外连接)中的所有数据行。外连接分为左外连接或左连接和右外连接或右连接:
● LEFT JOIN(左连接):返回包括左表中的所有记录和右表中连接字段相等的记录。
● RIGHT JOIN(右连接):返回包括右表中的所有记录和左表中连接字段相等的记录。

1.LEFT JOIN左连接

左连接的结果包括LEFT OUTER子句中指定的左表的所有行,而不仅仅是连接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果行中,右表的所有选择列表列均为空值。首先创建表orders,SQL语句如下:


create table `orders` (
	`o_num` int (11),
	`o_data` datetime ,
	`c_id` int (11)
); 
insert into `orders` (`o_num`, `o_data`, `c_id`) values('3001','2020-03-05 00:00:00','10001');
insert into `orders` (`o_num`, `o_data`, `c_id`) values('3002','2020-03-06 21:24:41','10003');
insert into `orders` (`o_num`, `o_data`, `c_id`) values('3003','2020-03-02 21:24:56','10006');
insert into `orders` (`o_num`, `o_data`, `c_id`) values('3004','2020-02-29 21:25:17','10008');
insert into `orders` (`o_num`, `o_data`, `c_id`) values('3005','2020-02-26 21:25:33','10001');

【例1.49】在customers表和orders表中,查询所有客户,包括没有订单的客户,SQL语句如下:

Select customers.c_id,orders.o_num from customers LEFT OUTER JOIN orders on customers.C_id=orders.c_id;

结果显示了5条记录,ID等于10002的客户目前并没有下订单,所以对应的orders表中并没有该客户的订单信息,所以该条记录只取出了customers表中相应的值,而从orders表中取出的值为空值NULL。

2.RIGHT JOIN右连接

右连接是左连接的反向连接,将返回右表的所有行。如果右表的某行在左表中没有匹配行,左表将返回空值。
【例7.50】在customers表和orders表中,查询所有订单,包括没有客户的订单,SQL语句如下:

Select customers.c_id,orders.o_num from customers RIGHT OUTER JOIN orders on customers.C_id=orders.c_id;

结果显示了5条记录,订单号等于30004的订单的客户可能由于某种原因取消了该订单,对应的customers表中并没有该客户的信息,所以该条记录只取出了orders表中相应的值,而从customers表中取出的值为空值NULL。

1.4.3 复合条件连接

查询复合条件连接查询是在连接查询的过程中,通过添加过滤条件限制查询的结果,使查询的结果更加准确。
【例1.51】在customers表和orders表中,使用INNER JOIN语法查询customers表中ID为10001的客户的订单信息,SQL语句如下:

Select customers.c_id,orders.o_num from customers INNER JOIN orders on customers.C_id=orders.c_id  AND customers.c_id=10001;

结果显示,在连接查询时指定查询客户ID为10001的订单信息,添加了过滤条件之后返回的结果将会变少,因此返回结果只有两条记录。
使用连接查询,并对查询的结果进行排序。
【例1.52】在fruits表和suppliers表之间,使用INNER JOIN语法进行内连接查询,并对查询结果排序,SQL语句如下:

Select suppliers.s_id,s_name,f_name,f_price from fruits inner join suppliers on fruits.s_id=suppliers.s_id order by fruits.s_id;

由结果可以看到,内连接查询的结果按照suppliers.s_id字段进行了升序排序。

1.5 子查询

子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从MySQL 4.1开始引入。在SELECT子句中先计算子查询,子查询结果作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。子查询中常用的操作符有ANY(SOME)、ALL、IN、EXISTS。子查询可以添加到SELECT、UPDATE和DELETE语句中,而且可以进行多层嵌套。子查询中也可以使用比较运算符,如“<”“<=”“>”“>=”和“!=”等。本节将介绍如何在SELECT语句中嵌套子查询。

1.5.1 带ANY、SOME关键字的子查询

ANY和SOME关键字是同义词,表示满足其中任一条件,它们允许创建一个表达式对子查询的返回值列表进行比较,只要满足内层子查询中的任何一个比较条件,就返回一个结果作为外层查询的条件。下面定义两个表tbl1和tbl2:

CREATE TABLE tbl1(num1 INT NOT NULL);
CREATE TABLE tbl2(num2 INT NOT NULL);
INSERT INTO tbl1 VALUE (1),(8),(56),(32);
INSERT INTO tbl2 VALUE (11),(48),(6),(2);

ANY关键字接在一个比较操作符的后面,表示若与子查询返回的任何值比较为TRUE,则返回TRUE。
【例1.53】返回tbl2表的所有num2列,然后将tbl1中的num1的值与之进行比较,只要大于num2的任何1个值,即为符合查询条件的结果

select num1 from tb11 where num1>any(select num2 from tb12);

在子查询中,返回的是tbl2表的所有num2列结果(6,14,11,20),然后将tbl1中的num1列的值与之进行比较,只要大于num2列的任意一个数即为符合条件的结果。

1.5.2 带ALL关键字的子查询

ALL关键字与ANY和SOME不同,使用ALL时需要同时满足所有内层查询的条件。例如,修改前面的例子,用ALL关键字替换ANY。ALL关键字接在一个比较操作符的后面,表示与子查询返回的所有值比较为TRUE,则返回TRUE。
【例1.54】返回tbl1表中比tbl2表num2列所有值都大的值,SQL语句如下:

select num1 from tb11 where num1>all(select num2 from tb12);

在子查询中,返回的是tbl2的所有num2列结果(6,14,11,20),然后将tbl1中的num1列的值与之进行比较,大于所有num2列值的num1值只有27,因此返回结果为27。

1.5.3 带EXISTS关键字的子查询

EXISTS关键字后面的参数是一个任意的子查询,系统对子查询进行运算以判断它是否返回行,如果至少返回一行,那么EXISTS的结果为true,此时外层查询语句将进行查询;如果子查询没有返回任何行,那么EXISTS返回的结果是false,此时外层语句将不进行查询。

【例1.55】查询suppliers表中是否存在s_id=107的供应商,如果存在,则查询fruits表中的记录,SQL语句如下:

Select * from fruits where exists (select s_name from suppliers where s_id =107);

由结果可以看到,内层查询结果表明suppliers表中存在s_id=107的记录,因此EXISTS表达式返回true;外层查询语句接收true之后对表fruits进行查询,返回所有的记录。EXISTS关键字可以和条件表达式一起使用。
【例1.56】查询suppliers表中是否存在s_id=107的供应商,如果存在,则查询fruits表中的f_price大于10.20的记录,SQL语句如下:

Select * from fruits where f_prices>10.20  exists (select s_name from suppliers where s_id =107);

由结果可以看到,内层查询结果表明suppliers表中存在s_id=107的记录,因此EXISTS表达式返回true;外层查询语句接收true之后根据查询条件f_price > 10.20对fruits表进行查询,返回结果为4条f_price大于10.20的记录。
NOT EXISTS与EXISTS使用方法相同,返回的结果相反。子查询如果至少返回一行,那么NOTEXISTS的结果为false,此时外层查询语句将不进行查询;如果子查询没有返回任何行,那么NOT EXISTS返回的结果是true,此时外层语句将进行查询。
【例1.57】查询suppliers表中是否存在s_id=107的供应商,如果不存在则查询fruits表中的记录,SQL语句如下:

Select * from fruits where not exists (select s_name from suppliers where s_id =107);

查询语句SELECT s_name FROM suppliers WHERE s_id = 107,对suppliers表进行查询返回了一条记录,NOT EXISTS表达式返回false,外层表达式接收false,将不再查询fruits表中的记录。
提示
EXISTS和NOT EXISTS的结果只取决于是否会返回行,而不取决于这些行的内容,所以这个子查询输入列表通常是无关紧要的。

1.5.4 带IN关键字的子查询

IN关键字进行子查询时,内层查询语句仅仅返回一个数据列,这个数据列里的值将提供给外层查询语句进行比较操作。
【例1.58】在orderitems表中查询f_id为c0的订单号,并根据订单号查询具有订单号的客户c_id,SQL语句如下:

Select c_id from orders where o_num in (select o_num from orderitems where f_id=’c0’)
-->select o_num from orderitems where f_id=’c0’
-->Select c_id from orders where o_num in (30003,30005)
Select c_id from orders where o_num not in (select o_num from orderitems where f_id=’c0’)

1.5.5

Select s_id ,f_name from fruits where s_id=(select s1.s_id from suppliers AS s1 where s1.s_city=’Tianjin’)
-->select s1.s_id from suppliers AS s1 where s1.s_city=’Tianjin’
-->

这个例子说明在处理SELECT语句的时候,MySQL实际上执行了两个操作过程,即先执行内层子查询,再执行外层查询,内层子查询的结果作为外部查询的比较条件。SELECT语句中可以使用NOT IN关键字,其作用与IN正好相反。

Select s_id ,f_name from fruits where s_id<>(select s1.s_id from suppliers AS s1 where s1.s_city=’Tianjin’)

1.7 为表和字段取别名

在前面介绍分组查询、集合函数查询和嵌套子查询章节中,读者注意到有的地方使用AS关键字为查询结果中的某一列指定一个特定的名字。在内连接查询时,则对相同的表fruits分别指定两个不同的名字,这里可以为字段或者表取一个别名,在查询时,使用别名替代其指定的内容,本节将介绍如何为字段和表创建别名以及如何使用别名。

1.7.1 为表取别名

当表名字很长或者执行一些特殊查询时,为了方便操作或者需要多次使用相同的表时,可以为表指定别名,用这个别名替代表原来的名称。为表取别名的基本语法格式为:

表名 [AS] 表别名

列名 [AS] 列别名

1.8 使用正则表达式查询

正则表达式通常被用来检索或替换那些符合某个模式的文本内容,根据指定的匹配模式匹配文本中符合要求的特殊字符串。例如,从一个文本文件中提取电话号码,查找一篇文章中重复的单词或者替换用户输入的某些敏感词语等,这些地方都可以使用正则表达式。正则表达式强大而且灵活,可以应用于非常复杂的查询。
MySQL中使用REGEXP关键字指定正则表达式的字符匹配模式。表7.3列出了REGEXP操作符中常用字符匹配列表。

1.8.1插入数据

Insert INTO table_name (column_list) values(value_list);

1.8.2同时插入多条记录

Insert INTO table_name (column_list) values(value_list),(value_list),(value_list);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

威威猫的栗子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值