概述
SELECT
语句用于从表中选取/查询数据,结果被存储在一个结果表中(称为结果集)。
语法格式
SELECT
[ALL | DISTINCT | DISTINCTROW ]
[HIGH_PRIORITY]
[STRAIGHT_JOIN]
[SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
[SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
select_expr [, select_expr] ...
[into_option]
[FROM table_references
[PARTITION partition_list]]
[WHERE where_condition]
[GROUP BY {col_name | expr | position}
[ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_condition]
[ORDER BY {col_name | expr | position}
[ASC | DESC], ...]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
[PROCEDURE procedure_name(argument_list)]
[into_option]
[FOR UPDATE | LOCK IN SHARE MODE]
into_option: {
INTO OUTFILE 'file_name'
[CHARACTER SET charset_name]
export_options
| INTO DUMPFILE 'file_name'
| INTO var_name [, var_name] ...
}
数据库案例
shell> unzip test_db-master.zip
shell> cd test_db-master/
# 设置引擎为InnoDB
shell> set storage_engine = InnoDB
# 执行初始化sql
shell> mysql -t < employees.sql
+-----------------------------+
| INFO |
+-----------------------------+
| CREATING DATABASE STRUCTURE |
+-----------------------------+
+------------------------+
| INFO |
+------------------------+
| storage engine: InnoDB |
+------------------------+
+---------------------+
| INFO |
+---------------------+
| LOADING departments |
+---------------------+
+-------------------+
| INFO |
+-------------------+
| LOADING employees |
+-------------------+
+------------------+
| INFO |
+------------------+
| LOADING dept_emp |
+------------------+
+----------------------+
| INFO |
+----------------------+
| LOADING dept_manager |
+----------------------+
+----------------+
| INFO |
+----------------+
| LOADING titles |
+----------------+
+------------------+
| INFO |
+------------------+
| LOADING salaries |
+------------------+
表说明:
- departments:记录的是9个部门的部门编号和部门名称;
- dept_emp:部门员工数据,员工id和部门id,其实时间和结束时间(注:9999的意思就是仍在职);
- dept_manger:同第二张表结构差不多,每个部门的每个经理的任职时期,总共就24个人,每个部门至少有过两个经理;
- employees:员工信息表,emp_no是唯一键值;
- salaries:记录每个员工每段时期的薪资;
- title:记录每个员工每段时期的职位名称,但请注意,周期与第五张表是不同的,因为在同一职位上你也是会涨工资的;
模型如下:
常用语法
使用*
查询表中的全部内容
在SELECT
语句中使用星号*
通配符查询所有字段。
语法格式
语法格式如下:
SELECT * FROM 表名;
实例
例如,从employees
表中检索所有字段的数据
SELECT * FROM employees;
输出结果如下:
查询表中指定的字段
语法格式
查询表中的某一个字段的语法格式为:
SELECT < 列名 > FROM < 表名 >;
实例
例如,查询employees
表中所有员工的firstname。
SELECT first_name FROM employees;
例如,查询employees
表中所有员工的firstname、lastname及生日。
SELECT first_name,last_name,birth_date FROM employees;
过滤重复数据(去重)
有时出于对数据分析的要求,需要消除重复的记录值。这时候就需要用到DISTINCT
关键字指示MySQL消除重复的记录值。
语法格式
语法格式为:
SELECT DISTINCT <字段名> FROM <表名>;
实例
例如,查询employees
表中所有员工的生日(去掉重复)。
SELECT DISTINCT birth_date FROM employees;
设置别名
表别名
在使用MySQL查询时,当表名很长或者执行一些特殊查询的时候,为了方便操作或者需要多次使用相同的表时,可以为表指定别名,用这个别名代替表原来的名称。
语法格式
语法格式如下:
<表名> [AS] <别名>
实例
例如,查询员工每个时期的薪资。
SELECT sa.salary FROM salaries AS sa;
字段别名
在使用SELECT
语句显示查询结果时,MySQL会显示每个SELECT
后面指定输出的字段,在有些情况下,显示的字段名称会很长或者名称不够直观,MySQL可以指定字段的别名,替换字段或表达式。
语法格式
<字段名> [AS] <字段别名>
限制查询结果的记录条数
在使用MySQL SELECT
语句时往往返回的是所有匹配的行,有些时候我们仅需要返回第一行或者前几行,这时候就需要用到MySQLLIMT
子句。
LIMIT
接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。
第一个参数“位置偏移量”指示 MySQL 从哪一行开始显示,是一个可选参数,如果不指定“位置偏移量”,将会从表中的第一条记录开始(第一条记录的位置偏移量是 0,第二条记录的位置偏移量是 1,以此类推);第二个参数“行数”指示返回的记录条数。
语法格式
语法格式如下:
<LIMIT> [<位置偏移量>,] <行数>
实例
例如,查询employees
表中第3条起的10条数据。
mysql> SELECT * FROM employees LIMIT 2,10;
+--------+------------+------------+-----------+--------+------------+
| emp_no | birth_date | first_name | last_name | gender | hire_date |
+--------+------------+------------+-----------+--------+------------+
| 10003 | 1959-12-03 | Parto | Bamford | M | 1986-08-28 |
| 10004 | 1954-05-01 | Chirstian | Koblick | M | 1986-12-01 |
| 10005 | 1955-01-21 | Kyoichi | Maliniak | M | 1989-09-12 |
| 10006 | 1953-04-20 | Anneke | Preusig | F | 1989-06-02 |
| 10007 | 1957-05-23 | Tzvetan | Zielinski | F | 1989-02-10 |
| 10008 | 1958-02-19 | Saniya | Kalloufi | M | 1994-09-15 |
| 10009 | 1952-04-19 | Sumant | Peac | F | 1985-02-18 |
| 10010 | 1963-06-01 | Duangkaew | Piveteau | F | 1989-08-24 |
| 10011 | 1953-11-07 | Mary | Sluis | F | 1990-01-22 |
| 10012 | 1960-10-04 | Patricio | Bridgland | M | 1992-12-18 |
+--------+------------+------------+-----------+--------+------------+
10 rows in set (0.00 sec)
对查询结果进行排序
ORDER BY
子句主要用来将结果集中的数据按照一定的顺序进行排序。
语法格式
语法格式如下:
SELECT field1, field2,...fieldN FROM table_name1, table_name2...
ORDER BY field1 [ASC [DESC][默认 ASC]], [field2...] [ASC [DESC][默认 ASC]]
关键字ASC
表示按升序分组,关键字DESC
表示按降序分组,其中ASC
为默认值。这两个关键字必须位于对应的字段名、表达式、列的位置之后。
实例
例如,根据出生日期降序排序,查询employees
表所有员工信息。
SELECT * FROM employees ORDER BY birth_date DESC;
例如,根据出生日期降序、员工编号升序排序,查询employees
表所有员工信息。
SELECT * FROM employees ORDER BY birth_date DESC,emp_no ASC;
条件查询
使用WHERE
子句来指定查询条件,从FROM
子句的中间结果中选取适当的数据行,达到数据过滤的效果。
语法格式
语法格式如下:
SELECT field1, field2,...fieldN FROM table_name1, table_name2...
[WHERE condition1 [AND [OR]] condition2.....
说明:
- 查询语句中你可以使用一个或者多个表,表之间使用逗号
,
分割,并使用WHERE
语句来设定查询条件; - 你可以在
WHERE
子句中指定任何条件; - 你可以使用
AND
或者OR
指定一个或多个条件; WHERE
子句也可以运用于SQL的DELETE
或者UPDATE
命令;WHERE
子句类似于程序语言中的if
条件,根据MySQL表中的字段值来读取指定的数据;
实例
例如,查询employees
表所有firstname叫Zhonghua的员工信息。
SELECT * FROM employees WHERE first_name = 'Zhonghua';
内连接查询
内连接是通过在查询中设置连接条件的方式,来移除查询结果集中某些数据行后的交叉连接。简单来说,就是利用条件表达式来消除交叉连接的某些数据行。
在MySQL FROM子句中使用关键字 INNER JOIN
连接两张表,并使用 ON
子句来设置连接条件。如果没有任何条件,INNER JOIN
和 CROSS JOIN
在语法上是等同的,两者可以互换。
语法格式
语法格式如下:
SELECT <字段名1,字段名2 …>
FROM <表名1> INNER JOIN <表名2> [ ON子句]
内连接是系统默认的表连接,所以在FROM
子句后可以省略INNER
关键字,只用关键字 JOIN
。使用内连接后,FROM
子句中的ON
子句可用来设置连接表的条件。
实例
例如,查询所有人的title。
SELECT e.emp_no,e.first_name,e.last_name,t.title,t.from_date,t.to_date FROM `employees` e JOIN titles t WHERE e.emp_no = t.emp_no;
外连接查询
内连接是在交叉连接的结果集上返回满足条件的记录;而外连接先将连接的表分为基表和参考表,再以基表为依据返回满足和不满足条件的记录。
外连接更加注重两张表之间的关系。按照连接表的顺序,可以分为左外连接和右外连接。
左外连接查询
左外连接又称为左连接,在 FROM
子句中使用关键字LEFT OUTER JOIN
或者LEFT JOIN
,用于接收该关键字左表(基表)的所有行,并用这些行与该关键字右表(参考表)中的行进行匹配,即匹配左表中的每一行及右表中符合条件的行。
在左外连接的结果集中,除了匹配的行之外,还包括左表中有但在右表中不匹配的行,对于这样的行,从右表中选择的列的值被设置为 NULL
,即左外连接的结果集中的 NULL
值表示右表中没有找到与左表相符的记录。
实例
例如,查询所有员工的部门编号。
SELECT e.emp_no,e.first_name,e.last_name,d.dept_no FROM `employees` e LEFT JOIN`dept_emp` d on e.emp_no = d.emp_no;
右外连接查询
右外连接又称为右连接,在FROM
子句中使用RIGHT OUTER JOIN
或者RIGHT JOIN
。与左外连接相反,右外连接以右表为基表,连接方法和左外连接相同。在右外连接的结果集中,除了匹配的行外,还包括右表中有但在左表中不匹配的行,对于这样的行,从左表中选择的值被设置为NULL
。
实例
例如,查询所有员工的部门编号。
SELECT e.first_name,e.last_name,d.dept_no FROM employees e RIGHT JOIN dept_emp d ON e.emp_no = d.emp_no;
子查询
子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从 MySQL 4.1 开始引入,在SELECT
子句中先计算子查询,子查询结果作为外层另一个查询的过滤条件,查询可以基于一个表或者多个表。
子查询中常用的操作符有ANY(SOME)
、ALL
、IN
和EXISTS
。
子查询可以添加到SELECT
、UPDATE
和DELETE
语句中,而且可以进行多层嵌套。子查询也可以使用比较运算符,如<
、<=
、>
、>=
、!=
等。
IN子查询
结合关键字IN
所使用的子查询主要用于判断一个给定值是否存在于子查询的结果集中。
语法格式
语法格式为:
<表达式> [NOT] IN <子查询>
语法说明如下:
<表达式>
:用于指定表达式。当表达式与子查询返回的结果集中的某个值相等时,返回TRUE
,否则返回FALSE
;若使用关键字NOT
,则返回的值正好相反。<子查询>
:用于指定子查询。这里的子查询只能返回一列数据。对于比较复杂的查询要求,可以使用SELECT
语句实现子查询的多层嵌套。
比较运算符子查询
比较运算符所使用的子查询主要用于对表达式的值和子查询返回的值进行比较运算。
语法格式
其语法格式为:
<表达式> {= | < | > | >= | <= | <=> | < > | != }
{ ALL | SOME | ANY} <子查询>
语法说明如下:
<子查询>
:用于指定子查询。<表达式>
:用于指定要进行比较的表达式。ALL
、SOME
和ANY
:可选项。用于指定对比较运算的限制。其中,关键字ALL
用于指定表达式需要与子查询结果集中的每个值都进行比较,当表达式与每个值都满足比较关系时,会返回TRUE
,否则返回FALSE
;关键字SOME
和ANY
是同义词,表示表达式只要与子查询结果集中的某个值满足比较关系,就返回TRUE
,否则返回FALSE
。
实例
例如,查询Giri Koblick历年工资。
SELECT * FROM salaries WHERE emp_no in (SELECT emp_no FROM employees WHERE first_name = 'Giri' AND last_name = 'Koblick');
结果如下:
+--------+--------+------------+------------+
| emp_no | salary | from_date | to_date |
+--------+--------+------------+------------+
| 26466 | 73178 | 1999-07-26 | 2000-07-25 |
| 26466 | 77529 | 2000-07-25 | 2001-07-25 |
| 26466 | 81844 | 2001-07-25 | 2002-07-25 |
| 26466 | 84824 | 2002-07-25 | 9999-01-01 |
+--------+--------+------------+------------+
EXIST子查询
关键字EXISTS
所使用的子查询主要用于判断子查询的结果集是否为空。
语法格式
其语法格式为:
EXISTS <子查询>
若子查询的结果集不为空,则返回TRUE
;否则返回FALSE
。
分组查询
在MySQL SELECT语句中,允许使用GROUP BY
子句,将结果集中的数据行根据选择列的值进行逻辑分组,以便能汇总表内容的子集,实现对每个组而不是对整个结果集进行整合。
语法格式
语法格式如下:
GROUP BY { <字段名> | <表达式> | <位置> } [ASC | DESC]
说明:
<字段名>
:指定用于分组的字段。可以指定多个字段,彼此间用逗号分隔。<表达式>
:指定用于分组的表达式。通常与聚合函数一块使用,例如可将表达式COUNT(*)AS' 人数 '
作为 SELECT 选择列表清单的一项。<位置>
:指定用于分组的选择字段在SELECT
语句结果集中的位置,通常是一个正整数。例如,GROUP BY 2
表示根据SELECT 语句列清单上的第2字段的值进行逻辑分组。ASC|DESC
:关键字ASC
表示按升序分组,关键字DESC
表示按降序分组,其中ASC
为默认值,注意这两个关键字必须位于对应的字段名、表达式、字段的位置之后。
GROUP BY
必须得配合聚合函数来用,分组之后你可以计数(COUNT),求和(SUM),求平均数(AVG)等。
对于GROUP BY
子句的使用,需要注意以下几点。
GROUP BY
子句可以包含任意数目的列,使其可以对分组进行嵌套,为数据分组提供更加细致的控制。GROUP BY
子句列出的每个列都必须是检索列或有效的表达式,但不能是聚合函数。若在SELECT语句中使用表达式,则必须在GROUP BY
子句中指定相同的表达式。- 除聚合函数之外,SELECT语句中的每个列都必须在
GROUP BY
子句中给出。 - 若用于分组的列中包含有
NULL
值,则NULL
将作为一个单独的分组返回;若该列中存在多个NULL
值,则将这些NULL值所在的行分为一组。
实例
例如,统计同一天生日的员工:
SELECT GROUP_CONCAT(emp_no),birth_date FROM `employees` GROUP BY birth_date;
例如,统计每个部门有多少在职员工:
SELECT
dept_no as 部门,
count(emp_no) as 人数
FROM
dept_emp
WHERE
to_date = '9999-01-01'
GROUP BY
dept_no;
结果如下:
+--------+--------+
| 部门 | 人数 |
+--------+--------+
| d001 | 14842 |
| d002 | 12437 |
| d003 | 12898 |
| d004 | 53304 |
| d005 | 61386 |
| d006 | 14546 |
| d007 | 37701 |
| d008 | 15441 |
| d009 | 17569 |
+--------+--------+
指定过滤条件
在MySQL SELECT语句中,除了能使用GROUP BY
子句分组数据外,还可以使用 HAVING
子句过滤分组,在结果集中规定了包含哪些分组和排除哪些分组。
语法格式
语法格式如下:
HAVING <条件>
HAVING子句和WHERE子句非常相似,HAVING子句支持WHERE子句中所有的操作符和语法,但是两者存在几点差异:
- WHERE 子句主要用于过滤数据行,而HAVING子句主要用于过滤分组,即HAVING子句基于分组的聚合值而不是特定行的值来过滤数据,主要用来过滤分组。
- WHERE 子句不可以包含聚合函数,HAVING子句中的条件可以包含聚合函数。
- HAVING子句是在数据分组后进行过滤,WHERE子句会在数据分组前进行过滤。WHERE子句排除的行不包含在分组中,可能会影响HAVING子句基于这些值过滤掉的分组。
实例
例如,统计员工人数大于30000的部门。
SELECT
(SELECT d.dept_name FROM departments d WHERE de.dept_no = d.dept_no) AS 部门,
count( de.emp_no ) AS 人数
FROM
dept_emp de
WHERE
de.to_date = '9999-01-01'
GROUP BY
de.dept_no
HAVING
count(de.emp_no) > 30000;
结果如下:
+-------------+--------+
| 部门 | 人数 |
+-------------+--------+
| Production | 53304 |
| Development | 61386 |
| Sales | 37701 |
+-------------+--------+
正则表达式查询
MySQL中正式表达式通常被用来检索或替换符合某个模式的文本内容,根据指定的匹配模式匹配文中符合要求的特殊字符串。
MySQL中使用REGEXP
关键字指定正则表达式的字符匹配模式,下表列出了REGEXP
操作符中常用的匹配列表。
实例
例如,统计9月2日出生的f开头lastname的员工。
SELECT * FROM employees WHERE birth_date REGEXP '09-02$' AND last_name REGEXP '^f';