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)