大数据兼云计算(王明龙)讲师-MYSQL-DAY09-查询语句

**

查询语句

**


查询的整体语法
SELECT 属性列表   
  FROM 表名和视图列表 
  [WHERE 条件表达式 1] 
  [GROUP BY 属性名 1 [HAVING条件表达式2]] 
  [ORDER BY 属性名2[ASC|DESC]]


基本查询

SELECT 字段名1,字段名2,…,字段名n FROM 表名 WHERE 查询条件;

下面以一个例子说明如何使用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;

【例7.1】从fruits表中检索所有字段的数据,SQL语句如下:
 SELECT * FROM fruits;

【例7.2】查询fruits表中f_name列所有水果名称,SQL语句如下:
SELECT f_name FROM fruits;
该语句使用SELECT声明从fruits表中获取名称为f_name字段下的所有水果名称,指定字段的名称紧跟在SELECT关键字之后,查询结果如下:
 SELECT f_name FROM fruits;

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


查询指定记录 , where关键字

语法:
select 字段名1,字段名2,… from 表 where 查询条件;


举例:

查询价格为10.2元的水果的名称,SQL语句如下:
SELECT f_name, f_price FROM fruits WHERE f_price = 10.2;

查找名称为“apple”的水果的价格,SQL语句如下:
SELECT f_name, f_price FROM fruits WHERE f_name = 'apple';

查询价格小于10的水果的名称,SQL语句如下:
SELECT f_name, f_price FROM fruits WHERE f_price < 10;

查询满足条件记录,in ()
语法:
where 字段名 in (记录1,记录2,....)

查询不满足条件以外记录,not in ()
语法:
where 字段名 not in (记录1,记录2,....)

例1,s_id为101和102的记录,SQL语句如下:
SELECT s_id,f_name, f_price 
FROM fruits 
WHERE s_id IN (101,102) 
ORDER BY f_name;

例2,查询所有s_id不等于101也不等于102的记录,SQL语句如下:
SELECT s_id,f_name, f_price
FROM fruits
WHERE s_id NOT IN (101,102);

带BETWEEN AND的范围查询
查询某个范围内的值,该操作符需要两个参数,即范围的开始值和结束值,如果字段值满足指定的范围查询条件,则这些记录被返回。

1.查询在 某记录 与 某记录 之间 的记录
where 字段名 between 记录 and 记录

【例7.9】查询价格在2.00元到10.20元之间的水果名称和价格,SQL语句如下:
SELECT f_name, f_price FROM fruits WHERE f_price BETWEEN 2.00 AND 10.20;
查询结果如下:
 SELECT f_name, f_price
     FROM fruits
     WHERE f_price BETWEEN 2.00 AND 10.20;

2.查询在 某记录 与 某记录 之外 的记录
where 字段名 not between 记录 and 记录

【例7.10】查询价格在2.00元到10.20元之外的水果名称和价格,SQL语句如下:
SELECT f_name, f_price
FROM fruits 
WHERE f_price NOT BETWEEN 2.00 AND 10.20;

模糊查询
where 字段名 like '要查询内容'

% :匹配所有字符,包括零字符
_  :匹配单个字符

【例7.11】查找所有以'b'字母开头的水果,SQL语句如下:
SELECT f_id, f_name
FROM fruits
WHERE f_name LIKE 'b%';

【例7.12】在fruits表中,查询f_name中包含字母'g'的记录,SQL语句如下:
SELECT f_id, f_name
FROM fruits
WHERE f_name LIKE '%g%';

【例7.13】查询以'b'开头,并以'y'结尾的水果的名称,SQL语句如下:
SELECT f_name
FROM fruits
WHERE f_name LIKE 'b%y';

【例7.14】在fruits表中,查询以字母'y'结尾,且'y'前面只有4个字母的记录,SQL语句如下:
SELECT f_id, f_name FROM fruits WHERE f_name LIKE '____y';

下面,在数据库中创建数据表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]');

SELECT COUNT(*) AS cust_num  FROM customers;


查询为空记录
where 字段名 is null

【例7.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;

查询结果如下:
+---------+---------------+-----------------+
| c_id    | c_name    |     c_email  |
+---------+---------------+-----------------+
| 10003 | Netbhood |        NULL  |
+---------+---------------+-----------------+
1 row in set (0.00 sec)

查询不为空记录
where 字段名 not null

【例7.16】查询customers表中c_email不为空的记录的c_id、c_name和c_email字段值,SQL语句如下:
SELECT c_id, c_name,c_email FROM customers WHERE c_email IS NOT NULL;

带AND的多条件查询
使用AND连接两个甚至多个查询条件,多个条件表达式之间用AND分开。

语法: where 字段名 条件1 and 条件2 .....and 条件n

【例7.17】在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;

【例7.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';

