1. 基础部分
GROUP BY
SQL原语:SELECT column_name, aggregate_function(column_name) FROM table_name
WHERE column_name operator value GROUP BY column_name;
GROUP BY适合多表组合,一个column有多个相同的值,按照该值进行分组并使用聚合函数获取所需的信息,具体实例参考后面。
WITH ROLLUP可以实现在分组统计数据基础上再进行相同的统计(SUM,AVG,COUNT…)。
NULL值处理
- IS NULL: 当列的值是 NULL,此运算符返回 true。
- IS NOT NULL: 当列的值不为 NULL, 运算符返回 true。
- <=>: 比较操作符(不同于=运算符),当比较的的两个值为 NULL 时返回 true。
特别注意不能使用 = NULL 或 != NULL 在列中查找 NULL 值。只能使用上面列出的方法来判断NULL值。
MySQL 正则表达式
模式 | 描述 |
---|---|
^ | 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 ‘\n’ 或 ‘\r’ 之后的位置。 |
$ | 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 ‘\n’ 或 ‘\r’ 之前的位置。 |
. | 匹配除 “\n” 之外的任何单个字符。要匹配包括 ‘\n’ 在内的任何字符,请使用象 ‘[.\n]’ 的模式。 |
[…] | 字符集合。匹配所包含的任意一个字符。 |
[^…] | 负值字符集合。匹配未包含的任意字符。 |
p1|p2|p3 | 匹配 p1 或 p2 或 p3。 |
* | 匹配前面的子表达式零次或多次。 |
+ | 匹配前面的子表达式一次或多次。。 |
{n} | n 是一个非负整数。匹配确定的 n 次。 |
{n,m} | m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。 |
2. 实例练习
2.1
查找最晚入职员工的所有信息
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
从表的信息可知,按照hire_date降序排列并返回第一条信息:
SELECT * FROM employees ORDER BY hire_date DESC LIMIT 1;
如果最晚入职的员工记录有多条,上面的语句会漏掉信息,所以采用下面的方式:
SELECT * FROM employees WHERE hire_date=(SELECT MAX(hire_date) FROM employees);
---这种使用方式要保证后面的SELECT语句只返回一行,一列。
查找入职员工时间排名倒数第三的员工所有信息
表的结构没有变,现在需要获取倒数第三名员工的信息,需要使用排序原语:
SELECT * FROM employees ORDER BY hire_date DESC LIMIT 2,1;
---LIMIT 的用法:
---后面一个参数a时,返回a条记录
---后面两个参数a,b时,返回从a+1的b条记录
2.2
查找各个部门当前(to_date=’9999-01-01’)领导当前薪水详情以及其对应部门编号dept_no
CREATE TABLE `dept_manager` (
`dept_no` char(4) NOT NULL,
`emp_no` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`from_date`));
希望查询的信息位于两个表中,使用JOIN来解决这个问题:
SELECT b.*,a.dept_no FROM salaries b,dept_manager a WHERE a.emp_no=b.emp_no AND b.to_date='9999-01-01' AND a.to_date='9999-01-01';
---INNER JOIN和WHERE等价
这里基于两个思考:首先,dept_manager
可能有一个员工多次的记录(比如人事调动),salaries
可能有一个员工多次的记录(比如薪水调整)。然后,使用JOIN的结果势必造成多次重复,假设A表中有一个员工的有n次记录,B表中该员工有m次记录,使用员工号相等条件的JOIN查询,结果该员工将有nm次记录。
2.3
查找所有员工的last_name和first_name以及对应部门编号dept_no,也包括展示没有分配具体部门的员工
CREATE TABLE `dept_emp` (
`emp_no` int(11) NOT NULL,
`dept_no` char(4) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`dept_no`));
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
首先应该想到有的员工并没有分配部门,为了得到这一信息需使用LEFT JOIN,因为该命令返回左表的所有信息,如果查询条件不满足则置空一些列,如下:
SELECT b.last_name,b.first_name,a.dept_no FROM employees b LEFT JOIN dept_emp a ON b.emp_no=a.emp_no
获取所有非manager的员工emp_no
非管理层员工的特点就是employees有记录,而dept_emp没有记录。所以使用LEFT JOIN来查询这一信息:
SELECT emp_no FROM (SELECT * FROM employees b LEFT JOIN dept_manager a ON a.emp_no=b.emp_no) WHERE dept_no IS NULL;
2.4
查找所有员工入职时候的薪水情况,给出emp_no以及salary, 并按照emp_no进行逆序
CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` char(1) NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`));
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`from_date`));
特别注意是入职的时候的情况,且所查询的信息分布在两个表中,需要使用JOIN,这里我喜欢使用WHERE等价代替:
SELECT a.emp_no,b.salary FROM employees a,salaries b WHERE a.emp_no=b.emp_no AND a.hire_date=b.from_date ORDER BY a.emp_no DESC;
2.5
查找薪水涨幅超过15次的员工号emp_no以及其对应的涨幅次数t
CREATE TABLE `salaries` (
`emp_no` int(11) NOT NULL,
`salary` int(11) NOT NULL,
`from_date` date NOT NULL,
`to_date` date NOT NULL,
PRIMARY KEY (`emp_no`,`from_date`));
将每一个员工看成一个分组,然后使用聚合函数得到条目信息总数,因为WHERE后面不能使用聚合函数,所以使用HAVING:
SELECT emp_no,COUNT(*) FROM salaries GROUP BY emp_no HAVING COUNT(*)>15;
3. 小结
经过实例练习,熟悉了查询命令中各种语法的使用场景和使用方式。之后的就可以学习索引和其他概念,并进行相应的练习。