SQL Cookbook读书笔记(更新ing)
1. 查询
1.1 获取所有数据
select * from emp
对于性能而言,和指定列相同。在编程中最好显式指定列
1.2 获取满足某个条件的行
select * from emp where deptno=10
where后跟条件,所有满足条件的行会被返回
1.3 获取满足多个条件的行
-- For example, if you would like to find all the employees in department 10, along with any employees who earn a commission, along with any employees in department 20 who earn at most $2,000
select * from emp where deptno=10 or comm is not null or (deptno=20 and sal >= 2000)
结合where和and、or和括号。括号中的条件会被一起计算
1.4 获取特定列
select ename, deptno, sal from emp
指定列名
1.5 给列一个有意义的名字
select sal as salary, comm as commission from emp
使用as关键字,有些数据库不需要as,但是都允许
1.6 在where子句中使用别名
select * from (select sal as salary, comm as commission from emp) x where x.salary>=2000
直接在where子句中使用别名是非法的,可以将其包在子查询中
where在select之前计算,所以不能直接用别名。from在where前计算完成,所以外层的where可以看到子查询中的别名
1.7 拼接多列的值
-- 需要的内容来自于多列的值拼接起来
-- DB2, Oracle, PostgreSQL
select ename||' WORK AS A '||job as msg from emp where deptno=10
-- MySQL
select concat(ename, ' WORK AS A ', job) as msg from emp where deptno=10
-- SQL Server
select ename + ' WORK AS A ' + job as msg from emp where deptno=10
1.8 在select中使用条件逻辑
-- you would like to produce a result set such that if an employee is paid $2,000 or less, a message of “UNDERPAID” is returned; if an employee is paid $4,000 or more, a message of “OVERPAID” is returned; and if they make somewhere in between, then “OK” is returned
select ename, sal,
case when sal <= 2000 then 'UNDERPAID'
when sal >= 4000 then 'OVERPAID'
else 'OK' end as status
from emp
使用CASE语句,在返回的内容上做条件逻辑。else子句可以忽略,如果没有else,那么对于所有不满足其它case的结果都返回null
1.9 限制返回的行数
-- DB2
select * from emp fetch first 5 rows only; FETCH FIRST子句
-- MySQL and PostgreSQL
select * from emp limit 5
-- Oracle
select * from emp where rownum <= 5
-- 1. 执行sql
-- 2. 取第一行作为rownumber1
-- 3. 是否达到5,如果达到则不返回内容,否则返回当前记录
-- 4. 取下一条记录作为rownumber+1
-- 5. 执行3
-- 由于oracle的rownum函数机制,rownum=5的条件不会成功,因为rownumber无法增长。而rownum=1能成功是因为为了确认结果集中是否有数据,oracle至少会尝试取一条结果
-- SQL Server
select top 5 * from emp
1.10 从表中返回随机结果
-- 使用dbms提供的随机函数,然后根据随机值sort,最后使用限制工具限制条数
-- DB2
select ename, job from emp order by rand() fetch first 5 rows only
-- MySQL
select ename, job from emp order by rand() limit 5
-- PostgreSQL
select ename, job from emp order by random() limit 5
-- Oracle
select * from (select ename, job from emp order by dbms_random.value()) x where rownum <= 5
-- SQL Server
select top 5 ename, job from emp order by newid()
-- order by会根据函数返回值(每一行)来进行重新排序,然后再进行select
1.11 查找NULL值
-- 查找所有某列为NULL值的行
select * from emp where comm is null
NULL不能用=和<>来判断,只能用is和is not
1.12 将NULL值转化为非NULL值返回
-- 使用coalesce函数来替换NULL值
select coalesce(comm, 0) from emp
coalesce函数接收一个或多个参数,返回第一个非NULL值,这个函数可以应用于所有dbms
1.13 根据模式查询
-- 只返回满足某些模式的行
-- Of the employees in departments 10 and 20, you want to return only those that have either an “I” somewhere in their name or a job title ending with “ER”
select ename, job from emp where deptno in (10, 20) and ename like '%I%' or job like '%ER'
使用like结合通配符%。大多数数据库也支持_通配符匹配单个字符
2. 查询结果排序
2.1 以特定顺序返回查询结果
select ename, job, sal from emp where DEPTNO=10 order by sal
select ename, job, sal from emp where DEPTNO=10 order by 3
使用order by子句。默认升序排列(SAL-ASC),指定DESC则为降序排列,order by后可以不加名称,使用数字(从1开始)
2.2 根据多个字段排序
-- sort the rows from EMP first by DEPTNO ascending, then by salary descending
select empno, deptno, sal, ename, job from emp order by 2 asc, 3 desc
使用order by,多个字段用逗号分隔。优先级是从左往右。可以根据不在select列表里的列排序,但这种情况必须使用列名而不能用数字。如果使用了group by或者distinct,那么不能根据不在select选择列的字段排序
2.3 根据子串排序
-- return employee names and jobs from table EMP and sort by the last two characters in the JOB field
-- DB2, MySQL, Oracle, and PostgreSQL
select ENAME, JOB from emp order by SUBSTR(job, LENGTH(job)-1)
-- SQL Server
select ename, job from emp order by substring(job, len(job)-1, 2)
2.4 排序字母-数字混合数据
-- 某一个字段中包含数字和字母,需要仅根据其中一部分来排序
-- You want to sort the results by DEPTNO or ENAME
-- Oracle, SQL Server, and PostgreSQL
/* ORDER BY DEPTNO */
select data
from V
order by replace(data,
replace(
translate(data,'0123456789','##########'),'#',''),'')
/* ORDER BY ENAME */
select data
from V
order by replace(
translate(data,'0123456789','##########'),'#','')
-- DB2(cast(deptno as char(2))进行类型转换)
/* ORDER BY DEPTNO */
select *
from (
select ename||' '||cast(deptno as char(2)) as data
from emp
order by replace(data,
replace(
translate(data,'##########','0123456789'),'#',''),'')
/* ORDER BY ENAME */
select *
from (
select ename||' '||cast(deptno as char(2)) as data
from emp
order by replace(
translate(data,'##########','0123456789'),'#','')
使用translate和replace来替换数字/字母,然后再通过order by排序。目前mysql不支持translate,所以没有解决方案
2.5 在排序时处理NULL值
-- 直接用这种方法没办法控制包含NULL值的行的位置
select ename, sal, comm from emp order by 3
-- DB2, MySQL, PostgreSQL, and SQL Server
select x.ename, x.sal, x.comm from
(select ename, sal, comm,
case when comm is null then 0
when comm is not null then 1 end as is_null
from emp) x order by x.is_null desc, x.comm
-- Oracle
select ename, sal, comm from emp order by comm nulls first/last
2.6 根据数据满足的条件排序
-- if JOB is SALES‐MAN, you want to sort on COMM; otherwise, you want to sort by SAL
select ename, sal, job, comm from emp order by case when job='SALESMAN' then comm else sal end
在order by子句中使用case
3. 多表查询
3.1 将一个查询集放到另一个上面
-- The tables do not necessarily have a common key, but their columns do have the same data types
select ename as ename_and_dname, deptno from emp where deptno=10
union all
select '----------------', null -- (from t1)
union all
select dname, deptno from dept
UNION ALL和UNION的区别在于 前者会包含重复值(在两个表中) ,和distinct相似,能不用尽量不用。UNION的要求是结果个数和类型相同
3.2 组合关联的行
-- display the names of all employees in department 10 along with the location of each employee’s department
select e.ename, d.loc from emp e, dept d where e.deptno=d.deptno and e.deptno=10
这是一个内联(inner-join)的例子。默认的join操作返回的是笛卡尔积
3.3 查找两张表中相同的行
-- You want to return the EMPNO, ENAME, JOB, SAL, and DEPTNO of all employees in EMP that match the rows from view V(所有job是clerk的员工)
-- MySQL and SQL Server
select e.empno, e.ename, e.job, e.sal, e.deptno from emp e, V where e.ename=V.ename and e.job=V.job and e.sal=V.sal
select e.empno, e.ename, e.job, e.sal, e.deptno from emp e join V on (e.ename=V.ename and e.job=V.job and e.sal=V.sal)
-- DB2, Oracle, and PostgreSQL 使用集合操作(如果不需要取V中的数据)
select empno, ename, job, sal, deptno from emp where (
ename, job, sal) in (select ename, job, sal from emp intersect select ename, job, sal from V)
3.4 获取在一张表中的数据且不再另一张表
-- find which departments (if any) in table DEPT do not exist in table EMP. In the example data
-- DB2, PostgreSQL, and SQL Server
select deptno from dept
except
select deptno from emp
-- Oracle
select deptno from dept
minus
select deptno from emp
-- MySQL
select deptno
from dept
where deptno not in (select deptno from emp)
差集函数让这种操作变得简单。EXCEPT操作取第一个结果集的内容并移除在第二个结果集出现的内容。使用这个操作符的限制包括,数据类型和值的个数必须在两个结果集中匹配,且不会返回重复值(与子查询不同)
有NULL参与的逻辑计算规则
or | t | f | n |
---|---|---|---|
t | t | t | t |
f | t | f | n |
n | t | n | n |
not | |
---|---|
t | f |
f | t |
n | n |
and | t | f | n |
---|---|---|---|
t | t | f | n |
f | f | f | f |
n | n | f | n |
可以使用关联子查询(correlated subquery)来解决NULL的问题,因为这种情况下外部查询的行在子查询中被引用
select d.deptno
from dept d
where not exists(select 1 from emp e where d.deptno = e.deptno)
select deptno
from dept
where deptno not in (select * from new_dept)
select d.deptno
from dept d
where not exists(select 1 from new_dept e where e.deptno = d.deptno)
3.5 获取一张表中和另一张表没有关联的行
-- find which departments have no employees
-- DB2, MySQL, PostgreSQL, and SQL Server
select d.*
from dept d
left outer join emp e on d.deptno = e.DEPTNO
where e.DEPTNO is null
3.6 添加join到一个查询且不影响其它的join
-- return all employees, the location of the department in which they work, and the date they received a bonus
-- DB2, MySQL, PostgreSQL, and SQL Server
select e.empno,
d.loc,
eb.received
from emp e
join dept d on e.DEPTNO = d.deptno
left join emp_bonus eb on e.empno = eb.empno
order by 2
-- scalar subquery(出现在select语句中的子查询),必须返回单个值
select e.empno,
d.loc,
(select eb.received from emp_bonus eb where eb.empno = e.empno) as received
from emp e
join dept d on e.DEPTNO = d.deptno
order by 2
3.7 确定两张表是否有相同的数据
-- MySQL and SQL Server
select e.empno,
e.ENAME,
e.JOB,
e.MGR,
e.HIREDATE,
e.SAL,
e.COMM,
e.DEPTNO,
e.cnt
from (select empno,
ENAME,
JOB,
MGR,
HIREDATE,
SAL,
COMM,
DEPTNO,
count(*) as cnt
from emp
group by empno, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) e
where not exists(
select NULL
from (select empno,
ENAME,
JOB,
MGR,
HIREDATE,
SAL,
COMM,
DEPTNO,
count(*) as cnt
from V
group by empno, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) v
where v.empno = e.empno
and v.ENAME = e.ENAME
and v.JOB = e.JOB
and coalesce(v.MGR, 0) = coalesce(e.MGR, 0)
and v.HIREDATE = e.HIREDATE
and v.SAL = e.SAL
and coalesce(v.COMM, 0) = coalesce(e.COMM, 0)
and v.DEPTNO = e.DEPTNO
and v.cnt = e.cnt
union all
select e.empno,
e.ENAME,
e.JOB,
e.MGR,
e.HIREDATE,
e.SAL,
e.COMM,
e.DEPTNO,
e.cnt
from (select empno,
ENAME,
JOB,
MGR,
HIREDATE,
SAL,
COMM,
DEPTNO,
count(*) as cnt
from V
group by empno, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) e
where not exists(
select NULL
from (select empno,
ENAME,
JOB,
MGR,
HIREDATE,
SAL,
COMM,
DEPTNO,
count(*) as cnt
from emp
group by empno, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) v
where v.empno = e.empno
and v.ENAME = e.ENAME
and v.JOB = e.JOB
and coalesce(v.MGR, 0) = coalesce(e.MGR, 0)
and v.HIREDATE = e.HIREDATE
and v.SAL = e.SAL
and coalesce(v.COMM, 0) = coalesce(e.COMM, 0)