带OR的多条件查询
OR操作符,表示只需要满足其中一个条件的记录即可返回。OR也可以连接两个甚至多个查询条件,多个条件表达式之间用AND分开。

语法: where 条件1 or 条件2

【例7.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;
查询结果如下:
 SELECT s_id,f_name, f_price
     FROM fruits
     WHERE s_id = 101 OR s_id = 102;

【例7.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);
查询结果如下:
 SELECT s_id,f_name, f_price
     FROM fruits
     WHERE s_id IN(101,102);


去重查询
select distinct 字段 from 表名;

【例7.21】查询fruits表中s_id字段的值,返回s_id字段值且不得重复,SQL语句如下:
SELECT DISTINCT s_id FROM fruits;



升序与降序
order by 字段名
order by 字段名 desc

【例7.22】查询fruits表的f_name字段值,并对其进行排序,SQL语句如下:
SELECT f_name FROM fruits ORDER BY f_name;

【例7.23】查询fruits表中的f_name和f_price字段,先按f_name排序,再按f_price排序,SQL语句如下:
SELECT f_name, f_price FROM fruits ORDER BY f_name, f_price;

【例7.24】查询fruits表中的f_name和f_price字段,对结果按f_price降序方式排序,SQL语句如下:
SELECT f_name, f_price FROM fruits ORDER BY f_price DESC;

【例7.25】查询fruits表,先按f_price降序排序,再按f_name字段升序排序,SQL语句如下:
SELECT f_price, f_name FROM fruits ORDER BY f_price DESC, f_name;


分组查询
分组查询是对数据按照某个或多个字段进行分组。
group by语法可以根据给定数据列的每个成员对查询结果进行分组统计,最终得到一个分组汇总表。

1.单例分组
group by 字段1

【例7.26】根据s_id对fruits表中的数据进行分组,SQL语句如下:
SELECT s_id, COUNT(*) AS Total FROM fruits GROUP BY s_id;

【例7.27】根据s_id对fruits表中的数据进行分组,将每个供应商的水果名称显示出来,SQL语句如下:
SELECT s_id, GROUP_CONCAT(f_name) AS Names FROM fruits GROUP BY s_id;

2.使用having过滤分组
group by  字段 having 条件表达式

【例7.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;

3.在 group by 子句中使用 with rollup
可以检索出更多的分组聚合信息,不仅能像一般的 group by 那样检索出各组的聚合信息,还能检索出本组类的整体聚合信息, group by 使用了with rollup,就不能再使用 order by 对结果集进行排序

语法:   group by 字段1,字段2,.... with rollup;

【例7.29】根据s_id对fruits表中的数据进行分组,并显示记录数量,SQL语句如下:
SELECT s_id, COUNT(*) AS Total 
FROM fruits 
GROUP BY s_id WITH ROLLUP;

4.多字段分组
group by 字段1,字段2,...

【例7.30】根据s_id和f_name字段对fruits表中的数据进行分组, SQL语句如下,
SELECT * FROM fruits group by s_id,f_name;

5.GROUP BY和ORDER BY一起使用
group by 字段1 [,字段2,... [with rollup] having 限制条件] order by 字段1,....字段n

为了演示效果,首先创建数据表,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)
) ;

解析:decimal(8,2)     8是长度,2是小数点取2位



然后插入演示数据。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);

【例7.31】查询订单价格大于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;



SELECT子句中的列名必须为分组列或列函数。列函数对于GROUP BY子句定义的每个组各返回一个结果。
某个员工信息表结构和数据如下:
  id  name  dept  salary  edlevel  hiredate 
      1 张三 开发部 2000 3 2009-10-11
      2 李四 开发部 2500 3 2009-10-01
      3 王五 设计部 2600 5 2010-10-02
      4 王六 设计部 2300 4 2010-10-03
      5 马七 设计部 2100 4 2010-10-06
      6 赵八 销售部 3000 5 2010-10-05
      7 钱九 销售部 3100 7 2010-10-07
      8 孙十 销售部 3500 7 2010-10-06 

例如,我想列出每个部门最高薪水的结果,sql语句如下:
SELECT DEPT, MAX(SALARY) AS MAXIMUM
FROM STAFF
GROUP BY DEPT

查询结果如下:
      DEPT  MAXIMUM 
      开发部 2500
      设计部 2600
      销售部 3500

解释一下这个结果:
1、满足“SELECT子句中的列名必须为分组列或列函数”,因为SELECT有GROUP BY DEPT中包含的列DEPT。
2、“列函数对于GROUP BY子句定义的每个组各返回一个结果”,根据部门分组,对每个部门返回一个结果,就是每个部门的最高薪水。

注意:计算的是每个部门(由 GROUP BY 子句定义的组)而不是整个公司的 MAX(SALARY)。

