SQL Cookbook
PS:SQL语句基于Oracle 11g,使用账户scott,操作表为emp和dept表。
一、检索数据
1.查找满足多个条件的行:
1 SELECT 2 * 3 FROM 4 emp 5 WHERE 6 DEPTNO = 10 7 OR comm IS NOT NULL 8 OR sal <= 2000 9 AND DEPTNO = 20;
其返回部门10中所有员工+所有得到提成的员工+部门20中工资不超过2000美金的员工。
1 SELECT 2 * 3 FROM 4 emp 5 WHERE 6 ( 7 deptno = 10 8 OR comm IS NOT NULL 9 OR sal <= 2000 10 ) 11 AND DEPTNO = 20;
其仅仅返回部门20中符合条件的员工
2.在where子句中引用取别名的列:
1 SELECT 2 * 3 FROM 4 ( 5 SELECT 6 sal AS salary, 7 comm AS commision 8 FROM 9 emp 10 ) x 11 WHERE 12 salary < 5000;
3.连接列值:
1 SELECT 2 ename || ' WORKS AS A ' || JOB 3 FROM 4 emp 5 WHERE 6 deptno = 10;
4.在select语句中使用条件逻辑:
1 SELECT 2 ename, 3 sal, 4 CASE 5 WHEN sal <= 2000 THEN 6 'UNDERPAID' 7 WHEN sal >= 4000 THEN 8 'OVERPAID' 9 ELSE 10 'OK' 11 END AS status 12 FROM 13 emp;
5.限制返回行数:
1 SELECT 2 * 3 FROM 4 emp 5 WHERE 6 ROWNUM <= 5;
Oracle中使用ROWNUM函数来得到每行的行号,但是ROWNUM=5返回为空;ROWNUM=1返回第1行。
6.从表中随机返回n条记录:
1 SELECT 2 * 3 FROM 4 ( 5 SELECT 6 ename, 7 JOB 8 FROM 9 emp 10 ORDER BY 11 dbms_random.VALUE() 12 ) 13 WHERE 14 ROWNUM <= 5;
7.为每行数据加个id:
1 SELECT ROWNUM AS ID, E .* FROM emp E;
二、查询结果排序
1.按子串最后两个字符排序:
1 SELECT 2 ename, 3 JOB 4 FROM 5 emp 6 ORDER BY 7 "SUBSTR" (JOB, LENGTH("JOB") - 2);
2.对于一个字母数字混合的列,分别按字母或数字进行排序:
(1)使用sys登录,给scott用户授权创建视图的权力:
1 GRANT CREATE VIEW TO scott;
(2)scott用户创建一个字母数字混合列的视图:
1 CREATE VIEW v AS SELECT 2 ename || ' ' || deptno AS DATA 3 FROM 4 emp;
(3)使用replace和translate函数解决这个问题:
replace()函数:字符串级别的替换
translate()函数:字符级别的替换
1>按照字母排序:
首先使用translate()函数,将所有的数字替换成#号,得到字符串1。然后使用replace()函数将字符串1中的#消除,得到字符串2。最后再使用replace()函数将混列列中的字母消除,仅仅剩下数字。
1 SELECT 2 DATA 3 FROM 4 v 5 ORDER BY 6 "REPLACE" ( 7 DATA, 8 "REPLACE" ( 9 "TRANSLATE" ( 10 DATA, 11 '0123456789', 12 '##########' 13 ), 14 '#', 15 '' 16 ), 17 '' 18 );
2>按照字母排序:
首先使用translate()函数将所有数字替换成#号,得到字符串1。接着使用replace()函数将所有的#号消除即可。
1 SELECT 2 DATA 3 FROM 4 v 5 ORDER BY 6 "REPLACE" ( 7 "TRANSLATE" ( 8 DATA, 9 '0123456789', 10 '##########' 11 ), 12 '#', 13 '' 14 );
3.处理排序空值:
Oracle 11g提供了解决方法。一般需要使用一个附加的列来进行排序
(1)空值排在最后
1 SELECT 2 ename, 3 sal, 4 comm 5 FROM 6 emp 7 ORDER BY 8 comm NULLS LAST;
(2)空值排在最前
1 SELECT 2 ename, 3 sal, 4 comm 5 FROM 6 emp 7 ORDER BY 8 comm NULLS FIRST;
4.根据条件选项进行排序:
1 SELECT 2 ename, 3 sal, 4 JOB, 5 comm 6 FROM 7 emp 8 ORDER BY 9 CASE 10 WHEN JOB = 'SALESMAN' THEN 11 comm 12 ELSE 13 sal 14 END;
三、操作多个表
1.多个表的数据集的叠加:
1 SELECT 2 ename AS ename_and_dname, 3 deptno 4 FROM 5 emp 6 WHERE 7 DEPTNO = 10 8 UNION ALL 9 SELECT 10 '---------------------------', 11 NULL 12 FROM 13 dual 14 UNION ALL 15 SELECT 16 dname AS ename_and_dname, 17 deptno 18 FROM 19 dept;
union all要求select列表的项目数和对应项目的数据类型必须要匹配。union和union all区别为:union筛选掉重复项,而union all包含所有项。
2.在一个表中查找其他表不匹配的记录:
1 SELECT 2 D .* 3 FROM 4 dept D, 5 emp E 6 WHERE 7 D .DEPTNO = E .DEPTNO (+) 8 AND E .DEPTNO IS NULL;
此为查找没有职员的部门。
3.在运算和比较时使用NULL值:
1 SELECT 2 ename, 3 comm 4 FROM 5 emp 6 WHERE 7 "COALESCE" (comm, 0) < ( 8 SELECT 9 comm 10 FROM 11 emp 12 WHERE 13 ename = 'WARD' 14 );
四、插入、更新与删除
1.插入默认值:
(1)有这样一张表:
1 CREATE TABLE D ( 2 ID INTEGER DEFAULT 0, 3 foo VARCHAR (10) 4 );
(2)在这样的表中插入一行:
1 INSERT INTO D (ID, foo) 2 VALUES 3 (NULL, 'foo');
注意:对于默认值,需要指定为NULL值的。
2.从一个表向另外的表中复制行:
1 INSERT INTO dept_east (deptno, dname, loc) SELECT 2 deptno, 3 dname, 4 loc 5 FROM 6 DEPT 7 WHERE 8 loc IN ('NEW YORK', 'BOSTON');
3.一次向多个表中插入记录:
1 INSERT ALL 2 WHEN loc IN ('NEW YORK', 'BOSTON') THEN 3 INTO dept_east (deptno, dname, loc) 4 VALUES 5 (deptno, dname, loc) 6 WHEN loc IN ('CHICAGO') THEN 7 INTO dept_mid (deptno, dname, loc) 8 VALUES 9 (deptno, dname, loc) 10 ELSE 11 INTO dept_west (deptno, dname, loc) 12 VALUES 13 (deptno, dname, loc) SELECT 14 deptno, 15 dname, 16 loc 17 FROM 18 dept;
4.合并记录:
1 MERGE INTO emp_commission ec USING (SELECT * FROM emp) emp ON (ec.empno = EMP.empno) 2 WHEN MATCHED THEN 3 UPDATE 4 SET ec.comm = 1000 DELETE 5 WHERE 6 (sal < 2000) 7 WHEN NOT MATCHED THEN 8 INSERT ( 9 ec.empno, 10 ec.ename, 11 ec.deptno, 12 ec.comm 13 ) 14 VALUES 15 ( 16 EMP.empno, 17 EMP.ename, 18 EMP.deptno, 19 emo.comm 20 );
5.删除重复记录:
(1)建立如下的表,并且插入数据:
1 CREATE TABLE dupes (ID INTEGER, NAME VARCHAR(10)); 2 3 INSERT INTO dupes 4 VALUES 5 (1, 'NAPOLEON'); 6 7 INSERT INTO dupes 8 VALUES 9 (2, 'DYNAMITE'); 10 11 INSERT INTO dupes 12 VALUES 13 (3, 'DYNAMITE'); 14 15 INSERT INTO dupes 16 VALUES 17 (4, 'SHE SELLS'); 18 19 INSERT INTO dupes 20 VALUES 21 (5, 'SEA SHELLS'); 22 23 INSERT INTO dupes 24 VALUES 25 (6, 'SEA SHELLS'); 26 27 INSERT INTO dupes 28 VALUES 29 (7, 'SEA SHELLS');
(2)删除重复的姓名组,只保留一个ID:
1 DELETE 2 FROM 3 dupes 4 WHERE 5 ID NOT IN ( 6 SELECT 7 MIN (ID) 8 FROM 9 dupes 10 GROUP BY 11 NAME 12 );
五、元数据查询
1.列出模式中的表:
1 SELECT table_name FROM all_tables;
2.列出表的列:
1 SELECT 2 column_name, 3 data_type, 4 column_id 5 FROM 6 all_tab_columns 7 WHERE 8 OWNER = 'SCOTT';
3.列出表的索引:
1 SELECT 2 table_name, 3 index_name, 4 column_name, 5 column_position 6 FROM 7 SYS.ALL_IND_COLUMNS 8 WHERE 9 table_name = 'EMP' 10 AND table_owner = 'SCOTT';
4.列出表的约束:
1 SELECT 2 A .table_name, 3 A .constraint_name, 4 b.column_name, 5 A .constraint_type 6 FROM 7 all_constraints A, 8 all_cons_columns b 9 WHERE 10 A .table_name = 'EMP' 11 AND A . OWNER = 'SCOTT' 12 AND A .table_name = b.table_name 13 AND A . OWNER = b. OWNER 14 AND A .constraint_name = b.constraint_name;
5.列出没有相应索引的外键:
1 SELECT 2 A .table_name, 3 A .constraint_name, 4 A .column_name, 5 c.index_name 6 FROM 7 all_cons_columns A, 8 all_constraints b, 9 all_ind_columns c 10 WHERE 11 A .table_name = 'EMP' 12 AND A . OWNER = 'SCOTT' 13 AND b.constraint_type = 'R' 14 AND A . OWNER = b. OWNER 15 AND A .table_name = b.table_name 16 AND A .constraint_name = b.constraint_name 17 AND A . OWNER = c.table_owner (+) 18 AND A .table_name = c.table_name (+) 19 AND A .column_name = c.column_name (+) 20 AND c.index_name IS NULL;
六、使用字符串
1.遍历字符串
将表EMP中的ENAME值为"KING"的字符串显示为4行,每行都包含一个字母。
(1)创建表T10,并且插入数据:
1 CREATE TABLE T10 (ID INT); 2 INSERT INTO T10 3 VALUES 4 (1); 5 6 INSERT INTO T10 7 VALUES 8 (2); 9 10 INSERT INTO T10 11 VALUES 12 (3); 13 14 INSERT INTO T10 15 VALUES 16 (4); 17 18 INSERT INTO T10 19 VALUES 20 (5); 21 22 INSERT INTO T10 23 VALUES 24 (6); 25 26 INSERT INTO T10 27 VALUES 28 (7); 29 30 INSERT INTO T10 31 VALUES 32 (8); 33 34 INSERT INTO T10 35 VALUES 36 (9); 37 38 INSERT INTO T10 39 VALUES 40 (10);
(2)实现功能:
1 SELECT 2 "SUBSTR" (E .ename, iter.pos, 1) AS C 3 FROM 4 ( 5 SELECT 6 ename 7 FROM 8 emp 9 WHERE 10 ename = 'KING' 11 ) E, 12 (SELECT ID AS pos FROM t10) iter 13 WHERE 14 iter.pos <= LENGTH (E .ename);
原理:先选出10个'KING"值,再从中选出‘KING’长度1-4个来,最后利用迭代器对每个位置的字符截取就可得到结果。
2.将字符和数字数据分离:
1 SELECT 2 REPLACE ( 3 "TRANSLATE" ( 4 DATA, 5 '0123456789', 6 '0000000000' 7 ), 8 '0' 9 ) ename, 10 REPLACE ( 11 "TRANSLATE" ( 12 LOWER (DATA), 13 'abcdefghijklmnopqrstuvwxyz', 14 RPAD ('z', 26, 'z') 15 ), 16 'z' 17 ) sal 18 FROM 19 ( 20 SELECT 21 ename || sal DATA 22 FROM 23 emp 24 );
3.去除非字母数字数据的记录:
(1)创建视图:
1 CREATE VIEW v1 AS SELECT 2 ename AS DATA 3 FROM 4 emp 5 WHERE 6 deptno = 10 7 UNION ALL 8 SELECT 9 ename || ', $' || CAST (sal AS CHAR(4)) || '.00' AS DATA 10 FROM 11 emp 12 WHERE 13 deptno = 20 14 UNION ALL 15 SELECT 16 ename || deptno AS DATA 17 FROM 18 emp 19 WHERE 20 deptno = 30;
(2)实现功能:
1 SELECT 2 DATA 3 FROM 4 v1 5 WHERE 6 "TRANSLATE" ( 7 LOWER (DATA), 8 '0123456789abcdefghijklmnopqrstuvwxyz', 9 RPAD ('a', 36, 'a') 10 ) = RPAD ('a', LENGTH(DATA), 'a');
4.按字符串中的数值排序:
(1)创建视图:
1 CREATE VIEW v2 AS SELECT 2 E .ename || ' ' || CAST (E .empno AS CHAR(4)) || ' ' || D .dname AS DATA 3 FROM 4 emp E, 5 dept D 6 WHERE 7 E .deptno = D .deptno;
(2)实现功能:
1 SELECT 2 DATA 3 FROM 4 v 5 ORDER BY 6 "TO_NUMBER" ( 7 "REPLACE" ( 8 "TRANSLATE" ( 9 DATA, 10 "REPLACE" ( 11 "TRANSLATE" ( 12 DATA, 13 '0123456789', 14 '##########' 15 ), 16 '#', 17 '' 18 ), 19 RPAD ('#', 20, '#') 20 ), 21 '#', 22 '' 23 ) 24 );
首先利用translate()函数将数字替换成#号,然后,利用replace()行将#号删除,得到仅剩字母的串。再通过translate()函数将原串中于仅剩字母串中相同的替换成#号,在用replace()删除#号,则所得的串仅仅剩下数字,在用to_number()函数将数字串变成数字。
注意:此处不能先将非数字变成#号,然后删除#号仅仅剩下数字。因为串中非数字类型不一定只有字母,可能有特殊符号,希腊文,什么的。
5.将表中行序号相同的行合成一行:
(1)有如下的表:
1 SELECT 2 deptno, 3 ename 4 FROM 5 emp 6 ORDER BY 7 1;
想要将deptno相同的行,合成一行,ename包含所有deptno相同的数据。如:10 clark;10 king;10 miller;-》10 clark,king,miller
(2)实现功能:
要实现功能,需要使用到Oracle中的ltrim()函数,sys_connect_by_path()和row_number()函数。ltrim(s1,s2)函数保证s1的第一个字符不出现在s2中。sys_connect_by_path(s1,s2)函数,将父节点s1下所有子节点按s2连接成一列。row_number()函数返回结果集中行的序列号,从1开始。
1 SELECT 2 deptno, 3 LTRIM ( 4 SYS_CONNECT_BY_PATH (ename, ','), 5 ',' 6 ) ename 7 FROM 8 ( 9 SELECT 10 deptno, 11 ename, 12 ROW_NUMBER () OVER ( 13 PARTITION BY deptno 14 ORDER BY 15 empno 16 ) rn, 17 COUNT (*) OVER (PARTITION BY deptno) cnt 18 FROM 19 emp 20 ) 21 WHERE 22 LEVEL = cnt START WITH rn = 1 CONNECT BY PRIOR deptno = deptno 23 AND PRIOR rn = rn - 1;
首先,子查询中:
1 SELECT 2 deptno, 3 ename, 4 ROW_NUMBER () OVER ( 5 PARTITION BY deptno 6 ORDER BY 7 empno 8 ) rn, 9 COUNT (*) OVER (PARTITION BY deptno) cnt 10 FROM 11 emp;
找出deptno,ename,同时rn列统计出每个deptno的序号,cnt统计出每个deptno出现的数目。序号rn的目的是用来作为遍历树。
然后,在外层查询中,设置遍历树的层数为cnt,开始的数为1,要求deptno相同。rn小的是rn大的父行,在用sys_connect_by_name将所有ename和逗号连接起来,最后,去除第一个逗号即可。
6.按照字母顺序重新组成字符串:
1 SELECT 2 old_name, 3 "REPLACE" ( 4 SYS_CONNECT_BY_PATH (c, ' '), 5 ' ', 6 '' 7 ) new_name 8 FROM 9 ( 10 SELECT 11 E .ename old_name, 12 "SUBSTR" (E .ename, iter.pos, 1) c, 13 ROW_NUMBER () OVER ( 14 PARTITION BY E .ename 15 ORDER BY 16 SUBSTR (E .ename, iter.pos, 1) 17 ) rn, 18 COUNT (*) OVER (PARTITION BY E .ename) cnt 19 FROM 20 emp E, 21 (SELECT ROWNUM pos FROM emp) iter 22 WHERE 23 iter.pos <= LENGTH (E .ename) 24 ORDER BY 25 1 26 ) x 27 WHERE 28 LEVEL = cnt START WITH rn = 1 CONNECT BY PRIOR rn = rn - 1 29 AND PRIOR old_name = old_name;
此处用到了三层select(比原作者少一层),最内部的select选出行号作为iter迭代器,中间曾的select对每个ename中的字母拆分,排序,统计出rn序号,以便建立遍历树,最外层的select将每个字母按序号组合即可。
7.判别可以作为数值的字符串
(1)创建视图:
1 CREATE VIEW v AS SELECT 2 "REPLACE" (mixed, ' ', '') AS mixed 3 FROM 4 ( 5 SELECT 6 "SUBSTR" (ename, 1, 2) || CAST (deptno AS CHAR(4)) || SUBSTR (ename, 3, 2) AS mixed 7 FROM 8 EMP 9 WHERE 10 deptno = 10 11 UNION ALL 12 SELECT 13 CAST (empno AS CHAR(4)) AS mixed 14 FROM 15 EMP 16 WHERE 17 deptno = 20 18 UNION ALL 19 SELECT 20 ename AS mixed 21 FROM 22 EMP 23 WHERE 24 deptno = 30 25 ) x;
(2)实现功能:
1 SELECT 2 TO_NUMBER ( 3 CASE 4 WHEN "REPLACE" ( 5 "TRANSLATE" ( 6 mixed, 7 '0123456789', 8 '9999999999' 9 ), 10 '9', 11 '' 12 ) IS NOT NULL THEN 13 "REPLACE" ( 14 "TRANSLATE" ( 15 mixed, 16 "REPLACE" ( 17 "TRANSLATE" ( 18 mixed, 19 '0123456789', 20 '9999999999' 21 ), 22 '9', 23 '' 24 ), 25 "RPAD" ('#', LENGTH(mixed), '#') 26 ), 27 '#', 28 '' 29 ) 30 ELSE 31 mixed 32 END 33 ) mixed 34 FROM 35 v 36 WHERE 37 "INSTR" ( 38 "TRANSLATE" ( 39 mixed, 40 '0123456789', 41 '9999999999' 42 ), 43 '9' 44 ) > 0;
首先使用translate()和replace()函数将数字删除,然后利用translate函数将串中数字替换成‘#’,最后删除'#'号仅剩下数字,转化为数字即可。其中where中instr()函数用来查找是否有数字。