个人对sql语句的一些基础理解
- 准备数据
CREATE TABLE learn_sql( l_id NUMBER(8), l_date DATE DEFAULT SYSDATE, l_str VARCHAR2(200) ); INSERT INTO learn_sql (l_id,l_str)VALUES(1,'小明');
- 最最最基础的sql语句,不考虑group分组,聚合函数
SELECT * FROM learn_sql; -- 这里的“*”将会被隐式转化为多个字段,即以下语句(所有字段),这里不多做赘述 SELECT l_id,l_date,l_str FROM learn_sql;
- 进阶,聚合函数,这个函数其实是跟group by分不开的
SELECT count(1) FROM learn_sql l; --请运行以下sql --准备数据 INSERT INTO learn_sql (l_id,l_str)VALUES(2,'小青'); INSERT INTO learn_sql (l_id,l_str)VALUES(3,'小黑'); INSERT INTO learn_sql (l_id,l_str)VALUES(4,'小红'); INSERT INTO learn_sql (l_id,l_str)VALUES(9,'小蓝'); --查询语句 SELECT count(1) FROM learn_sql l GROUP BY NULL;-- 这里的null是随便写的,你也可以写一个,只要不是表中的字段,都会是null的结果,也许说成是空更好点吧,未知。。。空。。。随你心情理解吧。 --现在应该稍微有点理解为什么写了聚合函数之后就不能再在上面写表中的字段了吧,但是其他插入方法也是可以的,参考上图的获值方式
- 再次进阶
SELECT count(1),'你好' FROM learn_sql l GROUP BY CASE WHEN 1=1 THEN l.l_id ELSE 1 END HAVING COUNT(1)=2; -- 至于为什么能写case函数,因为它最后能返回一个字段,在此处它返回了一个l_id字段,decode也是如此 -- having里面最好不要写乱七八糟的东西,能在where里写的就不在having里写(好像是废话。。。。) 就比如: -- 1 SELECT count(1),SUM(l.l_id),CASE WHEN 1=1 THEN l.l_id ELSE 1 END "表的id" FROM learn_sql l GROUP BY CASE WHEN 1=1 THEN l.l_id ELSE 1 END HAVING CASE WHEN 1=1 THEN l.l_id ELSE 1 END = 2; -- 2. SELECT count(1),SUM(l.l_id),CASE WHEN 1=1 THEN l.l_id ELSE 1 END "表的id" FROM learn_sql l WHERE CASE WHEN 1=1 THEN l.l_id ELSE 1 END = 2 GROUP BY CASE WHEN 1=1 THEN l.l_id ELSE 1 END -- 这里最好选择2这种方式,能在where里面过滤的就不要到having里过滤,因为having是二次过滤 -- 除非是像以下这种问题,where不能过滤的聚合函数,需要放在having里面过滤 SELECT count(1),SUM(l.l_id),l.l_id "表的id" FROM learn_sql l GROUP BY l.l_id HAVING COUNT(1)>=1;
- 对于总结:最关键的应该是类型了吧。哪里可以写什么东西,感觉没有必要去记,只要写上去或者得到的类型结果集(一般分为两种情况:结果集rs<=1和>1)是该处所需要的类型就行了,sql语句认清本质即可
-- 只要类型对了,就算 select 的是 * 也可以照用不误 !!!!!!!!主要看返回值 CREATE TABLE test_1206 (tid NUMBER(8)); INSERT INTO test_1206 VALUES(1); SELECT (SELECT * FROM test_1206),e.* FROM employees e;
-- 拓展:行转列函数 SELECT wm_concat(l.l_str) NAME FROM learn_sql l;
古典问题
- 查询表中的管理层,或者非管理层
- 查询管理层
--因为in里面可以写null,eg:1 in (null,2) 1=null(假)继续过滤.. SELECT COUNT(1) FROM employees e1 WHERE e1.employee_id IN (SELECT e2.manager_id FROM employees e2);
--exists SELECT COUNT(1) FROM employees e WHERE EXISTS (SELECT 1 FROM employees e2 WHERE e2.manager_id = e.employee_id);
- 查询不是管理层的员工
--not in需要加过滤条件 eg:1 not in(2,3,null); 当到了1!=null(假)直接跳出过滤
SELECT COUNT(1)
FROM employees e
WHERE e.employee_id NOT IN
(SELECT e2.manager_id
FROM employees e2
WHERE e2.manager_id IS NOT NULL);
--not exist
SELECT COUNT(1)
FROM employees outere
WHERE NOT EXISTS (SELECT 1
FROM employees innere
WHERE outere.employee_id = innere.manager_id);
- 层级查询
SELECT LEVEL
,PRIOR e.last_name
,e.*
FROM employees e
CONNECT BY PRIOR e.employee_id = e.manager_id
START WITH e.last_name = 'King'
ORDER BY LEVEL DESC;
##基础函数
###数字的玩法
改变金钱前面的字符
alter session set NLS_CURRENCY = ‘¥';
SELECT to_char(432553425.1
,'L000,000,000,000.000')
FROM dual;
--¥000,432,553,425.100
SELECT to_char(2341.3241
,'L0999,999,999.0000')
FROM dual;
-- ¥0000,002,341.3241
SELECT to_char(2341.3241
,'L9999,999,999.0000')
FROM dual;
-->2,341.3241
日期转换
- 年
–>twenty seventeen
SELECT to_char(SYSDATE
,'year')
FROM dual;
–>2017
SELECT to_char(SYSDATE
,'yyyy')
FROM dual;
- 月
–>12
SELECT to_char(SYSDATE
,'mm')
FROM dual;
SELECT to_char(SYSDATE
,'month')
FROM dual;
-->december
SELECT to_char(SYSDATE
,'mon')
FROM dual;
-->dec
- 日
–>5 这个月的第几天
SELECT to_char(SYSDATE
,'dd')
FROM dual;
–tuesday
SELECT to_char(SYSDATE
,'day')
FROM dual;
–>fifth
SELECT to_char(SYSDATE
,'ddspth')
FROM dual;
–>tue
SELECT to_char(SYSDATE
,'dy')
FROM dual;
- 格式化时间
–>02:13:54:pm
SELECT to_char(SYSDATE
,'hh:mi:ss:am')
FROM dual;
–>现在是2017年12月05日 下午 16:19:58 星期二
SELECT REPLACE('现在是' || to_char(SYSDATE
,'yyyy"年"') ||
to_char(to_char(SYSDATE
,'mm')
,'99') || '月' ||
to_char(to_char(SYSDATE
,'DD')
,'00') || '日'
,' '
,'') || ' ' || decode(to_char(SYSDATE
,'am')
,'am'
,'上午'
,'下午') || ' ' ||
to_char(SYSDATE
,'hh24:mi:ss') || decode(to_char(SYSDATE
,'dy')
,'tue'
,' 星期二') 我的日期
,SYSDATE 默认日期
FROM dual;
字符串的一些玩法
--INSTR(源字符串, 要查找的字符串, 从第几个字符开始, 要找到第几个匹配的序号)返回找到的位置,如果找不到则返回0.
SELECT INSTR('hello world','d',1,1) FROM dual; --结果为11
SELECT INSTR('hello world','o',1,2) FROM dual; -- 结果为8
比较屌一点的函数–>分析函数
partition函数,块函数,分组函数。。随你叫吧
- 这里以
- 求一个员工的employees表里面的信息,以及这个员工所在部门的最大工资,(注:有员工不在任何部门)
SELECT MAX(e.salary) over(PARTITION BY e.department_id) "最大工资"
,e.*
FROM employees e
ORDER BY e.employee_id;SELECT e2.max_sal "最大工资" ,e.* FROM employees e ,(SELECT e.department_id ,MAX(e.salary) max_sal FROM employees e GROUP BY e.department_id) e2 WHERE e.department_id = e2.department_id ORDER BY e.employee_id;以上结果看的眼花的话请看以下sql
SELECT DISTINCT (e2.max_sal) 最大工资 ,e2.department_id FROM employees e ,(SELECT e.department_id ,MAX(e.salary) max_sal FROM employees e GROUP BY e.department_id) e2 WHERE e.department_id = e2.department_id ORDER BY e2.department_id;SELECT DISTINCT (MAX(e.salary) over(PARTITION BY e.department_id)) 最大工资 ,e.department_id FROM employees e ORDER BY e.department_id;- 请再次比较以下sql,注意where条件(null!=null)
SELECT DISTINCT (e2.max_sal) 最大工资
,e2.department_id
FROM employees e
,(SELECT e.department_id
,MAX(e.salary) max_sal
FROM employees e
GROUP BY e.department_id) e2
WHERE e.department_id = e2.department_id
OR (e.department_id IS NULL
AND e2.department_id IS NULL)
ORDER BY e2.department_id;
排名函数
- 入门级,对50号部门的员工从工资方面进行rank(),dense_rank(),row_number()的排序
SELECT e.salary
,e.department_id"部门"
,rank() over(ORDER BY e.salary DESC) rank
, -- 重复的不算一个 1,1,1,4,4,6,6,6,9.....
dense_rank() over(ORDER BY e.salary DESC) dense_rank
, -- 重复的算一个 1,1,1,1,2,2,2,2,3.................
row_number() over(ORDER BY e.department_id DESC) row_number -- 从小到大无重复值 1,2,3,4,5,6.......
FROM employees e
WHERE e.department_id = 50;- 进阶级别 从各个部门出发来为每个员工进行排名
SELECT d.department_name
,e.last_name
,e.salary
,rank() over(PARTITION BY d.department_name ORDER BY e.salary DESC) dept_salary_rank1
,dense_rank() over(PARTITION BY d.department_name ORDER BY e.salary DESC) dept_salary_rank2
,row_number() over(PARTITION BY d.department_name ORDER BY e.salary DESC) dept_salary_rank3
FROM employees e
,departments d
WHERE 1 = 1
AND e.department_id = d.department_id;
视图
- 准备点数据
-- 创建表test_1206
CREATE TABLE test_1206 (
ID NUMBER(8),
NAME VARCHAR2(20),
birthday DATE DEFAULT SYSDATE
);
--修改表名字
ALTER TABLE test_1206 RENAME TO table_1206;
-- 查询表内容
SELECT * FROM table_1206;-- 结果为空
--插入一条数据
INSERT INTO table_1206 VALUES (1,'xiaoli',to_date('19960206','yyyymmdd'));
-- 查询表内容
SELECT * FROM table_1206; -- 有结果
commie; --就练习一次,这个随你开心就好
简单视图
--创建视图 CREATE VIEW view_1206 AS SELECT * FROM table_1206 WHERE ID = 1; -- 通过视图获取信息 SELECT * FROM view_1206; -- 可以查到结果(注意where条件) -- 从视图向表中插入数据 INSERT INTO view_1206 VALUES(4,'xiaoli',SYSDATE);-- where条件不影响插入 SELECT * FROM TABLE_1206; --注意where条件,所以从表中直接查询,查询结果为已经插入 -- update也一样,注意where条件,逻辑说得过去就行,因为视图毕竟就是一个子查询
复杂视图
-- 创建视图 CREATE VIEW view_1206_complicated AS (SELECT NAME,COUNT(1) 统计同名的人 FROM table_1206 GROUP BY NAME); select * FROM view_1206_complicated; --通过视图查询 -- 现在你也应该大约了解为什么复杂视图不能进行DML操作了吧
删除视图
DROP VIEW view_1206_complicated;
- 总结
--最简单的方法去理解视图,上面那条简单视图插入的效果相同,复杂先抛开不说,首先它极有可能是不具备业务意义的 INSERT INTO (SELECT * FROM table_1206 WHERE id = 1) VALUES (7 ,'Tom' ,SYSDATE); --对我这个新手来说,视图就是对用的频繁的子查询进行缩写,暂且可以认为它是一张表
序列
创建序列
create sequence my_index minvalue 1 --最小值 nomaxvalue --不设置最大值 start with 1 --从1开始计数 increment by 1 --每次加1个 nocycle --一直累加,不循环 nocache; --不建缓冲区 /* 如果你给出了cache值那么系统将自动读取你的cache值大小个seq, 这样在反复操作时会加快运行速度,但如果遭遇意外情况如当机了或oracle死了, 则下次取出的seq值将和上次的不连贯.如果没有强迫症连或者业务要求不连贯无所谓建议用cache. 假如缓冲区为200,这次用到了50,当机器关机后,下次在开启机器,会从201开始 */
使用序列
SELECT my_index.nextval FROM dual;
SELECT my_index.currval FROM dual;--刚创建的序列不能直接得到当前值,需要用nextval初始化
insert进阶。。。。随便看看吧,用的机会应该不多(注意别名)
- 一个来源插入多个目标表(无条件)
INSERT ALL INTO sal_history
VALUES
(empid
,hiredate
,sal) INTO mgr_history
VALUES
(empid
,mgr
,sal)
SELECT employee_id empid
,hire_date hiredate
,salary sal
,manager_id mgr
FROM employees
WHERE employee_id > 200;
- 一个来源插入多个目标表(有条件)
INSERT ALL INTO sal_history
VALUES
(empid
,hiredate
,sal) INTO mgr_history
VALUES
(empid
,mgr
,sal)
SELECT employee_id empid
,hire_date hiredate
,salary sal
,manager_id mgr
FROM employees
WHERE employee_id > 200;
- 一个来源插入多个目标表(有条件,首次匹配即跳到下一条,也就是说满足一个条件就插入,但是只插入一次)
INSERT FIRST WHEN sal > 25000 THEN INTO special_sal
VALUES
(deptid
,sal) WHEN hiredate LIKE
('%00%') THEN INTO hiredate_history_00
VALUES
(deptid
,hiredate) WHEN hiredate LIKE
('%99%') THEN INTO hiredate_history_99
VALUES
(deptid
,hiredate) ELSE INTO hiredate_history
VALUES
(deptid
,hiredate)
SELECT department_id deptid
,SUM(salary) sal
,MAX(hire_date) hiredate
FROM employees
GROUP BY department_id;
- 列转行(一行变多行,交叉报表的反操作,就是选择性插入查询结果,具有分类的效果)
INSERT ALL INTO sales_info
VALUES
(employee_id
,week_id
,sales_mon) INTO sales_info
VALUES
(employee_id
,week_id
,sales_tue) INTO sales_info
VALUES
(employee_id
,week_id
,sales_wed) INTO sales_info
VALUES
(employee_id
,week_id
,sales_thur) INTO sales_info
VALUES
(employee_id
,week_id
,sales_fri)
SELECT employee_id
,week_id
,sales_mon
,sales_tue
,sales_wed
,sales_thur
,sales_fri
FROM sales_source_data;
delete进阶
进阶第一段如果不是像我一样有强迫症,就别看了。
- 准备数据
CREATE TABLE test_bin AS
SELECT e.employee_id
,e.last_name
FROM employees e;
SELECT * FROM test_bin;
- 运行下面语句注意观察,最好截图
SELECT * FROM tab;
- 删库跑路
drop TABLE test_bin;
- 运行下面语句注意观察,对比上次查询结果
SELECT * FROM tab;
- 查询回收站的数据
SELECT * FROM recyclebin;
- 如想查询已经删除的库,注意双引号
SELECT * FROM "BIN$owBmXtYwQbGx1UIC/tv3kA==$0";
– 清空回收站,这次就真的得跑路了。。。。。
purge recyclebin;
- delete直接清空表,仅仅一次,删的连渣都不剩了。。。。
CREATE TABLE test_bin AS
SELECT e.employee_id
,e.last_name
FROM employees e;
SELECT * FROM test_bin;
DROP TABLE test_bin PURGE;
SELECT * FROM recyclebin;
闪回(注意:闪回不局限与delete,看了你就明白了)
- 准备数据
CREATE TABLE timestamp_test_table AS
SELECT e.employee_id
,e.last_name
FROM employees e;
SELECT * FROM timestamp_test_table; -- 查询数据是否插入,首先看看是有数据的哦,可别说我骗你
COMMIT;--DDL是已经带有commit功能的,这个提交是为了让你玩的放心 - 等几分钟,删库跑路(这里为什么要等几分钟,为了让你等等能把时间对准。。。。)
DELETE FROM timestamp_test_table;
COMMIT;
SELECT * FROM timestamp_test_table; -- 这可是真的删了哦 - 查询一分钟之前数据库的东西,这就叫闪回
SELECT * FROM timestamp_test_table AS OF TIMESTAMP SYSDATE-1/(24*60);
-- 下面这句是查询二十分钟之前的数据库的东西(这也是为啥让你等几分钟删除了)
SELECT * FROM timestamp_test_table AS OF TIMESTAMP SYSDATE-20/(24*60);
其他乌七八糟的东西
全局临时表
- 全局临时表 在不同的回话之间可以屏蔽数据,可以有触发器,检查约束,索引 等。比如在程序的执行过程的当前回话中需要临时存放一些数据,这些数据是其他回话无法访问的,此时全局临时表就是一个很好的方案。
基于会话
CREATE global temporary TABLE temp_table_session
(tid NUMBER,tname VARCHAR2(20))
ON COMMIT preserve rows;-
插入数据
INSERT INTO temp_table_session
SELECT e.employee_id
,e.last_name
FROM employees e;
SELECT * FROM temp_table_session;
COMMIT;
SELECT * FROM temp_table_session; -
重新开启一个会话或者重启客户端,重新连接,随你
SELECT * FROM temp_table_session;--查不到数据啦。。。。
基于事务
CREATE global temporary TABLE temp_table_session2 (tid NUMBER,tname VARCHAR2(20)) ON COMMIT DELETE ROWS;
-
插入数据
INSERT INTO temp_table_session2
SELECT e.employee_id
,e.last_name
FROM employees e;
SELECT * FROM temp_table_session2;
COMMIT;
SELECT * FROM temp_table_session2;