例如,查询每个部门的总的薪水数
SELECT DEPT, sum( SALARY ) AS total
FROM STAFF
GROUP BY DEPT

查询结果如下:
DEPT  total 
开发部 4500
设计部 7000
销售部 9600

将 WHERE 子句与 GROUP BY 子句一起使用分组查询可以在形成组和计算列函数之前具有消除非限定行的标准 WHERE 子句。必须在GROUP BY 子句之前指定 WHERE 子句。

例如,查询公司2010年入职的各个部门每个级别里的最高薪水
SELECT DEPT, EDLEVEL, MAX( SALARY ) AS MAXIMUM
FROM staff
WHERE HIREDATE > '2010-01-01'
GROUP BY DEPT, EDLEVEL
ORDER BY DEPT, EDLEVEL

查询结果如下:
  DEPT  EDLEVEL  MAXIMUM 
      设计部 4 2300
      设计部 5 2600
      销售部 5 3000
      销售部 7 3500

注意:在SELECT语句中指定的每个列名也在GROUP BY子句中提到。未在这两个地方提到的列名将产生错误。
GROUP BY子句对DEPT和EDLEVEL的每个唯一组合各返回一行。

在GROUP BY子句之后使用HAVING子句,可应用限定条件进行分组,以便系统仅对满足条件的组返回结果。
为此,在GROUP BY子句后面包含一个HAVING子句。HAVING子句可包含一个或多个用AND和OR连接的谓词。每个谓词将组特性(如AVG(SALARY))与下列之一进行比较:

例如:寻找雇员数超过2个的部门的最高和最低薪水:
SELECT DEPT, MAX( SALARY ) AS MAXIMUM, MIN( SALARY ) AS MINIMUM
FROM staff
GROUP BY DEPT
HAVING COUNT( * ) >2
ORDER BY DEPT

查询结果如下:
  DEPT  MAXIMUM  MINIMUM 
      设计部 2600 2100
      销售部 3500 3000

例如:寻找雇员平均工资大于3000的部门的最高和最低薪水:
SELECT DEPT, MAX( SALARY ) AS MAXIMUM, MIN( SALARY ) AS MINIMUM
FROM staff
GROUP BY DEPT
HAVING AVG( SALARY ) >3000
ORDER BY DEPT

查询结果如下:
  DEPT  MAXIMUM  MINIMUM 
      销售部 3500 3000








用LIMIT限制查询结果的数量

LIMIT关键字可以返回指定位置的记录

语法: limit [位置偏移量,] 行数

位置偏移量:就是显示指定以下的内容,包括其本身,起始计算行永远从0开始,所以你定为4,就是从第3行开始算起

查询首行以下,多少行内容
语法:limit 行数

【例7.32】显示fruits表查询结果的前4行,SQL语句如下:
SELECT * From fruits LIMIT 4;

查询指定行,以下多少行内容
语法:limit 指定行,要显示指定行以下的多少行
   如:limit 4,3

【例7.33】在fruits表中,使用LIMIT子句,返回从第5个记录开始的,行数长度为3的记录,SQL语句如下:
SELECT * From fruits LIMIT 4, 3;           //虽然位置偏移量设置为4,但会显示第3行以下3行的内容,就是因为位置偏移量永远从0算起


使用集合函数查询

COUNT()函数
SUM()函数
AVG()函数
MAX()函数
MIN()函数

COUNT()函数
统计数据表中包含的记录行的总数,或者根据查询结果返回的列中包含的数据行数。

COUNT(*)                                 //统计所有列
COUNT(字段1,字段2,...字段n)       //统计指定列

【例7.34】查询customers表中总的行数,SQL语句如下:
 SELECT COUNT(*) AS cust_num 
     FROM customers;

【例7.35】查询customers表中有电子邮箱的顾客的总数,SQL语句如下:
 SELECT COUNT(c_email) AS email_num
     FROM customers;

【例7.36】在orderitems表中,使用COUNT()函数统计不同订单号中订购的水果种类,SQL语句如下:
 SELECT o_num, COUNT(f_id)
     FROM orderitems 
     GROUP BY o_num;

SUM()函数
SUM()是一个求总和的函数,返回指定列值的总和。

SUM(字段)       //求指定列总和

【例7.37】在orderitems表中查询30005号订单一共购买的水果总量,SQL语句如下:
 SELECT SUM(quantity) AS items_total
     FROM orderitems
     WHERE o_num = 30005;
【例7.38】在orderitems表中,使用SUM()函数统计不同订单号中订购的水果总量,SQL语句如下:
 SELECT o_num, SUM(quantity) AS items_total
     FROM orderitems
     GROUP BY o_num;

AVG()函数
AVG()函数通过计算返回的行数和每一行数据的和,求得指定列数据的平均值。

