想要成为一名数据研发工程师,SQL是必会的技能。数仓建模过程中用到Hive,其实也是通过写类SQL的语句,通过底层的引擎将其翻译成MapReduce程序,减少了程序员的开发量。除此之外,Spark、Flink等计算框架也支持使用SQL来实现查询。所以在面试的过程中,SQL是必须要考察的内容。今天先分享SQL的基础语法,而窗口函数是重中之重,后面单独写一篇进行讲解。
SELECT 查询列表
FROM 表名或视图列表
WHERE 条件表达式
GROUP BY 字段名 HAVING 条件表达式
ORDER BY 字段 ASC|DESC
LIMIT m,n;
说明:
作用:从2张或多张表中,取出有关联的数据。
关联查询一共有几种情况:
说明:
select <select_list>
from tableA A
inner join tableB B
on A.key=B.key;
select <select_list>
from tableA A
left join tableB B
on A.key=B.key;
select <select_list>
from tableA A
right join tableB B
on A.key=B.key;
select <select_list>
from tableA A
left join tableB B
on A.key=B.key
where B.key is null;
select <select_list>
from tableA A
right join tableB B
on A.key=B.key
where A.key is null;
select <select_list>
from tableA A
left join tableB B
on A.key=B.key
union
select <select_list>
from tableA A
right join tableB B
on A.key=B.key;
select <select_list>
from tableA A
left join tableB B
on A.key=B.key
where B.key is null
union
select <select_list>
from tableA A
right join tableB B
on A.key=B.key
where A.key is null;
定义:将两(或多)个表的所有行进行组合,连接后的行数为两(或多)个表的乘积数。
在 MySQL 中,如果缺少关联条件或者关联条件不准确,则会出现笛卡尔积。
注:外连接必须写关联条件,否则报语法错误。
表连接的约束条件可以有三种方式:WHERE, ON, USING
#关联条件
#把关联条件写在where后面
SELECT ename,dname FROM t_employee,t_department WHERE t_employee.dept_id=t_department.did;
#把关联条件写在on后面,只能和JOIN一起使用
SELECT ename,dname FROM t_employee JOIN t_department ON t_employee.dept_id=t_department.did;
#把关联字段写在using()中,只能和JOIN一起使用
SELECT ename,basic_salary FROM t_employee INNER JOIN t_salary USING(eid);
#n张表关联,需要n-1个关联条件
#查询员工姓名,基本工资,部门名称
SELECT ename,basic_salary,dname FROM t_employee,t_department,t_salary
WHERE t_employee.dept_id=t_department.did AND t_employee.eid=t_salary.eid;
SELECT ename,basic_salary,dname FROM t_employee JOIN t_department JOIN t_salary
ON t_employee.dept_id=t_department.did AND t_employee.eid=t_salary.eid;
有两种,显式的和隐式的,返回连接表中符合连接条件和查询条件的数据行
格式:
隐式:SELECT [cols_list] from 表1,表2 where [condition]
显式:SELECT [cols_list] from 表1 JOIN 表2 ON [关联条件] where [其他筛选条件]
SELECT dep.dept_id,dep.name,emp.id,emp.name
FROM t_employee emp JOIN t_department dep
ON emp.dept_id=dep.dept_id;
外连接分为左外连接,右外连接,
#查询所有部门信息以及该部门员工信息
SELECT did,dname,eid,ename
FROM t_department LEFT OUTER JOIN t_employee
ON t_department.did = t_employee.dept_id;
#查询所有员工信息,以及员工的部门信息
SELECT eid,ename,did,dname
FROM t_department RIGHT OUTER JOIN t_employee
ON t_employee.dept_id = t_department.did;
#查询所有部门信息和员工信息
SELECT did,dname,eid,ename
FROM t_department LEFT OUTER JOIN t_employee
ON t_department.did = t_employee.dept_id
UNION
SELECT did,dname,eid,ename
FROM t_department RIGHT OUTER JOIN t_employee
ON t_department.did = t_employee.dept_id;
当 table1 和 table2 本质上是同一张表,只是用取别名的方式虚拟成两张表以代表不同的意义。然后两个表再进行内连接,外连接等查询。
#自连接
#查询员工姓名以及领导姓名,仅显示有领导的员工
SELECT emp.ename,mgr.ename
FROM t_employee AS emp, t_employee AS mgr
WHERE emp.mid = mgr.eid;
#查询员工姓名以及领导姓名,仅显示有领导的员工
SELECT emp.ename,mgr.ename
FROM t_employee AS emp JOIN t_employee AS mgr
ON emp.mid = mgr.eid;
#查询所有员工姓名及其领导姓名
SELECT emp.ename,mgr.ename
FROM t_employee AS emp LEFT JOIN t_employee AS mgr
ON emp.mid = mgr.eid;
设置条件,对记录进行筛选。例如查询id=1的学生信息:
select * from stu_tb where id=1;
很多情况下,用户都需要进行一些汇总操作,比如统计整个公司的人数或者统计每一个部门的人数等。在分组之后,由于一个分组有多行数据,所以必须要进行聚合才能得到1对1的答案进行输出。聚合函数有如下几种:
#聚合函数
#AVG(【DISTINCT】 expr) 返回expr的平均值
SELECT AVG(basic_salary) FROM t_salary;
#COUNT(【DISTINCT】 expr)返回expr的非NULL值的数目
#统计员工总人数
SELECT COUNT(*) FROM t_employee;#count(*)统计的是记录数
#统计员工表的员工所在部门数
SELECT COUNT(dept_id) FROM t_employee;#统计的是非NULL值
SELECT COUNT(DISTINCT dept_id) FROM t_employee;#统计的是非NULL值,并且去重
#MIN(【DISTINCT】 expr)返回expr的最小值
#查询最低基本工资值
SELECT MIN(basic_salary) FROM t_salary;
#MAX(【DISTINCT】 expr)返回expr的最大值
#查询最高基本工资值
SELECT MAX(basic_salary) FROM t_salary;
#查询最高基本工资与最低基本工资的差值
SELECT MAX(basic_salary)-MIN(basic_salary) FROM t_salary;
#SUM(【DISTINCT】 expr)返回expr的总和
#查询基本工资总和
SELECT SUM(basic_salary) FROM t_salary;
上面的聚合函数是对整个表格使用的。如果加上 group by,可以实现按照部门统计等操作。
#group by + 聚合函数
#统计每个部门的人数
SELECT dept_id,COUNT(*) FROM t_employee
GROUP BY dept_id;
#统计每个部门的平均基本工资
SELECT emp.dept_id,AVG(s.basic_salary )
FROM t_employee AS emp,t_salary AS s
WHERE emp.eid = s.eid
GROUP BY emp.dept_id;
#统计每个部门的年龄最大者
SELECT dept_id,MAX(birthday) FROM t_employee GROUP BY dept_id;
#统计每个部门基本工资最高者
SELECT emp.dept_id,MAX(s.basic_salary )
FROM t_employee AS emp,t_salary AS s
WHERE emp.eid = s.eid
GROUP BY emp.dept_id;
#统计每个部门基本工资之和
SELECT emp.dept_id,SUM(s.basic_salary )
FROM t_employee AS emp,t_salary AS s
WHERE emp.eid = s.eid
GROUP BY emp.dept_id;
注意:
having与where类似,可筛选数据。但也有不同点,不同点如下:
#按照部门统计员工人数,仅显示部门人数少于3人的
SELECT dept_id,COUNT(*) AS c
FROM t_employee
WHERE dept_id IS NOT NULL
GROUP BY dept_id
HAVING c <3;
按一个或多个字段对查询结果进行排序。用法:order by col1,col2,col3…
#统计每个部门的平均基本工资,并按照平均工资降序排列
SELECT emp.dept_id,AVG(s.basic_salary)
FROM t_employee AS emp,t_salary AS s
WHERE emp.eid = s.eid
GROUP BY emp.dept_id
ORDER BY AVG(s.basic_salary) DESC;
用法:limit m,n
#统计每个部门的平均基本工资,并显示前三名
SELECT emp.dept_id,AVG(s.basic_salary)
FROM t_employee AS emp,t_salary AS s
WHERE emp.eid = s.eid
GROUP BY emp.dept_id
ORDER BY AVG(s.basic_salary) DESC
LIMIT 0,3;
某些情况下,当进行一个查询时,需要的条件或数据要用另外一个 select 语句的结果,这个时候,就要用到子查询。例如求比张三工资高的员工。一般根据子查询的嵌入位置分为,where型子查询,from型子查询,exists型子查询。
where 型子查询即把内层 sql 语句查询的结果作为外层 sql 查询的条件。
子查询要包含在括号内。
建议将子查询放在比较条件的右侧。
单行操作符对应单行子查询,多行操作符对应多行子查询。
对于单行操作符,右边子查询必须返回的是单个值,单行比较运算符(=,>,>=,<,<=,<>)
对于多行操作符,右边子查询可以返回多行,但必须是单列,ALL,ANY,IN 其中,ALL和ANY运算符必须与单行比较运算符(=,>,>=,<,<=,<>)结合使用。
IN:等于任何一个
ALL:和子查询返回的所有值比较。例如:sal>ALL(1,2,3) 等价于 sal>1 && sal>2 &&sal>3,即大于所有。
ANY:和子查询返回的任意一个值比较。例如:sal>ANY(1,2,3)等价于sal>1 or sal>2 or sal>3,即大于任意一个就可以。
EXISTS:判断子查询是否有返回结果(不关心具体行数和内容),如果返回则为TRUE,否则为FALSE。
#查询和张三,李四在同一个部门的员工
SELECT * FROM t_employee
WHERE dept_id IN(SELECT dept_id FROM t_employee WHERE ename='张三' OR ename = '李四');
#查询全公司工资最高的员工编号,基本工资
#方法一
SELECT eid,basic_salary FROM t_salary
WHERE basic_salary = (SELECT MAX(basic_salary) FROM t_salary);
#方法二
SELECT eid,basic_salary FROM t_salary
WHERE basic_salary >= ALL(SELECT basic_salary FROM t_salary);
from 型子查询即把内层 sql 语句查询的结果作为临时表供外层 sql 语句再次查询。
#找出比部门平均工资高的员工编号,基本工资
SELECT t_employee.eid,basic_salary
FROM t_salary INNER JOIN t_employee INNER JOIN (
SELECT emp.dept_id AS did,AVG(s.basic_salary) AS avg_salary
FROM t_employee AS emp,t_salary AS s
WHERE emp.eid = s.eid
GROUP BY emp.dept_id) AS temp
ON t_salary.eid = t_employee.eid AND t_employee.dept_id = temp.did
WHERE t_salary.basic_salary > temp.avg_salary;
exists 型子查询把外层的查询结果拿到内层,看内层的查询是否成立。
#查询部门信息,该部门必须有员工
SELECT * FROM t_department
WHERE EXISTS (SELECT * FROM t_employee WHERE t_employee.dept_id = t_department.did);