MySQL从数据表中查询数据的基本语为SELECT语。SELECT语的基本格式是:
SELECT
{* | <字段列名>}
[
FROM <表 1>, <表 2>…
[WHERE <表达式>
[GROUP BY
[HAVING [{ }…]]
[ORDER BY ]
[LIMIT[,] ]
]
SELECT [字段1,字段2,...,字段n]
FROM [表或视图]
WHERE [查询条件];
其中,各条子句的含义如下:
{*|<字段列名>} 包含星号通配符的字段列表,表示查询的字段,
其中字段列至少包含一个字段名称,如果要查询多个字段,
多个字段之间要用逗号隔开,最后一个字段后不要加逗号。
FROM <表 1>,<表 2>…,表 1 和表 2 表示查询数据的来源,可以是单个或多个。
WHERE 子句是可选项,如果选择该项,将限定查询行必须满足的查询条件。
GROUP BY< 字段 >,该子句告诉 MySQL 如何显示查询出来的数据,并按照字段分组。
[ORDER BY< 字段 >],该子句告诉 MySQL 按什么样的顺序显示查询出来的数据,
可以进行的排序有升序(ASC)和降序(DESC)。
[LIMIT[
数。
下面以一个例子说明如何使用SELECT从单个表中获取数据。
首先定义数据表,输入语句如下:
create table fruits
(
f_id char(10) not null,
s_id int not null,
f_name char(255) not null,
f_price decimal(8,2) not null,
primary key(f_id)
);
为了演示如何使用SELECT语句,需要插入如下数据:
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', 11.2),
('bs2',105,'melon',8.2),
('t1',102,'banana', 10.3),
('t2',102,'grape', 5.3),
('o2',103,'coconut', 9.2),
('c0',101,'cherry', 3.2),
('a2',103, 'apricot',2.2),
('l2',104,'lemon', 6.4),
('b2',104,'berry', 7.6),
('m1',106,'mango', 15.6),
('m2',105,'xbabay', 2.6),
('t4',107,'xbababa', 3.6),
('m3',105,'xxtt', 11.6),
('b5',107,'xxxx', 3.6);
使用SELECT语句查询f_id字段的数据。
select f_id, f_name from fruits;
该语句的执行过程是,SELECT语决定了要询的列值,
在这里查询f_id和f_name两个字段的值,FROM 子句指定了数据的来源,
这里指定数据表 fruits,因此返回结果为 fruits表中fid和fname两个字段下所有的数据。
其显示顺序为添加到表中的顺序.
单表查询
是指从一张表数据中查询所需的数据。
主要有:查询所有字段、查询指定字段、查询指定记录、查询空值、多条件的查询、
对查询结果进行排序等。
在SELECT语句中使用星号(*)通配符查询所有字段
SELECT查询记录最简单的形式是从一个表中检索所有记录,实现的方法是使用星号(*)通配符指定查找所有列的名称。语法格式如下:
SELECT * FROM 表名;
从fruits表中检索所有字段的数据,SQL语句如下:
select * from fruits;
可以看到,使用星号 (*) 通配符时,将返回所有列,列按照定义表时候的顺序显示。
下面介绍另外一种查询所有字段值的方法。根据前面 SELECT 语句的格式,SELECT关
键字
后面的字段名为将要查找的数据,因此可以将表中所有字段的名称跟在 SELECT 子句后面,
如果忘记了字段名称,可以使用 DESC 命令查看表的结构。有时候,由于表中的字段可能比较多,不一定能记得所有字段的名称,因此该方法会很不方便,不建议使用。
例如查询fruits 表中的所有数据,SOL语句也可以书写如下:
select f_id,s_id,f_name,f_price from fruits;
查询结果与上面通配符查询相同
提醒:一般情况下,除非需要使用表中所有的字段数据,最好不要使用通配符“*“。
使用通配符虽然可以节省输入查询语句的时间,但是获取不需要的列数据通常会
降低查询和所使用的应用程序的效率。通配符的优势是,当不知道所需要的列的名
称时, 可以通过它获取它。
查询表中的某一个字段,语法格式为:
SELECT 列名 FROM 表名;
查询fruits表中f_name列所有水果名称,SQL语句如下:
select f_name from fruits;
该语句使用SELECT声明从fruits表中获取名称为f_name字段下的所有水果名称,
指定字段的名称紧跟在SELECT关键字之后
使用SELECT 声明,可以获取多个字段下的数据,只需要在关键字 SELECT 后面指定要
查找的字段的名称,不同字段名称之间用逗号(,)分隔开,最后一个字段后面不需要
加逗号
语法格式如下:
SELECT 字段名1,字段名2,... 字段名n FROM 表名;
例如,从fruits表中获取f_name和f_price两列,SQL语句如下:
select f_name, f_price from fruits;
该语句使用SELECT声明从fruits表中获取名称为f_name和f_price两个字段下的所有水
果名称和价格,两个字段之间用逗号分隔开,
数据库中包含大量的数据,根据特殊要求,可能只需要查询表中的指定数据,
即对数据进行过滤。在SELECT 语句中,通过 WHERE子可以对数据进行过滤,
语法格式为:
SELECT 字段名1,字段名2,... 字段名n
FROM 表名
WHERE 查询条件;
在WHERE子句中,MySQL提供了一系列的条件判断符,查询结果如表所示
查询价格为10.2元的水果的名称,SQL语句如下:
select f_name, f_price
from fruits
where f_price = 10.2;
该语句使用SELECT声明从 fruits 表中取价格等于10.2水果的数据,从查询结果可以看
到,价格是 10.2的水果的名称是 blackberry,其他的均不满足查询条件.
查找名称为“apple”的水果的价格,SQL语句如下:
select f_name, f_price
from fruits
where f_name = 'apple';
该语句使用SELECT声明从fruits 表中获取名称为“apple”的水果的价格,从查询结果可以
看到 只有名称为“apple”行被返回,其他的均不满足查询条件。
IN 关键字用来查询满足指定范围内的条件的记录,使用IN操作符,
将所有检索条件用括号括起来,检索条件之间用逗号分隔开,
只要满足条件范围内的一个值即为匹配项。
比如:查询s_id为101和102的记录,SQL语句如下:
select s_id,f_name, f_price
from fruits
where s_id IN (101,102)
order BY f_name;
order BY f_name; ---------将名字排序
相反的,可以使用关键字NOT 来检索不在条件范围内的记录。
查询所有s_id不等于101也不等于102的记录,SQL语句如下:
select s_id,f_name, f_price
from fruits
where s_id not in (101,102)
order BY f_name;
BETWEEN AND 用来查询某个范围内的值,该操作符需要两个参数,
即范围的开始值和结束值,如果字段值满足指定的范围查询条件,则这些记录被返回.
查询价格在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,表示指定范围之外的值,
如果字段值不满足指定的范围内的值,则这些记录被返回。
查询价格在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 的记录,也应当作为查询结果。
在前面的检索操作中,讲述了如何查询多个字段的记录,如何进行比较查询或者是查
询 一个条件范围内的记录,如果要查找所有的包含字符“ge”的水果名称,该如何查找呢?
简单的比较操作在这里已经行不通了,在这里,需要使用通配符进行匹配查找,通过
创建查找模式对表中的数据进行比较。执行这个任务的关键字是 LIKE。通配符是一种
在SQL的WHERE条件子中拥有特殊意思的字符,SOL语中支持多种通配符,
可以和LIKE一起使用的通配符有"%"和“_",
查找所有以’b’字母开头的水果,SQL语句如下:
select f_id, f_name
from fruits
where f_name like 'b%';
该语句查询的结果返回所有以“b’开头的水果的id和name,“%’告诉MySQL, 返回所有以字母b’开头的记录,不管“b’后面有多少个字符。
在搜索匹配时通配符“%’可以放在不同位置.
在fruits表中,查询f_name中包含字母’g’的记录,SQL语句如下:
select f_id, f_name
from fruits
where f_name like '%g%';
该语句查询字符串中包含字母“g’的水果名称,只要名字中有字符“g’,
而前面或后面不管有多少个字符,都满足查询的条件。
另一个非常有用的通配符是下划线通配符“_’,该通配符的用法和“%’相同,
区别是"%" 可以匹配多个字符,而"_",只能匹配任意单个字符,如果要匹配多个字符,则需要使用相同个数的“_" .
在fruits表中,查询以字母’y’结尾,且’y’前面只有4个字母的记录,SQL语句如下:
select f_id, f_name
from fruits
where f_name like '____y';
从结果可以看到,以y’结尾且前面只有4个字母的记录只有一条。
其他记录的f_name字段也有以“y’结尾的,但其总的字符串长度不为 5,因此不在返回结果中。
使用 SELECT 查询时,可以增加查询的限制条件,这样可以使查询的结果更加精确
MySQL在WHERE子句中使用AND操作符限定只有满足所有查询条件的记录才会被返回
可以使用AND连接两个甚至多个查询条件,多个条件表达式之间用AND 分开。
and表示同时满足
在fruits表中查询s_id = 101,并且f_price大于等于5的水果价格和名称,SQL语句如下:
select f_id, f_price, f_name
from fruits
where s_id = '101' and f_price >=5;
在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';
与AND相反,在WHERE声明中使用OR操作符,表示只需要满足其中一个条件的记录即
可返回。
OR也可以连接两个甚至多个查询条件,多个条件表达式之间用OR分开。
比如:查询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;
在这里,也可以使用IN 操作符实现与OR 相同的功能,下面的例子可进行说明。
比如:查询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中的操作数结合。
SELECT 返回所有匹配的行,有可能是表中所有的行,如仅仅需要返回第一行或者前几
行, 使用LIMIT关键字,
基本语法格式如下:
LIMIT [位置偏移量,] 行数
第一个“位置偏移量”参数指示 MySOL从哪一行开始显示,是一个可选参数,
如果不指定“位置偏移量”,将会从表中的第一条记录开始(第一条记录的位置偏移量0,
第二条记录的位置偏移量是 1...依次类推);第二个参数“行数”指示返回的记录条数。
(表示只查看几行)
原本表的内容
比如:显示fruits表查询结果的前4行,SQL语句如下:
select * from fruits limit 4;
再比如:在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”等价。带两个参数的LIMIT可以返回从任何一个位置开始的指定的行数. 返回第一行时,位置偏移量是0。因此,“LIMIT1,1”将返回第二行,而不是第一行。
从前面的例子可以看到,SELECT 查询返回所有匹配的行。
例如,查询 fruits 表中所有的sid,可以看到查询结果返回了 16 条记录,其中有一些
重复的s_id 值,有时,出于对数据分析的要求,需要消除重复的记录值,如何使查询结果
没有重复呢?
在 SELECT 语句中,可以使用DISTINCT关键字指示MySQL消除重复的记录值。
语法格式为:
SELECT DISTINCT 字段名 FROM 表名;
比如:查询fruits表中s_id字段的值,返回s_id字段值且不得重复,SQL语句如下:
select distinct s_id from fruits;
单列排序:使用ORDER BY子对指定的列数据进行排序
查询fruits表的f_name字段值,并对其进行排序,SQL语句如下:
select f_name
from fruits
order by f_name;
该语句查询的结果和前面的语句相同,不同的是,通过指定 ORDER BY 子句,
MySQL对查询的name列的数据,按字母表的顺序进行了升序排序。
有时,需要根据多列值进行排序。比如,如果要显示一个学生列表,可能会有多个学
生的姓氏是相同的,因此还需要根据学生的名进行排序。对多列数据进行排序,须将需
要排序的列之间用逗号隔开。
查询fruits表中的f_name和f_price字段,先按f_name排序,再按f_price排序,SQL语句
如下:
select f_name, f_price
from fruits
order by f_name, f_price;
注意:在对多列进行排序的时候,首先排序的第一列必须有相同的列值,才会对第二列进
行排序。
如果第一列数据中所有值都是唯一的,将不再对第二列进行排序。
默认情况下,查询数据按字母升序进行排序(从 A~Z),但数据的排序并不仅限于此,
还可以使用ORDER BY对查询结果进行降序排序(从Z~A)这
可以通过关键字DESC实现降序排序
下面的例子表明了如何进行降序排列。
查询fruits表中的f_name和f_price字段,对结果按f_price降序方式排序,SQL语句如
下:
select f_name, f_price
from fruits
order by f_price desc;
分组查询是对数据按照某个或多个字段进行分组,(将名字一样的组合成一个组)
MySQL中使用GROUP BY关键字对数据进行分组,基本语法形式为:
[GROUP BY 字段]
[HAVING <条件表达式>]
字段值 为进行分组时所依据的列名称:
“HAVING<条件表达式>”指定满足表达式限定条件的结果将被显示。
GROUP BY关键字通常和集合函数一起使用,例如:MAX()MIN()、COUNT()、
SUM(),AVG()。
例如,要返回每个水果供应商提供的水果种类,这时就要在分组过程中用到 COUNTO
函数,
把数据分为多个逻辑组,并对每个组进行集合计算。
根据s_id对fruits表中的数据进行分组,SQL语句如下:
select s_id, count(*) as total
from fruits
group by s_id;
查询结果显示s_id 表示供应商的ID ,Ttal字段使用COUNT()函数计算得出 GROUP BY子句按照 s_id 排序并对数据分组可以看到ID为101 102 105 的供应商分别提供3 种水果ID为 103、104、107的供应商分别提供2种水果,ID为106的供应商只提供1种水果,如果要查看每个供应商提供的水果的种类的名称,该怎么办呢?
count(*) as total ————将group by s_id分的组的数用count(*)记录并放到total列中
MySQL中可以在GROUP BY字节中使用GROUP_CONCAT()函数, 将每个分组中各个字段的值显示出来。 (看名字)
根据s_id对fruits表中的数据进行分组,将每个供应商的水果名称显示出来,SQL语句如下:
select s_id, group_concat(f_name) as names
from fruits
group by s_id;
group_concat(f_name) ——取出f_name的内容
由结果可以看到,GROUP_CONCAT()函数将每个分组中的名称显示出来了,
其名称的个数与COUNT()函数计算出来的相同。
既看计数又看内容:
select s_id, group_concat(f_name) as names,count(*) as total
from fruits
group by s_id;
GROUP BY可以和HAVING一起限定显示记录所需满足的条件,只有满足条件的分组才
会被显示。
根据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——再次过滤分组
提醒:
HAVING关键字与 WHERE 关键字都是用来过滤数据,两者有什么区别呢?
其中重要的一点是,HAVING在数据分组之后进行过滤来选择分组,
而 WHERE 在分组之前用来选择记录。另外WHERE排除的记录不再包括在分组中。
使用WITH ROLLUP 关键字之后,在所有查询出的分组记录之后增加一条记录,
该记录计算查询出的所有记录的总和,即统计记录数量。
根据s_id对fruits表中的数据进行分组,并显示记录数量,SQL语句如下:
select s_id, COUNT(*) as Total
from fruits
group by s_id with rollup;
由结果可以看到,通过GROUP BY分组之后,在显示结果的最后面新添加了一行,
该行Total列的值正好是上面所有数值之和。
某些情况下需要对分组进行排序,在前面的介绍中,ORDER BY用来对查询的记录排序
如果和GROUP BY一起使用可以完成对分组的排序。
为了演示效果,首先创建数据表,SQL语句如下:
CREATE TABLE orderitems
(
o_num int NOT NULL,
o_item int NOT NULL,
f_id char(10) NOT NULL,
quantity int NOT NULL,
item_price decimal(8,2) NOT NULL,
PRIMARY KEY (o_num,o_item)
) ;
然后插入演示数据。SQL语句如下:
INSERT INTO orderitems(o_num, o_item, f_id, quantity, item_price)
VALUES(30001, 1, 'a1', 10, 5.2),
(30001, 2, 'b2', 3, 7.6),
(30001, 3, 'bs1', 5, 11.2),
(30001, 4, 'bs2', 15, 9.2),
(30002, 1, 'b3', 2, 20.0),
(30003, 1, 'c0', 100, 10),
(30004, 1, 'o2', 50, 2.50),
(30005, 1, 'c0', 5, 10),
(30005, 2, 'b1', 10, 8.99),
(30005, 3, 'a2', 10, 2.2),
(30005, 4, 'm1', 5, 14.99);
查询订单价格大于100的订单号和总订单价格,SQL语句如下:
select o_num, sum(quantity * item_price) as orderTotal
from orderitems
group by o_num
having sum(quantity*item_price) >= 100;
可以看到,返回的结果中orderTotal列的总订单价格并没有按照一定顺序显示,
接下来,使用ORDER BY关键字按总订单价格排序显示结果,SQL语句如下:
select o_num, sum(quantity * item_price) as orderTotal
from orderitems
group by o_num
having sum(quantity*item_price) >= 100
order by orderTotal;
由结果可以看到,GROUP BY 子句按订单号对数据进行分组,
SUM()函数便可以返回总的订单价格,
HAVING 子句对分组数据进行过滤,使得只返回总价格大于 100的订单,
最后使用ORDER BY子排序输出。
注意:
当使用ROLLUP时不能同时使用ORDER BY子进行结果排序。
即ROLLUP和ORDER BY是互相排斥的。
数据表创建的时候,设计者可以指定某列中是否可以包含空值(NULL)。空值不同于0,
也不同于空字符串。空值一般表示数据未知、不适用或将在以后添加数据。
在 SELECT 语句中使用ISNULL子句,可以查询某字段内容为空的记录。
下面,在数据库中创建数据表customers,该表中包含了本章中需要用到的数据。
CREATE TABLE customers
(
c_id int NOT NULL AUTO_INCREMENT,
c_name char(50) NOT NULL,
c_address char(50) NULL,
c_city char(50) NULL,
c_zip char(10) NULL,
c_contact char(50) NULL,
c_email char(255) 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 ', 'Tianjin',
'300000', 'LiMing', '[email protected]'),
(10002, 'Stars', '333 Fromage Lane',
'Dalian', '116000', 'Zhangbo','[email protected]'),
(10003, 'Netbhood', '1 Sunny Place', 'Qingdao', '266000',
'LuoCong', NULL),
(10004, 'JOTO', '829 Riverside Drive', 'Haikou',
'570000', 'YangShan', '[email protected]');
表的内容
查询customers表中c_email为空的记录的c_id、c_name和c_email字段值,
SQL语句如下:
select c_id, c_name,c_email from customers where c_email is null;
select c_id, c_name,c_email from customers where c_email is not null;
可以看到,显示customers 表中字段cemail的值为NULL的记录,
满足查询条件与IS NULL相反的是NOT IS NULL,该关键字查找字段不为空的记录。
总结:
select : 选择哪些列
form : 从哪一个表选择
where : 查询的条件
order by : 按某一列排序
group by : 将某一列相同的行合成一个行
having : 过滤分组
LIMIT + 行数 :查看几行
有时候并不需要返回实际表中的数据,而只是对数据进行总结。 MySQL提供一些查询功能,可以对获取的数据进行分析和报告。这些函数的功能有:
计算数据表中记录行数的总数、 计算某个字段列下数据的总和, 计算表中某个字段下的最大值、最小值或者平均值。这些聚合函数的名称和作用如表7.2所示。
以下数据表为操作对象:
COUNT()函数统计数据表中包含的记录行的总数,或者根据查询结果返回列中包含的
数据行数。
其使用方法有两种:
COUNT(*) 计算表中总的行数,不管某列有数值或者为空值。
COUNT(字段名)计算指定列下总的行数,计算时将忽略空值的行
比如:查询customers表中总的行数,SQL语句如下:
select count(*) as cust_num
from customers;
由查询结果可以看到,COUNT(*)返回 customers 表中记录的总行数,
不管其值是什么返回的总数的名称为custnum。
查询customers表中有电子邮箱的顾客的总数,SQL语句如下:
select count(c_email) as cust_num
from customers;
由查询结果可以看到表中5个customer 只有3个有email,customer的email为空值NULL
的记录 没有被COUNT()函数计算。
两个例子中不同的数值,说明了两种方式在计算总数的时候对待 NULL 值的方式不同。
即指定列的值为空的行被 COUNT()函数忽略,
但是如果不指定列,而在COUNT()函数中使用星号“*”,则所有记录都不忽略。
前面介绍分组查询的时候,介绍了COUNT() 函数与GROUP BY关键字一起使用,
用来计算不同分组中的记录总数。
在orderitems表中,使用COUNT()函数统计不同订单号中订购的水果种类,SQL语句如
下:
select o_num, count(f_id)
from orderitems
group by o_num;
查询结果可以看到,GROUP BY 关键字先按照订单号进行分组,然后计算每个分组中
的总记录数。
SUM()是一个求总和的函数,返回指定列值的总和。
在orderitems表中查询30005号订单一共购买的水果总量,SQL语句如下:
select sum(quantity) as items_total
from orderitems
where o_num = 30005;
由查询结果可以看到,SUM(quantity)函数返回订单中所有水果数量之和,
WHERE子句指定查询的订单号为30005。
SUM()可以与GROUP BY一起使用,来计算每个分组的总和。
在orderitems表中,使用SUM()函数统计不同订单号中订购的水果总量,SQL语句如
下:
select o_num, sum(quantity) as items_total
from orderitems
group by o_num;
由查询结果可以看到,GROUP BY按照订单号onum进行分组,
SUM()函数计算每个分组中订购的水果的总量。SUM()函数在计算时,忽略列值为NULL的行.
AVG()函数通过计算返回的行数和每一行数据的和,求得指定列数据的平均值。
在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一起使用,来计算每个分组的平均值
在fruits表中,查询每一个供应商的水果价格的平均值,SQL语句如下:
select s_id,avg(f_price) as avg_price
from fruits
group by s_id;
GROUP BY 关键字根据s_id字段对记录进行分组,然后计算出每个分组的平均值,
这种分组求平均值的方法非常有用,例如: 求不同班级学生成绩的平均值,
求不同部门工人的平均工资,求各地的年平均气温等等。
AVG()函数使用时,其参数为要计算的列名称,如果要得到多个列的多个平均值,
则需要在每一列上使用AVG()函数。
MAX()返回指定列中的最大值。
在fruits表中查找市场上价格最高的水果,SQL语句如下:
select max(f_price) as max_price
from fruits;
由结果可以看到,MAX()函数查询出了fprice字段的最大值15.70。
MAX()也可以和GROUP BY关键字一起使用,求每个分组中的最大值.
在fruits表中查找不同供应商提供的价格最高的水果,SQL语句如下:
select s_id, max(f_price) as max_price
from fruits
group by s_id;
由结果可以看到,GROUP BY 关键字根据s_id字段对记录进行分组,
然后计算出每个分组中的最大值。
MAX()函数不仅适用于查找数值类型,也可应用于字符类型
在fruits表中查找f_name的最大值,SQL语句如下:
select max(f_name)
from fruits;
由结果可以看到,MAX()函数可以对字母进行大小判断,并返回最大的字符或者字符串
值。
MAX()函数除了用来找出最大的列值或日期值之外,还可以返回任意列中的最大值,
包括返回字符类型的最大值。在对字符类型数据进行比较时,按照字符的 ASCII 码值
大小进行比较,从a~z,a的ASCII 码最小,z的最大。在比较时,先比较第一个字母,如果相等,继续比较下一个字符,一直到两个字符不相等或者字符结束为止。例如,“b’与“t比较时,“t’为最大 值;“bcd”与“bca”比较时,“bcd”为最大值。
原理参考 MAX()函数。
MIN()返回指定列中的最小值。
在fruits表中查找市场上价格最低的水果,SQL语句如下:
select min(f_price) as min_price
from fruits;
由结果可以看到,MIN()函数查询出了fprice字段的最小值2.20。
MIN()也可以和GROUP BY关键字一起使用,求每个分组中的最小值.
在fruits表中查找不同供应商提供的价格最低的水果,SQL语句如下:
select s_id, min(f_price) as min_price
from fruits
group by s_id;
由结果可以看到,GROUP BY 关键字根据s_id字段对记录进行分组,
然后计算出每个分组中的最小值。
MIN()函数不仅适用于查找数值类型,也可应用于字符类型
在fruits表中查找f_name的最大值,SQL语句如下:
select min(f_name)
from fruits;
由结果可以看到,MIN()函数可以对字母进行大小判断,并返回最小的字符或者字符串
值。
连接是关系数据库模型的主要特点。连接查询是关系数据库中最主要的查询,主要包括内连接、外连接等。通过连接运算符可以实现多个表查询。在关系数据库管理系统中,表建立时各数据之间的关系不必确定,常把一个实体的所有信息存放在一个表中。当查询数据时,通过连接操作查询出存放在多个表中的不同实体的信息。当两个或多个表中存在相同意义的字段时,便可以通过这些字段对不同的表进行连接查询。本节将介绍多表之间的内连接查询、外连接查询以及复合条件连接查询。
内连接(INNER JOIN)使用比较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行,组合成新记录,也就是说,在内连接查询中,只有满足条件的记录才能出现在结果关系中。
为了演示的需要,首先创建数据表suppliers,SQL语句如下:
create table suppliers
(
s_id int not null auto_increment,
s_name char(50) not null,
s_city char(50) null,
s_zip char(10) null,
s_call char(50) not null,
primary key(s_id)
) ;
插入需要演示的数据,SQL语句如下:
INSERT INTO suppliers(s_id, s_name,s_city, s_zip, s_call)
VALUES(101,'FastFruit Inc.','Tianjin','300000','48075'),
(102,'LT Supplies','Chongqing','400000','44333'),
(103,'ACME','Shanghai','200000','90046'),
(104,'FNK Inc.','Zhongshan','528437','11111'),
(105,'Good Set','Taiyuang','030000', '22222'),
(106,'Just Eat Ours','Beijing','010', '45678'),
(107,'DK Inc.','Zhengzhou','450000', '33332');
在fruits表和suppliers表之间使用内连接查询。
查询之前,查看两个表的结构:
desc fruits;
desc 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 中,
而另外两个字段在表 supplies 中;同时FROM子句列出了两个表fruits和suppliers。
WHERE子句在这里作为过滤条件,指明只有两个表中的 id 字段值相等的时候才
符合连接查询的条件。从返回的结果可以看到,显示的记录是由两个表中不同列
值组成的新记录。
因为fruits 表和suppliers 表中有相同的字段s_id,因此在比较的时候,需要完全
限定表名(格式为“表名.列名”),
如果只给出s_id,MySQL 将不知道指的是哪一个,并返回错误信息。
下面的内连接查询语句返回与前面完全相同的结果:
在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子句在某些时候
会影响查询的性能。
如果在一个连接查询中,涉及的两个表都是同一个表,这种查询称为自连接查询。
自连接是一种特殊的内连接,它是指相互连接的表在物理上为同一张表,但可以在逻辑上
分为两张表。
查询供应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';
and是为了缩小范围
此处查询的两个表是相同的表,为了防止产生二义性,对表使用了别名,ftuits 表第 1
次
出现的别名为f1,第2次出现的别名为f2,使用SELECT语返回列时明确指出返回以f1为
前缀
的列的全名,WHERE 连接两个表,并按照第2个表的f_id 对数据进行过滤,返回所需
数据。
内连接的三种方法:
外连接查询将查询多个表中相关联的行,内连接时,返回查询结果集合中的仅是符合
查询条件和连接条件的行。但有时候需要包含没有关联的行中数据,即返回查询结果集合中的不仅包含符合连接条件的行,而且还包括左表(左外连接或左连接)、右表(右外连接或右连接) 或两个连接表(全外连接)中的所有数据行。外连接分为左外连接或左连接和右外连接或右连接:
LEFT JOIN(左连接):返回包括左表中的所有记录和右表中连接字段相等的记录
RIGHT JOIN(右连接): 返回包括右表中的所有记录和左表中连接字段相等的记录
左连接的结果包括LEFT OUTER子中指定的左表的所有行,而不仅仅是连接列所匹配的行。
如果左表的某行在右表中没有匹配行,则在相关联的结果行中,右表的所有选择列表列均为空值。
首先创建表orders,SOL语句如下:
CREATE TABLE orders
(
o_num int NOT NULL AUTO_INCREMENT,
o_date datetime NOT NULL,
c_id int NOT NULL,
) ;
插入需要演示的数据,SQL语句如下:
INSERT INTO orders(o_num, o_date, c_id)
VALUES(30001, '2008-09-01', 10001),
(30002, '2008-09-12', 10003),
(30003, '2008-09-30', 10004),
(30004, '2008-10-03', 10005),
(30005, '2008-10-08', 10001);
在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。
右连接是左连接的反向连接,将返回右表的所有行。
如果右表的某行在左表中没有匹配行左表将返回空值。
在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 表中并没有该客户的信息,所以该条记录只取出了 ordes 表中相应的值, 而从customers表中取出的值为空值NULL。
复合条件连接查询是在连接查询的过程中,通过添加过滤条件,限制查询的结果,
使查询的结果更加准确。在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的订单信息,添加了过滤条件之后
返回的结果将会变少,因此返回结果只有两条记录。使用连接查询,并对查询的结果
进行排序。在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 字段进行了升序排序
子查询指一个查询语句嵌套在另一个查询语句内部的查询,
这个特性从MySQL 4.1开始引入。在 SELECT 子句中先计算子查询,子查询结果
作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。子查询中常用
的操作符有ANY(SOME)、ALL、IN、EXISTS子查询可以添加到 SELECT、UPDATE
和DELETE 语句中,而且可以进行多层嵌套。子查询中也可以使用比较运算符,
如“<” 、"<=”、“>” 、“>=”和“!=”等。
ANY和SOME关键字是同义词,表示满足其中任一条件,
它们允许创建一个表达式对子查询的返回值列表进行比较,
只要满足内层子查询中的任何一个比较条件,就返回一个结果作为外层查询的条件。
下面定义两个表tbl1和tbl2:
CREATE table tbl1 ( num1 INT NOT NULL);
CREATE table tbl2 ( num2 INT NOT NULL);
分别向两个表中插入数据:
INSERT INTO tbl1 values(1), (5), (13), (27);
INSERT INTO tbl2 values(6), (14), (11), (20);
ANY关键字接在一个比较操作符的后面,表示若与子查询返回的任何值比较为TRUE,
则返回TRUE。
返回tb12表的所有num2列,然后将tb11中的num1的值与之进行比较,只要大于num2的任何1个值,即为符合查询条件的结果。
select num1
from tbl1
where num1 > any (select num2 from tbl2);
在子查询中,返回的是 tbl2 表的所有 num2列结果(6,14,11,20),然后将 bl1 中的
num1列的值与之进行比较,只要大于num2列的任意一个数即为符合条件的结果。
ALL关键字与ANY和SOME 不同,使用ALL时需要同时满足所有内层查询的条件。
例如,修改前面的例子,用ALL 关键字替换ANY。
ALL 关键字接在一个比较操作符的后面,表示与子查询返回的所有值比较为 TRUE,则
返回TRUE。 返回tbl1表中比tbl2表num2 列所有值都大的值,
SQL语句如下:
select num1
from tbl1
where num1 > all (select num2 from tbl2);
在子查询中,返回的是 tbl2 的所有num2 列结果(6,14,1120),然后将 tbll 中的numl列
的值与之进行比较,大于所有num2列值的num1值只有27,因此返回结果为27。
EXISTS关键字后面的参数是一个任意的子查询,系统对子查询进行运算以判断它是否返回行,如果至少返回一行,那么EXISTS 的结果为true,此时外层查询语句将进行查询;如果子查询没有返回任何行,那么EXISTS返回的结果是false,此时外层语句将不进行查询。
查询suppliers表中是否存在s_id=107的供应商,如果存在,则查询fruits表中的记录,SQL语句如下:
select *
from fruits
where exists
(select s_name from suppliers where s_id = 107);
由结果可以看到,内层查询结果表明suppliers 表中存在sid=107 的记录,
因此EXISTS表达式返回true;外层查询语句接收 true 之后对表fuits进行查询,
返回所有的记录。EXISTS关键字可以和条件表达式一起使用。
查询suppliers表中是否存在s_id=107的供应商,
如果存在,则查询fruits表中的f_price大于10.20的记录,
SQL语句如下:
select * from suppliers;
select *
from fruits
where f_price>10.20 and exists
(select s_name from suppliers where s_id = 107);
由结果可以看到,内层查询结果表明suppliers 表中存在s d=107的记录,因此EXISTS表达式返回 true;外层查询语接收 true 之后根据查询条件f price >10.20对 fruits表进行查询,返回结果为4条f_price大于10.20的记录。
若后面为假
NOT EXISTS与 EXISTS使用方法相同,返回的结果相反。子查询如果至少返回一行,
那么NOT EXISTS的结果为 false,此时外层查询语句将不进行查询;如果子查询没有返回任何行,那么NOT EXISTS返回的结果是true,此时外层语句将进行查询。
查询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的结果只取决于是否会返回行,而不取决于这些行的内容,所以这
个子查询输入列表通常是无关紧要的。
IN 关键字进行子查询时,内层查询语句仅仅返回一个数据列,这个数据列里的值将提供给外层查询语句进行比较操作。
在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';
可以看到,符合条件的o_num列的值有两个:30003和30005,然后执行外层查询,在orders表中查询订单号等于30003或30005的客户c_id。
嵌套子查询语句还可以写为如下形式,实现相同的效果:
select c_id from orders where o_num in (30003, 30005);
这个例子说明在处理SELECT语句的时候,MySQL实际上执行了两个操作过程,
即先执行内层子查询,再执行外层查询,内层子查询的结果作为外部查询的比较条件。
SELECT语句中可以使用NOT IN关键字,其作用与IN正好相反。
与前一个例子类似,但是在SELECT语句中使用NOT IN关键字,SQL语句如下:
select c_id from orders where o_num not in
(select o_num from orderitems where f_id = 'c0');
这里返回的结果有 3条记录,由前面可以看到,子查询返回的订单值有两个,
即 30003和30005,但为什么这里还有值为 10001的c_id 呢?
这是因为cd等于10001的客户的订单不止一个,可以查看订单表orders中的记录。
SELECT * FROM orders;
可以看到,虽然排除了订单号为 30003 和30005 的客户c_id,但是onum为30001的
订单与30005都是10001号客户的订单。所以结果中只是排除了订单号,是仍然有可能选择同一个客户。
子查询的功能也可以通过连接查询完成,但是子查询使得MySQL代码更容易阅读和编写。
在前面介绍的带ANY、ALL 关键字的子查询时使用了“>”比较运算符,子查询时还可以使用其他的比较运算符,如“<”,“<=”,"=",“>=”和“!=”等。
在suppliers表中查询s_city等于“Tianjin”的供应商s_id,然后在fruits表中查询所有该供应商提供的水果的种类,SQL语句如下:
select s_id, f_name
from fruits
where s_id =
(select s1.s_id from suppliers as s1 where s1.s_city = 'Tianjin');
该嵌套查询首先在 suppliers 表中查找s_city 等于 Tianjin 的供应商的s_id,
单独执行子查询查看s_id的值,执行下面的操作过程:
select s1.s_id from suppliers as s1 where s1.s_city = 'Tianjin';
然后在外层查询时,在 fruits 表中查找s_id 等于 101的供应商提供的水果的种类查询
结果表明,“Tianjin”地区的供应商提供的水果种类有3种,分别
为“apple“,"blackberry”,“cherry”
在suppliers表中查询s_city等于“Tianjin”的供应商s_id,
然后在fruits表中查询所有非该供应商提供的水果的种类,SQL语句如下:
select s_id, f_name from fruits
WHERE s_id <>
(select s1.s_id from suppliers as s1 where s1.s_city = 'Tianjin');
该嵌套查询执行过程与前面相同,在这里使用了不等于“<>”运算符,
因此返回的结果和前面正好相反。
利用UNION关键字,可以给出多条SELECT语句,并将它们的结果组合成单个结果集。
合并时,两个表对应的列数和数据类型必须相同。各个 SELECT 语句之间使用UNION或UNION ALL 关键字分隔。UNION 不使用关键字 ALL,执行的时候删除重复的记录,
所有返回的行都是唯一的;使用关键字 ALL 的作用是不删除重复行也不对结果进行自动排序。
基本语法格式如下:
SELECT column,..FROM table1
UNION [ALL]
SELECT column,...FROM table2
举例:查询所有价格小于9的水果的信息,查询s_id等于101和103所有的水果的信息, 使用UNION连接查询结果,SQL语句如下:
select s_id, f_name, f_price
from fruits
where f_price < 9.0
union
select s_id, f_name, f_price
from fruits
where s_id in(101,103);
如前所述,UNION将多个SELECT语句的结果组合成一个结果集合。
可以分开查看每个SELECT语句的结果:
select s_id, f_name, f_price
from fruits
where f_price < 9.0;
select s_id, f_name, f_price
from fruits
where s_id in(101,103);
由分开查询的结果可以看到,第1条SELECT语句查询价格小于9的水果
第2条SELECT语句查询供应商101和103提供的水果 使用UNION将两条SELECT语分隔开执行完毕之后把输出结果组合成单个的结果集,并删除重复的记录。使用UNION ALL包含重复的行,在前面的例子中,分开查询时,两个返回结果中有相同的记录。 UNION 从查询结果集中自动去除了重复的行,如果要返回所有匹配行,而不进行删除,可以使用UNIONALL。 查询所有价格小于9的水果的信息,查询s_id等于101和103的所有水果的信息,
使用UNION ALL连接查询结果,SQL语句如下:
select s_id, f_name, f_price
from fruits
where f_price < 9.0
union all
select s_id, f_name, f_price
from fruits
where s_id in(101,103);
由结果可以看到,这里总的记录数等于两条 SELECT 语句返回的记录数之和,连接查询结果并没有去除重复的行。
UNION和UNION ALL的区别:
使用UNION ALL的功能是不删除重复行,加上ALL关键字语句执行时所需要的资源少,
所以尽可能地使用它,因此知道有重复行但是想保留这些行,确定查询结果中不会有
重复数据或者不需要去掉重复数据的时候,应当使用UNION ALL以提高查询效率。
当表名字很长或者执行一些特殊查询时,为了方便操作或者需要多次使用相同的表时
可以为表指定别名,用这个别名替代表原来的名称。
为表取别名的基本语法格式为:
表名 [AS] 表别名
“表名”为数据库中存储的数据表的名称,
“表别名”为查询时指定的表的新名称,
AS关键字为可选参数。
举例:为orders表取别名o,查询30001订单的下单日期,SQL语句如下:
select *
from orders as o
where o.o_num = 30001;
在这里orders AS o代码表示为orders表取别名为o,指定过滤条件时直接使用o代替
orders,
查询结果如下:
举例:为customers和orders表分别取别名,并进行连接查询,SQL语句如下:
select c.c_id,o.o_num
from customers as c left outer join orders as o
on c.c_id = o.c_id;
由结果看到,MySQL可以同时为多个表取别名,而且表别名可以放在不同的位置,
如WHERE子句、SELECT列表、ON子句以及ORDER BY子句等。
在前面介绍内连接查询时指出自连接是一种特殊的内连接,
在连接查询中的两个表都是同一个表,其查询语句如下:
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';
在这里,如果不使用表别名,MySOL 将不知道引用的是哪个 fiuits 表实例,
这是表别名的一个非常有用的地方。
在本章和前面各章节的例子中可以看到,在使用SELECT 语句显示查询结果时,
MySQL会显示每个 SELECT 后面指定的输出列,在有些情况下,显示的列的名称会很
长或者名称不够直观,MySQL可以指定列别名,替换字段或表达式。
为字段取别名的基本语法格式为:
列名 [AS] 列别名
“列名”为表中字段定义的名称,
“列别名”为字段新的名称,
AS关键字为可选参数
举例1:查询fruits表,为f_name取别名fruit_name,f_price取别名fruit_price,
为fruits表取别名f1,查询表中f_price < 8的水果的名称,SQL语句如下:
select f1.f_name as fruit_name, f1.f_price as fruit_price
from fruits as f1
where f1.f_price < 8;
也可以为 SELECT子句中的计算字段取别名,
例如,对使用 COUNT 聚合函数或者CONCAT等系统函数执行的结果字段取别名。
举例2:查询suppliers表中字段s_name和s_city,使用CONCAT函数连接这两个字段值,
并取列别名为suppliers_title。如果没有对连接后的值取别名,其显示列名称将会不够直
观,
SQL语句如下:
select concat(trim(s_name) , ' (', trim(s_city), ')')
from suppliers
order by s_name;
由结果可以看到,显示结果的列名称为SELECT子句后面的计算字段,
实际上计算之后的列是没有名字的,这样的结果让人很不容易理解,如果为字段取一个
别名,
将会使结果清晰,SQL语句如下,
select concat(trim(s_name) , ' (', trim(s_city), ')')
as suppliers_title
from suppliers
order by s_name;
由结果可以看到,SELECT子句计算字段值之后增加了AS suppliers title,
它指示MySQL为计算字段创建一个别名suppliers title,显示结果为指定的列别名,
这样就增强了查询结果的可读性。
正则表达式通常被用来检索或替换那些符合某个模式的文本内容,
根据指定的匹配模式匹配文本中符合要求的特殊字符串。例如从一个文本文件中提取
电话号码, 查找一篇文章中重复的单词或者替换用户输入的某些敏感词语等等,这些地方都可以使用正则表达式。正则表达式强大而且灵活,可以应用于非常复杂的查询。
MSQL中使用REGEXP关键字指定正则表达式的字符匹配模式
字符 “^” 匹配以特定字符或者字符串开头的文本
举例:在fruits表中,查询f_name字段以字母’b’开头的记录,SQL语句如下:
select *
from fruits
where f_name regexp '^b';
fruits表中有3条记录的f_name字段值是以字母b开头,返回结果有3条记录。
举例2:在fruits表中,查询f_name字段以“be”开头的记录,SQL语句如下:
select *
from fruits
where f_name regexp '^be';
字符“$’匹配以特定字符或者字符串结尾的文本
举例:在fruits表中,查询f_name字段以字母’y’结尾的记录,SQL语句如下:
select *
from fruits
where f_name regexp 'y$';
fruits表中有4条记录的f_name字段值是以字母’y’结尾,返回结果有4条记录。
举例2:在fruits表中,查询f_name字段以字符串“rry”结尾的记录,SQL语句如下:
select *
from fruits
where f_name regexp 'rry$';
fruits表中有3条记录的f_name字段值是以字符串“rry”结尾,返回结果有3条记录。
字符'.'匹配任意一个字符
举例:在fruits表中,查询f_name字段值包含字母’a’与’g’且两个字母之间只有一个字母的记录,
SQL语句如下,
select *
from fruits
where f_name regexp 'a.g';
查询语句中“a.g’指定匹配字符中要有字母a和g,且两个字母之间包含单个字符,
并不限定匹配的字符的位置和所在查询字符串的总长度,因此orange和 mango 都符合匹配条件。
同时满足两个要求的写法
select *
from fruits
where f_name regexp 'a.g' and f_name regexp '^m' ;
星号’*’匹配前面的字符任意多次,包括0次。加号“+’匹配前面的字符至少一次。
举例:在fruits表中,查询f_name字段值以字母’b’开头,且’b’后面出现字母’a’的记录,
SQL语句如下:
select *
from fruits
where f_name regexp '^ba*';
星号’*’可以匹配任意多个字符,blackberry和berry中字母b后面并没有出现字母a,
但是也满足匹配条件。
在fruits表中,查询f_name字段值以字母’b’开头,且’b’后面出现字母’a’至少一次的记录,
SQL语句如下:
select *
from fruits
where f_name regexp '^ba+';
‘a+’匹配字母’a’至少一次,只有banana满足匹配条件。
正则表达式可以匹配指定字符串,只要这个字符串在查询文本中即可,
如要匹配多个字符串,多个字符串之间使用分隔符‘|’隔开。
举例:在fruits表中,查询f_name字段值包含字符串“on”的记录,SQL语句如下:
select *
from fruits
where f_name regexp 'on';
可以看到,f_name字段的melon、lemon和coconut3个值中都包含有字符串“on”,满 足匹配条件。 再举例:在fruits表中,查询f_name字段值包含字符串“on”或者“ap”的记录,SQL语句如下:
select *
from fruits
where f_name regexp 'on|ap';
可以看到,f_name字段的melon、lemon 和coconut3 个值中都包含有字符串“on”,
apple和apricot值中包含字符串“ap”,满足匹配条件。
等价于
select *
from fruits
where f_name regexp 'on' or f_name regexp 'ap';
之前介绍过,LIKE 运算符也可以匹配指定的字符串,但与REGEXP 不同,
LIKE匹配的字符串如果在文本中间出现,则找不到它,相应的行也不会返回。
而 REGEXP 在文本内进行匹配,如果被匹配的字符串在文本中出现,REGEXP 将会找到
它.
举例:在fruits表中,使用LIKE运算符查询f_name字段值为“on”的记录,SQL语句如下:
select *
from fruits
where f_name like 'on';
select *
from fruits
where f_name like '%on%';
方括号“[ ]”指定一个字符集合,只匹配其中任何一个字符,即为所查找的文本
举例:在fruits表中,查找f_name字段中包含字母’o’或者’t’的记录,SQL语句如下:
select *
from fruits
where f_name regexp '[ot]';
查询结果可以看到,所有返回的记录的f_name字段的值中都包含有字母o或者t,或者
两个都有。
方括号“[]”还可以指定数值集合。
举例:在fruits表,查询s_id字段中数值中包含4、5或者6的记录,SQL语句如下:
select *
from fruits
where s_id regexp '[456]';
查询结果中,s_id字段值中有3个数字中的1个即为匹配记录字段。
匹配集合“[456]”也可以写成“[4-6]”即指定集合区间。例如“[a-z]”表示集合区间为从a~z的子母,“[0-9]”表示集合区间为所有数字。
“[^字符集合]” 匹配不在指定集合中的任何字符。
举例1:在fruits表中,查询f_id字段包含字母a~e和数字1~2以外的字符的记录,SQL
语句如下:
select *
from fruits
where f_id regexp '[^a-e1-2]';
返回记录中的f_id 字段值中包含了指定字母和数字以外的值,如 s、m、o、t等,
这些字母均不在a~e或1~2之间,满足匹配条件。
“字符串{n,}”表示至少匹配 n 次前面的字符;“字符串{n,m}”表示匹配前面的字符串不
少于n次, 不多于 m 次。例如,a{2,}表示字母 a 连续出现至少2次,也可以大于2次;
a{2,4}表示字母a连续出现最少2次,最多不能超过4次。
举例:在fruits表中,查询f_name字段值出现字母’x’至少2次的记录,SQL语句如下:
select *
from fruits
where f_name regexp 'x{2,}';
可以看到,f_name字段的“xxxx”包含了4个字母’x’,“xxtt”包含两个字母’x’,
均为满足匹配条件的记录。
举例2:在fruits表中,查询f_name字段值出现字符串“ba”最少1次,最多3次的记录, SQL语句如下:
select *
from fruits
where f_name regexp 'ba{1,3}';
可以看到,f_name字段的xbabay值中“ba”出现了2次,banana中出现了1次,
xbababa中出现了3次,都满足匹配条件的记录。
创建如下表结构以及插入部分实验数据:
(1)计算所有女员工(’F’)的工龄。
select e_name,year(curDate())-year(hireDate)
from employee
where e_gender = 'f' ;
(2)使用LIMIT查询从第3条记录开始到第6条记录。
select *
from employee
limit 2,4;
(3)查询销售人员(SALESMAN)的最低工资。
select min(e_salary) as min_salary
from employee
where e_gender = 'f' ;
(4)查询名字以字母N或者 S结尾的记录
select *
from employee
where e_name regexp 'N$' or e_name regexp 'S$' ;
(5)查询在Beijing工作的员工的姓名和职务
select employee.e_name,employee.e_job,dept.d_location
from employee left outer join dept
on dept.d_location = 'Beijing' and employee.dept_no = dept.d_no;
(6)使用左连接方式查询employee和dept表
select *
from employee left outer join dept
on employee.dept_no = dept.d_no;
(7)查询所有2001~2005年入职的员工的信息,查询部门编号为20和30的员工信息并使用UNION合并两个查询结果。
select e_name,dept_no
from employee
where year(hireDate)>=2001 and year(hireDate)<=2005
union all
select d_name,d_no
from dept
where d_no>=20 and d_no<=30 ;
或者
select e_name,dept_no
from employee
where hireDate between '2001-01-01' and '2005-12-31'
union
select d_name,d_no
from dept
where d_no in (20,30);
(8)使用LIKE查询员工姓名中包含字母a的记录。
select e_name
from employee
where e_name like '%b%';
(9)使用REGEXP查询员工姓名中包含T或C或M3个字母中任意1个的记录
select e_name
from employee
where e_name regexp '[TCM]';
CREATE TABLE dept
(
d_no INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
d_name VARCHAR(50),
d_location VARCHAR(100)
);
CREATE TABLE employee
(
e_no INT NOT NULL PRIMARY KEY,
e_name VARCHAR(100) NOT NULL,
e_gender CHAR(2) NOT NULL,
dept_no INT NOT NULL,
e_job VARCHAR(100) NOT NULL,
e_salary SMALLINT NOT NULL,
hireDate DATE,
CONSTRAINT dno_fk FOREIGN KEY(dept_no)
REFERENCES dept(d_no)
);
INSERT INTO dept
VALUES (10, 'ACCOUNTING', 'ShangHai'),
(20, 'RESEARCH ', 'BeiJing '),
(30, 'SALES ', 'ShenZhen '),
(40, 'OPERATIONS ', 'FuJian ');
INSERT INTO employee
VALUES (1001, 'SMITH', 'm',20, 'CLERK',800,'2005-11-12'),
(1002, 'ALLEN', 'f',30, 'SALESMAN', 1600,'2003-05-12'),
(1003, 'WARD', 'f',30, 'SALESMAN', 1250,'2003-05-12'),
(1004, 'JONES', 'm',20, 'MANAGER', 2975,'1998-05-18'),
(1005, 'MARTIN', 'm',30, 'SALESMAN', 1250,'2001-06-12'),
(1006, 'BLAKE', 'f',30, 'MANAGER', 2850,'1997-02-15'),
(1007, 'CLARK', 'm',10, 'MANAGER', 2450,'2002-09-12'),
(1008, 'SCOTT', 'm',20, 'ANALYST', 3000,'2003-05-12'),
(1009, 'KING', 'f',10, 'PRESIDENT', 5000,'1995-01-01'),
(1010, 'TURNER', 'f',30, 'SALESMAN', 1500,'1997-10-12'),
(1011, 'ADAMS', 'm',20, 'CLERK', 1100,'1999-10-05'),
(1012, 'JAMES', 'm',30, 'CLERK', 950,'2008-06-15');