AVG(字段)    //求指定列平均值

【例7.39】在fruits表中,查询s_id=103的供应商的水果价格的平均值,SQL语句如下:
 SELECT AVG(f_price) AS avg_price
     FROM fruits
     WHERE s_id = 103;
【例7.40】在fruits表中,查询每一个供应商的水果价格的平均值,SQL语句如下:
 SELECT s_id,AVG(f_price) AS avg_price
      FROM fruits
      GROUP BY s_id;

MAX()函数
MAX()返回指定列中的最大值

MAX(字段)    //求指定列最大值

【例7.41】在fruits表中查找市场上价格最高的水果,SQL语句如下:
SELECT MAX(f_price) AS max_price FROM fruits;

【例7.42】在fruits表中查找不同供应商提供的价格最高的水果,SQL语句如下:
 SELECT s_id, MAX(f_price) AS max_price
      FROM fruits
 GROUP BY s_id;

【例7.43】在fruits表中查找f_name的最大值,SQL语句如下:
 SELECT MAX(f_name) FROM fruits;

MIN()函数
MIN()返回查询列中的最小值

MIN(字段)     //求指定列最小值

【例7.44】在fruits表中查找市场上价格最低的水果,SQL语句如下:
SELECT MIN(f_price) AS min_price FROM fruits;
【例7.45】在fruits表中查找不同供应商提供的价格最低的水果,SQL语句如下:
  SELECT s_id, MIN(f_price) AS min_price
     FROM fruits
     GROUP BY s_id;



连接查询

1.内连接查询
2.外连接查询
3.复合条件连接查询

语法:
SELECT 属性名列表  FROM 表名1 LEFT|RIGHT JOIN 表名 2 ON 表名1.属性 1=表名2.属性 2; 

解析:
LEFT JOIN    左表全记录,右表符合条件 
RIGHT 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');

1.内连接查询
内连接(INNER JOIN)使用比较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行,组合成新的记录,也就是说,在内连接查询中,只有满足条件的记录才能出现在结果关系中。

SELECT 字段 FROM 表1 INNER JOIN 表2 ON 匹配条件;

【例7.46】在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;

【例7.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;

【例7.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';


2.外连接查询
返回到查询结果集合中的不仅包含符合连接条件的行,而且还包括左表(左外连接或左连接)、右表(右外连接或右连接)或两个边接表(全外连接)中的所有数据行。

1) left join              (左连接,是left outer join的简写) 
2) left outer join     (左外连接) 

3) right join           (右连接,是right outer join的简写) 
4) right outer join  (右外连接) 


1) left join   (左连接) 
左连接的结果包括 left outer 子句中指定的左表的所有行,而不仅仅是连接列所匹配的行。如果左表的某行在右表中没有匹配行,则在相关联的结果行中,右表的所有选择列表列均为空值。

语法: select 字段 from 表1 left join 表2 on 匹配条件;

首先创建表orders,SQL语句如下:
CREATE TABLE orders
(
  o_num  int      NOT NULL AUTO_INCREMENT,
  o_date datetime NOT NULL,
  c_id   int      NOT NULL,
  PRIMARY KEY (o_num)
) ;

插入需要演示的数据,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);

【例7.49】在customers表和orders表中,查询所有客户,包括没有订单的客户,

SQL语句如下:
 SELECT customers.c_id, orders.o_num
     FROM customers LEFT JOIN orders
     ON customers.c_id = orders.c_id;

显示结果: 
+-------+-------+
| c_id  | o_num |
+-------+-------+
| 10001 | 30001 |
| 10003 | 30002 |
| 10004 | 30003 |
| 10001 | 30005 |
| 10002 |  NULL |
+-------+-------+

2) left outer join     (左外连接) 
语法: select 字段 from 表1 left outer join 表2 on 匹配条件;

【例7.49】在customers表和orders表中,查询所有客户,包括没有订单的客户,

SQL语句如下:
 SELECT customers.c_id, orders.o_num
     FROM customers LEFT OUTER JOIN orders
     ON customers.c_id = orders.c_id;

显示结果: 
+-------+-------+
| c_id  | o_num |
+-------+-------+
| 10001 | 30001 |
| 10003 | 30002 |
| 10004 | 30003 |
| 10001 | 30005 |
| 10002 |  NULL |
+-------+-------+

3) right join           (右连接) 
右连接是左连接的反向连接,将返回右表的所有行。如果右表的某行在左表中没有匹配行,左表将返回空值。

语法: select 字段 from 表1 right join 表2 on 匹配条件;

【例7.50】在customers表和orders表中,查询所有订单,包括没有客户的订单,

SQL语句如下:
SELECT customers.c_id, orders.o_num
     FROM customers RIGHT JOIN orders
     ON customers.c_id = orders.c_id;

显示结果:
+-------+-------+
| c_id  | o_num |
+-------+-------+
| 10001 | 30001 |
| 10003 | 30002 |
| 10004 | 30003 |
|  NULL | 30004 |
| 10001 | 30005 |
+-------+-------+

4) right outer join  (右外连接) 
语法: select 字段 from 表1 right outer join 表2 on 匹配条件;

【例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;

显示结果:
+----------+----------+
| c_id    | o_num |
+----------+----------+
| 10001 | 30001 |
| 10003 | 30002 |
| 10004 | 30003 |
|  NULL | 30004  |
| 10001 | 30005 |
+-------+-------------+


3.复合条件连接查询
复合条件连接查询是在连接查询的过程中,通过添加过滤条件,限制查询的结果。

select 字段 from 表1 inner join 表2 on 条件1 and 条件2;

【例7.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;

【例7.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;


子查询
带ANY、SOME关键字的子查询
带ALL关键字的子查询
带EXISTS关键字的子查询
带IN关键字的子查询
带比较运算符的子查询
合并查询结果


带ANY、SOME关键字的子查询
-----------------------------------------
ANY和SOME关键字是同义词,表示满足其中任一条件,它们允许创建一个表达式对子查询的返回值列表进行比较,只要满足内层子查询中的任何一个比较条件,就返回一个结果作为外层查询的条件。
ANY关键字接在一个比较操作符的后面,表示若与子查询返回的任何值比较为TRUE,则返回TRUE。

语法:  select 字段 from 表1 匹配条件  比较条件 any (select 字段 from 匹配条件);

下面定义两个表tb1和tb2:
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);

【例7.53】返回tbl2表的所有num2列,然后将tbl1中的num1的值与之进行比较,只要大于num2的任何1个值,即为符合查询条件的结果。
SELECT num1 FROM tbl1 WHERE num1 > ANY (SELECT num2 FROM tbl2);

带ALL关键字的子查询
ALL关键字与ANY和SOME不同,使用ALL时需要同时满足所有内层查询的条件。

语法: select 语句 比较条件 all (select语句);

【例7.54】返回tbl1表中比tbl2表num2 列所有值都大的值,SQL语句如下:
 SELECT num1 FROM tbl1 WHERE num1 > ALL (SELECT num2 FROM tbl2);


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

语法:select 字段 from 表1 where exists (select 字段 from 表2 匹配条件);
         select 字段 from 表1 where 条件表达式 exists (select 字段 from 表2 匹配条件);

【例7.55】查询suppliers表中是否存在s_id=107的供应商,如果存在,则查询fruits表中的记录,SQL语句如下:
 SELECT * FROM fruits
     WHERE EXISTS
     (SELECT s_name FROM suppliers WHERE s_id = 107);

【例7.56】查询suppliers表中是否存在s_id=107的供应商,如果存在,则查询fruits表中的f_price大于10.20的记录,SQL语句如下:
 SELECT * FROM fruits
     WHERE f_price>10.20 AND EXISTS
     (SELECT s_name FROM suppliers WHERE s_id = 107);

【例7.57】查询suppliers表中是否存在s_id=107的供应商,如果不存在则查询fruits表中的记录,SQL语句如下:
 SELECT * FROM fruits
     WHERE NOT EXISTS
     (SELECT s_name FROM suppliers WHERE s_id = 107);

带IN关键字的子查询
IN关键字进行子查询时,内层查询语句仅仅返回一个数据列,这个数据列里的值将提供给外层查询语句进行比较操作。

语法:select * from 表1 匹配条件 in (select 字段 from 表2 匹配条件);
         select * from 表1 匹配条件 not in (select 字段 from 表2 匹配条件);

【例7.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';

单独执行子查询语句,可以看到符合条件的o_num列的值有两个:30003和30005,然后执行外层查询,在orders表中查询订单号等于30003或30005的客户c_id是多少。

嵌套子查询语句还可以写为如下形式,实现相同的效果:
SELECT c_id FROM orders WHERE o_num IN (30003, 30005);


【例7.59】与前一个例子类似,但是在SELECT语句中使用NOT IN关键字,SQL语句如下:
SELECT c_id FROM orders WHERE o_num NOT IN
     (SELECT o_num  FROM orderitems WHERE f_id = 'c0');


带比较运算符的子查询
select 语句 比较运算符 (select 语句);

【例7.60】在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');

【例7.61】在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关键字分隔。

语法:  select 语句 union all select 语句;

【例7.63】查询所有价格小于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);




如前所述,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);







为表和字段取别名

为表取别名
为了方便操作或者需要多次使用相同的表时,可以为表指定别名,用这个别名替代表原来的名称。

语法:  表名 [AS] 表别名

【例7.64】为orders表取别名o,查询30001订单的下单日期,SQL语句如下:
SELECT * FROM orders AS o 
WHERE o.o_num = 30001;

在这里orders AS o代码表示为orders表取别名为o,指定过滤条件时直接使用o代替orders,查询结果如下:
+-------+---------------------+-------+
| o_num | o_date        | c_id  |
+-------+---------------------+-------+
| 30001 | 2008-09-01 00:00:00 | 10001 |
+-------+---------------------+-------+

【例7.65】为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;
+-------+-------+
| c_id  | o_num |
+-------+-------+
| 10001 | 30001 |
| 10001 | 30005 |
| 10002 | NULL |
| 10003 | 30002 |
| 10004 | 30003 |
+-------+-------+

由结果看到,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';
+------+------------+
| f_id | f_name   |
+------+------------+
| a1   | apple    |
| b1   | blackberry |
| c0   | cherry   |
+------+------------+


为字段取别名
MySQL可以指定列别名,替换字段或表达式。

语法:  列名 [AS] 列别名

【例7.66】查询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;

【例7.67】查询suppliers表中字段s_name和s_city,使用CONCAT函数连接这两个字段值,并取列别名为suppliers_title。
如果没有对连接后的值取别名,其显示列名称将会不够直观,

SQL语句如下:
 SELECT CONCAT(TRIM(s_name) , ' (',  TRIM(s_city), ')')
      FROM suppliers
      ORDER BY s_name;
+--------------------------------------------------------------+
| CONCAT(TRIM(s_name) , ' (',  TRIM(s_city), ')') |
+--------------------------------------------------------------+
| ACME (Shanghai)                          |
| DK Inc. (Qingdao)                           |
| FastFruit Inc. (Tianjin)                        |
| FNK Inc. (Zhongshan)                        |
| Good Set (Taiyuan)                          |
| Just Eat Ours (Beijing)                        |
| LT Supplies (Chongqing)                      |
+---------------------------------------------------------------+

由结果可以看到,显示结果的列名称为SELECT子句后面的计算字段,实际上计算之后的列是没有名字的,这样的结果让人很不容易理解,如果为字段取一个别名,将会使结果清晰,

SQL语句如下,
 SELECT CONCAT(TRIM(s_name) , ' (', TRIM(s_city), ')')
     AS suppliers_title
     FROM suppliers
     ORDER BY s_name;

+------------------------------+
| suppliers_title         |
+------------------------------+
| ACME (Shanghai)     |
| DK Inc. (Qingdao)     |
| FastFruit Inc. (Tianjin)  |
| FNK Inc. (Zhongshan)  |
| Good Set (Taiyuan)     |
| Just Eat Ours (Beijing)  |
| LT Supplies (Chongqing)|
+------------------------------+







使用正则表达式查询

语法: select 字段 from 表 where 字段 regexp '包含正则在内的字符串';

查询以特定字符或字符串开头的记录
查询以特定字符或字符串结尾的记录 
用符号"."来替代字符串中的任意一个字符
使用"*"和"+"来匹配多个字符
匹配指定字符串 
匹配指定字符中的任意一个
匹配指定字符以外的字符
使用{M}或者{M,N}来指定字符串连续出现的次数


查询以特定字符或字符串开头的记录
语法: regexp '^字符'

【例7.68】在fruits表中,查询f_name字段以字母'b'开头的记录,SQL语句如下:
 SELECT * FROM fruits WHERE f_name REGEXP '^b';
+------+------+-----------------+------------+
| f_id | s_id | f_name      |  f_price  |
+------+------+-----------------+-------------+
| b1   |  101  | blackberry |   10.20  |
| b2   |  104  | berry     |    7.60  |
| t1   |  102  | banana    |   10.30  |
+------+----------+---------------+-----------+

fruits表中有3条记录的f_name字段值是以字母b开头,返回结果有3条记录。


【例7.69】在fruits表中,查询f_name字段以“be”开头的记录,SQL语句如下:
 SELECT * FROM fruits WHERE f_name REGEXP '^be';
+------+------+--------+---------+
| f_id | s_id | f_name | f_price |
+------+------+--------+---------+
| b2  |  104 | berry |   7.60  |
+------+------+--------+---------+
只有berry是以“be”开头,所以查询结果中只有1条记录。


查询以特定字符或字符串结尾的记录
字符'$'匹配以特定字符或者字符串结尾的文本。

语法: regexp '字符$'

【例7.70】在fruits表中,查询f_name字段以字母'y'结尾的记录,SQL语句如下:
 SELECT * FROM fruits WHERE f_name REGEXP 'y$';
+------+------+------------+---------+
| f_id | s_id | f_name     | f_price |
+------+------+------------+---------+
| b1   |  101 | blackberry |   10.20 |
| b2   |  104 | berry      |    7.60 |
| c0   |  101 | cherry     |    3.20 |
| m2   |  105 | xbabay    |    2.60 |
+--------+--------+-------------+---------+
fruits表中有4条记录的f_name字段值是以字母'y'结尾,返回结果有4条记录。

【例7.71】在fruits表中,查询f_name字段以字符串“rry”结尾的记录,SQL语句如下:
 SELECT * FROM fruits WHERE f_name REGEXP 'rry$';
+------+------+------------+-----------+
| f_id | s_id | f_name    | f_price  |
+------+------+------------+-----------+
| b1  |  101 | blackberry |   10.20  |
| b2  |  104 | berry     |    7.60  |
| c0  |  101 | cherry    |    3.20  |
+------+------+-------------+------------+

fruits表中有3条记录的f_name字段值是以字符串“rry”结尾,返回结果有3条记录。


用符号"."来替代字符串中的任意一个字符
字符'.'匹配任意一个字符

语法: regexp '字符.'
          regexp '字符.字符'
          regexp '.字符'

【例7.72】在fruits表中,查询f_name字段值包含字母'a'与'g'且两个字母之间只有一个字母的记录,SQL语句如下,
 SELECT * FROM fruits WHERE f_name REGEXP 'a.g';
+------+------+--------+---------+
| f_id | s_id | f_name | f_price |
+------+------+--------+---------+
| bs1  |  102 | orange | 11.20 |
| m1  |  106 | mango | 15.60 |
+------+------+--------+---------+

查询语句中'a.g'指定匹配字符中要有字母a和g,且两个字母之间包含单个字符,并不限定匹配的字符的位置和所在查询字符串的总长度,因此orange和mango都符合匹配条件。


使用"*"和"+"来匹配多个字符
星号'*'匹配前面的字符任意多次,包括0次
加号'+'匹配前面的字符至少一次

语法: regexp '字符+'
          regexp '字符*'
          regexp '*字符*'
          .....

【例7.73】在fruits表中,查询f_name字段值以字母'b'开头,且'b'后面出现字母'a'的记录,SQL语句如下:
 SELECT * FROM fruits WHERE f_name REGEXP '^ba*';
+------+------+------------+------------+
| f_id | s_id | f_name    | f_price  |
+------+------+------------+------------+
| b1  |  101 | blackberry |   10.20 |
| b2  |  104 | berry     |    7.60 |
| t1  |  102 | banana    |   10.30 |
+------+------+------------+--------------+
星号'*'可以匹配任意多个字符,blackberry和berry中字母b后面并没有出现字母a,但是也满足匹配条件。


【例7.74】在fruits表中,查询f_name字段值以字母'b'开头,且'b'后面出现字母'a'至少一次的记录,SQL语句如下:
 SELECT * FROM fruits WHERE f_name REGEXP '^ba+';
+------+------+--------+---------+
| f_id | s_id | f_name | f_price |
+------+------+--------+---------+
| t1  |  102 | banana |  10.30 |
+------+------+--------+---------+

'a+'匹配字母'a'至少一次,只有banana满足匹配条件。


匹配指定字符串 
正则表达式可以匹配指定字符串,只要这个字符串在查询文本中即可,如要匹配多个字符串,多个字符串之间使用分隔符'|'隔开。

语法:regexp '字符串|字符串'

【例7.75】在fruits表中,查询f_name字段值包含字符串“on”的记录,SQL语句如下:
  SELECT * FROM fruits WHERE f_name REGEXP 'on';
+------+------+-----------+---------+
| f_id | s_id | f_name  | f_price |
+------+------+-----------+---------+
| bs2  |  105 | melon  |  8.20  |
| l2   |  104 | lemon  |  6.40  |
| o2   |  103 | coconut |  9.20  |
+------+------+------------+---------+

可以看到,f_name字段的melon、lemon和coconut3个值中都包含有字符串“on”,满足匹配条件。

【例7.76】在fruits表中,查询f_name字段值包含字符串“on”或者“ap”的记录,SQL语句如下:
  SELECT * FROM fruits WHERE f_name REGEXP 'on|ap';
+------+------+----------+---------+
| f_id | s_id | f_name  | f_price |
+-------+-------+-----------+---------+
| a1   |  101 | apple   |    5.20 |
| a2   |  103 | apricot |    2.20 |
| bs2  |  105 | melon   |    8.20 |
| l2   |  104 | lemon   |    6.40 |
| o2   |  103 | coconut |    9.20 |
| t2   |  102 | grape   |    5.30 |
+-------+-------+----------+----------+

【例7.77】在fruits表中,使用LIKE运算符查询f_name字段值为“on”的记录,SQL语句如下:
 SELECT * FROM fruits WHERE f_name LIKE 'on';
Empty set (0.00 sec)
f_name字段没有值为“on”的记录,返回结果为空。读者可以体会一下两者的区别。


匹配指定字符中的任意一个
方括号“[]”指定一个字符集合,只匹配其中任何一个字符,即为所查找的文本。

语法: regexp '[ab]'

【例7.78】在fruits表中,查找f_name字段中包含字母'o'或者't'的记录,SQL语句如下:
 SELECT * FROM fruits WHERE f_name REGEXP '[ot]';
+------+------+---------+---------+
| f_id | s_id | f_name  | f_price |
+------+------+---------+---------+
| a2   |  103 | apricot |    2.20 |
| bs1  |  102 | orange  |   11.20 |
| bs2  |  105 | melon   |    8.20 |
| l2   |  104 | lemon   |    6.40 |
| m1   |  106 | mango   |   15.60 |
| m3   |  105 | xxtt    |   11.60 |
| o2   |  103 | coconut |    9.20 |
+------+------+---------+---------+

查询结果可以看到,所有返回的记录的f_name字段的值中都包含有字母o或者t,或者两个都有。

方括号“[]”还可以指定数值集合

【例7.79】在fruits表,查询s_id字段中数值中包含4、5或者6的记录,SQL语句如下:
 SELECT * FROM fruits WHERE s_id REGEXP '[456]';
+------+------+---------+----------+
| f_id | s_id   | f_name | f_price |
+-------+-------+---------+---------+
| b2   |  104 | berry  |    7.60 |
| bs2  |  105 | melon  |    8.20 |
| l2   |  104 | lemon  |    6.40 |
| m1   |  106 | mango  |   15.60 |
| m2   |  105 | xbabay |    2.60 |
| m3   |  105 | xxtt   |   11.60 |
+-------+-------+---------+----------+

查询结果中,s_id字段值中有3个数字中的1个即为匹配记录字段。

匹配集合“[456]”也可以写成“[4-6]”即指定集合区间。例如“[a-z]”表示集合区间为从a~z的字母,“[0-9]”表示集合区间为所有数字。


匹配指定字符以外的字符
“[^字符集合]”匹配不在指定集合中的任何字符。

语法: regexp '[^字符集合]'

【例7.80】在fruits表中,查询f_id字段包含字母a~e和数字1~2以外的字符的记录,SQL语句如下:
 SELECT * FROM fruits WHERE f_id REGEXP '[^a-e1-2]';
+------+------+---------+---------+
| f_id | s_id | f_name  | f_price |
+------+------+---------+---------+
| b5   |  107 | xxxx    |    3.60 |
| bs1  |  102 | orange  |   11.20 |
| bs2  |  105 | melon   |    8.20 |
| c0   |  101 | cherry  |    3.20 |
| l2   |  104 | lemon   |    6.40 |
| m1   |  106 | mango   |   15.60 |
| m2   |  105 | xbabay  |    2.60 |
| m3   |  105 | xxtt    |   11.60 |
| o2   |  103 | coconut |    9.20 |
| t1   |  102 | banana  |   10.30 |
| t2   |  102 | grape   |    5.30 |
| t4   |  107 | xbababa |    3.60 |
+------+------+---------+---------+



使用{M}或者{M,N}来指定字符串连续出现的次数
"字符串{n,}"      表示至少匹配n次前面的字符。
"字符串{n,m}"   表示匹配前面的字符串不少于n次,不多于m次。

语法: regexp '字符串{n,}'
          regexp '字符串{n,m}'

【例7.81】在fruits表中,查询f_name字段值出现字母'x'至少2次的记录,SQL语句如下:
  SELECT * FROM fruits WHERE f_name REGEXP 'x{2,}';
+------+------+--------+---------+
| f_id | s_id | f_name | f_price |
+------+------+--------+---------+
| b5  |  107 | xxxx  |   3.60 |
| m3  |  105 | xxtt  |  11.60 |
+------+-------+--------+---------+
可以看到,f_name字段的“xxxx”包含了4个字母'x',“xxtt”包含两个字母'x',均为满足匹配条件的记录。
【例7.82】在fruits表中,查询f_name字段值出现字符串“ba”最少1次,最多3次的记录,SQL语句如下:
 SELECT * FROM fruits WHERE f_name REGEXP 'ba{1,3}';
+------+----------+-----------+---------+
| f_id |  s_id  | f_name  | f_price  |
+------+----------+----------+------------+
| m2  |  105  | xbabay  |    2.60  |
| t1   |  102  | banana  |   10.30  |
| t4   |  107  | xbababa |    3.60  |
+-------+--------+------------+-----------+
可以看到,f_name字段的xbabay值中“ba”出现了2次,banana中出现了1次,xbababa中出现了3次,都满足匹配条件的记录。

猜你喜欢

转载自blog.csdn.net/wangminglong1989/article/details/81556718