首页 > 代码库 > oracle sql 高级编程 历史笔记整理
oracle sql 高级编程 历史笔记整理
20130909 周一
oracle sql 开发指南 第7章 高级查询
1.层次化查询select level,ttt.*,sys_connect_by_path(ttt.col1,‘,‘) from ttt start with .. Connect by prior …
因为先建立树,再进行where过滤的。在where中过滤和在cooonect by中过滤是不一样的。
2.rollup cube高级查询 select grouping(col1) .. From ttt group by rollup/cube(col1,col2),这里的select中只能使用col1,col2 和统计函数,和不用rollup一样,只是数据多了关于分组的统计信息。
3.not in 和 =<> + all应该注意null值
oracle sql 高级编程 第1章 sql核心
- Sql Plus登陆
- Sql Plus帮助
帮助列表
具体项目的帮助
- 执行sql文件
- 多表插入
INSERT ALL
WHEN order_total < 100000 THEN
INTO small_orders
WHEN order_total > 100000 AND order_total < 200000 THEN
INTO medium_orders
ELSE
INTO large_orders
SELECT order_id, order_total, sales_rep_id, customer_id
FROM orders;
INSERT FIRST
WHEN ottl < 100000 THEN
INTO small_orders
VALUES(oid, ottl, sid, cid)
WHEN ottl > 100000 and ottl < 200000 THEN
INTO medium_orders
VALUES(oid, ottl, sid, cid)
WHEN ottl > 290000 THEN
INTO special_orders
WHEN ottl > 200000 THEN
INTO large_orders
VALUES(oid, ottl, sid, cid)
SELECT o.order_id oid, o.customer_id cid, o.order_total ottl,
o.sales_rep_id sid, c.credit_limit cl, c.cust_email cem
FROM orders o, customers c
WHERE o.customer_id = c.customer_id;
- Merge(复杂的update语句可以用merge实现,不记update了)
MERGE INTO bonuses D
USING (SELECT employee_id, salary, department_id FROM employees
WHERE department_id = 80) S
ON (D.employee_id = S.employee_id)
WHEN MATCHED THEN UPDATE SET D.bonus = D.bonus + S.salary*.01
DELETE WHERE (S.salary > 8000)
WHEN NOT MATCHED THEN INSERT (D.employee_id, D.bonus)
VALUES (S.employee_id, S.salary*0.1)
WHERE (S.salary <= 8000);
oracle sql 高级编程 第2章 sql执行
1.sql执行步骤:
验证--->查询转换--->确定执行计划--->执行sql并取得结果
2.查询转换 /*+ no_query_transformation */
视图合并(在select中是视图) /*+ no_merge */
子查询解嵌套(在where中是子查询) /*+ no_unnest */
谓语前推(最常用 最好用)
利用物化视图进行查询重写
3. 对于这几种的控制,大部分情况下谓语前推肯定是要允许的,试图合并也是应该的,子查询解嵌套的应用场景较少,只在子查询保证关联列的唯一性的时候才可使用,否则会影响结果的行数。物化视图没用过,不过如果有物化视图,进行重写肯定不应该禁止的。
20130910 周二
oracle sql 高级编程 第3章 访问和连接方法
1.全表扫描或者索引扫描
2.数据块是最小单元 一张表的高水位线是最后一块有数据写入的数据块
删除了部分数据之后,高水位线不会立刻跌落到删除后的位置。但是执行全表扫描时,将会一直扫描到高水位线处为止,可能包含空白数据。所以说如果执行了大数据的删除,最还重新对表生成统计信息。
3.获取表分配的数据块数:
select blocks from user_segments where segment_name = ‘@table_name‘
4.获取实际的有数据的数据数据块数:
select count(distinct dbms_rowid.rowid_block_number(rowid)) from table_name;
5.索引的聚簇因子 索引代表的列的不同值在数据块中的分布情况 紧凑还是稀疏 越紧凑越适合使用索引扫描方式 聚簇因子代表数据的存储分布情况 重建索引不能改变据簇因子的值
联接方法:
嵌套循环联接:nested loops 特点:如果有一张表的数据较少可以作为驱动表的话 适合使用这种联接
排序合并联接:sort join merge join 特点排序的开销比较大,如果表过大的话,在使用内存的同时会使用临时磁盘空间,所以对资源比较消耗,但是 如果两张表比较大 而且联接条件是非等式的时候,这种联接就是唯一的选择了。
散列联接:hash join 特点:两张表都比较大 并且是等联接的情况下,使用这个。
笛卡尔联接。
一大一小:嵌套循环
两个都大的等连:散列连接
两个都打的不等连接:排序合并
20130911周三
oracle sql 高级编程 第四章 sql是关于集合的
1.面向集合的角度思考问题
2.执行计划中的filter操作
执行过滤操作
如果filter下面针对的是一张表,那么就是简单的过滤
如果filter下面针对的是两张表,那么就是以第一张表为驱动表,驱动表中的每一行,都要执行第二张表(内层表)的查询一次,效率极低。这种情况一般用在in,exist的关联子查询并且无法解嵌套的情况。
3.union minus intersect操作都会默认执行类似distinct一样的去除重复行的操作。Union all不会。
4.在集合操作(union minus intersect)和group by操作中,null会作为一个特定的值来运算,在这些操作中null = null。
5.在sum avg count中将忽略null。
20130912周四
oracle sql 开发指南 第7章 高级查询
概括:对某个区间(全表 partition by之后的分组 自定义的窗口)执行row_number sum count min max avg first_value last_value
lag lead等操作。可以获得区间的行号,前几个后几个的值,第一个最后一个的值,sum等统计信息。
以下示例中红色是可选的
1.row_number函数 不接受参数 返回行号
select row_number() over (partition by col1 order by col2) from table_name;
如果有group by,将会先执行group by,在执行窗口操作,那么col1和col2必须是group by之后可以出现在select中的有效列。
2.窗口函数:窗口函数是在一个自定义的"窗口"中执行sum count min max avg first_value last_value等函数,这些函数都要传递一个列名作为参数。
定义窗口:
partition by col2 order by col1 rows between (current row)/(unbounded/123.. preceding) and (current row)/(unbounded/123.. following)
例如:
select sum(col1) over (order by col2 rows between unbounded preceding and current row) from table_name;
3.报表函数:对partition by的结果集执行sum count min max avg。这和group by的效果是一样的,只不过group by限制结果集中只能包含group by的列和sum count等列。而报表函数能够打破这个限制。例如:
select s.*,sum(s.col1) over(partiton by s.col2),sum(s.col3) over(partiton by s.col4) from table_name s;
4.lag和lead 获取前面第n个的值 或者 后面第n个的值 不能用在窗口函数中,只能over(partition by col2 order by col1)
例如:
select lead(col1) over (order by col2) from table_name;
oracle sql 高级编程 第6章 执行计划
如何获取实际的执行计划:
select /*+ gather_plan_statistics pub_organ_sig */ * from pub_organ o where o.organ_code like ‘2562%‘;
select * from v$sql s where s.SQL_TEXT like ‘%pub_organ_sig%‘
select * from table(dbms_xplan.display_cursor(‘2cg64wudvfkr6‘,null,‘ALLSTATS LAST -COST -BYTES‘));
PLAN_TABLE_OUTPUT |
SQL_ID 2cm7ax09dcpjf, child number 0 |
------------------------------------- |
select /*+ gather_plan_statistics pub_organ_sig */ * from pub_organ o where |
o.organ_code like ‘2562%‘ |
|
Plan hash value: 33305308 |
|
----------------------------------------------------------------------------------------- |
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | |
----------------------------------------------------------------------------------------- |
|* 1 | TABLE ACCESS FULL| PUB_ORGAN | 1 | 2 | 0 |00:00:00.01 | 240 | |
----------------------------------------------------------------------------------------- |
|
Predicate Information (identified by operation id): |
--------------------------------------------------- |
|
1 - filter("O"."ORGAN_CODE" LIKE ‘2562%‘) |
|
E-Rows : expect rows 预测的行
A-Rows : actual rows 实际的行
如果A-Rows比E-Rows小的多,那么需要重新生成统计信息。
重新生成统计信息:
在命令窗口中执行: EXEC dbms_stats.gather_table_stats(user,‘PUB_ORGAN‘,estimate_percent => 100, cascade => TRUE,method_opt => ‘FOR ALL COLUMNS SIZE 1‘);
在sql窗口中执行: begin dbms_stats.gather_table_stats(user,‘PUB_ORGAN‘,estimate_percent => 100, cascade => TRUE,method_opt => ‘FOR ALL COLUMNS SIZE 1‘); end;
begin dbms_stats.gather_table_stats方法:
This procedure gathers table and column (and index) statistics. It attempts to
parallelize as much of the work as possible, but there are some restrictions as
described in the individual parameters.
用来生成表,列,索引的统计信息。
所有参数:
DBMS_STATS.GATHER_TABLE_STATS (
ownname VARCHAR2,
tabname VARCHAR2,
partname VARCHAR2 DEFAULT NULL,
estimate_percent NUMBER DEFAULT to_estimate_percent_type
(get_param(‘ESTIMATE_PERCENT‘)),
block_sample BOOLEAN DEFAULT FALSE,
method_opt VARCHAR2 DEFAULT get_param(‘METHOD_OPT‘),
degree NUMBER DEFAULT to_degree_type(get_param(‘DEGREE‘)),
granularity VARCHAR2 DEFAULT GET_PARAM(‘GRANULARITY‘),
cascade BOOLEAN DEFAULT to_cascade_type(get_param(‘CASCADE‘)),
stattab VARCHAR2 DEFAULT NULL,
statid VARCHAR2 DEFAULT NULL,
statown VARCHAR2 DEFAULT NULL,
no_invalidate BOOLEAN DEFAULT to_no_invalidate_type (
get_param(‘NO_INVALIDATE‘)),
force BOOLEAN DEFAULT FALSE);
具体参数用途介绍:
ownname:表所属是用户的用户名。
tabname:表名。
estimate_percent: 采样的数据百分比。0.00001-100的数值。如果不传入此参数 ,oracle将会使用oracle认为最有的值进行分析,如果传入null,将分析全部数据。相当于传入100.如果不传入此参数,oracle将会是用get_param(‘ESTIMATE_PERCENT‘),此函数返回DBMS_STATS.AUTO_SAMPLE_SIZE常量值,此值为0.也就是说,传入0时,将有oracle决定最有的百分比。
cascade :是否统计索引信息。详细英文文档解释:Gather statistics on the indexes for this table. Index statistics gathering is not parallelized. Using this option is equivalent to
running the GATHER_INDEX_STATS Procedure on each of th
table‘s indexes. Use the constant DBMS_STATS.AUTO_
CASCADE to have Oracle determine whether index statistics to
be collected or not. This is the default. The default value can b
changed using theSET_PARAM Procedure.
degree:并行度。
method_opt 统计那些列需要统计:
■ FOR ALL [INDEXED | HIDDEN] COLUMNS [size_
clause]
■ FOR COLUMNS [size clause] column|attribut
[size_clause] [,column|attribute [size_
clause]...]
size_clause is defined as size_clause := SIZ
{integer | REPEAT | AUTO | SKEWONLY}
- integer : Number of histogram buckets. Must be in the
range [1,254].
- REPEAT : Collects histograms only on the columns that
already have histograms.
- AUTO : Oracle determines the columns to collect histogram
based on data distribution and the workload of the columns
- SKEWONLY : Oracle determines the columns to collect
histograms based on the data distribution of the columns.
The default is FOR ALL COLUMNS SIZE AUTO.The defaul
value can be changed using the SET_PARAM Procedure.
20130930周一
oracle sql 开发指南 第7章 高级查询
- sql的执行步骤:where操作---group by操作---having---窗口操作---order by操作。所以说窗口操作时在过滤和分组之后的结果集上执行的。
- 窗口函数的常用功能:
计算累计和
计算移动平均值
计算中心平均值
获取上一条(第一条),下一条(最后一条)记录的值。
报表函数
- first_value() last_value()与lag lead的区别:
前者:
获取上一条的值:
select z.zyear,
z.zmonth,
z.jxzbid,
count(1),
first_value(count(1)) over(order by z.zyear, z.zmonth, z.jxzbid rows between 1 preceding and current row)
from Z00HRJDWWCZ z
group by z.zyear, z.zmonth, z.jxzbid
order by z.zyear, z.zmonth, z.jxzbid
获取第一条的值:
select z.zyear,
z.zmonth,
z.jxzbid,
count(1),
first_value(count(1)) over(order by z.zyear, z.zmonth, z.jxzbid)
from Z00HRJDWWCZ z
group by z.zyear, z.zmonth, z.jxzbid
order by z.zyear, z.zmonth, z.jxzbid
lag获取上一条的值:
select z.zyear,
z.zmonth,
z.jxzbid,
count(1),
lag(count(1),1) over(order by z.zyear, z.zmonth, z.jxzbid)
from Z00HRJDWWCZ z
group by z.zyear, z.zmonth, z.jxzbid
order by z.zyear, z.zmonth, z.jxzbid
如果没有上一个,lag取出来为空,而first_value取出来为当前行的值。
lag不支持开窗子句。
- 报表函数
报表函数可以无障碍的对自定义的分组执行sum等统计操作。
如:
select z.zyear,
z.zmonth,
z.jxzbid,
count(1) over(partition by z.zyear),--当前年有多少条
count(1) over(partition by z.zmonth),--当前月有多少条
count(1) over(partition by z.jxzbid)--当前指标有多少条
from Z00HRJDWWCZ z
order by z.zyear, z.zmonth, z.jxzbid
oracle sql 高级编程 第7章 高级分组
- Group by不能保证排序。必须加order by。
20131008 周二
oracle sql 高级编程 第8章 分析函数
- group by会缩减行数,分析函数会保持行数不变。
- partition by字句中的列支持谓语前推,但是order by中的列不支持。如:
PLAN_TABLE_OUTPUT |
SQL_ID ab4nygf153qa5, child number 0 |
------------------------------------- |
with max_v as (select /*+ gather_plan_statistics zjf_flag */ t.zyear, t.zmonth, max(t.bndywcz) |
over(partition by t.zyear order by t.zmonth) from Z00HRJDWWCZ t) select * from max_v v where v.zyear = |
‘2011‘ and v.zmonth = ‘2‘ |
|
Plan hash value: 1995484584 |
|
------------------------------------------------------------------------------------------------------------------------ |
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | O/1/M | |
------------------------------------------------------------------------------------------------------------------------ |
|* 1 | VIEW | | 1 | 4053 | 100 |00:00:00.01 | 38 | | | | |
| 2 | WINDOW SORT | | 1 | 4053 | 1981 |00:00:00.01 | 38 | 160K| 160K| 1/0/0| |
|* 3 | TABLE ACCESS FULL| Z00HRJDWWCZ | 1 | 4053 | 4053 |00:00:00.01 | 38 | | | | |
------------------------------------------------------------------------------------------------------------------------ |
|
Predicate Information (identified by operation id): |
--------------------------------------------------- |
|
1 - filter("V"."ZMONTH"=‘2‘) |
3 - filter("T"."ZYEAR"=‘2011‘) |
|
filter("V"."ZMONTH"=‘2‘)操作是在window操作之后执行的,没有执行谓语前推。此种情况下ZMONTH的索引也不会被使用到。
oracle sql 高级编程 第10章 子查询因子化
- 只能用as,不能用is。子查询用括号括起来,各个子查询中间用逗号分隔。最后一个不加逗号.
- inline 内联视图,materialize 临时物化视图。
oracle sql 高级编程 第10章 子查询因子化
- 执行计划中字段的说明:
Starts:当前操作的执行次数。
E-Rows:当前操作预计返回的行数
A-Rows :当前操作在实际运行时返回的行数
A-Time:当前操作在实际运行是耗费的时间
Buffers:当前操作在执行期间进行的逻辑读操作数量(从buffer中读取 如果没有 就执行物理读)
Reads:当前操作在执行期间进行的物理读操作数量(从磁盘读入buffer)
OMem:最优执行时所需内存的预估值
1Mem:一次通过所需内存的预估值
Used-Mem:最后一次操作使用的内存量 - 编写sql的时候:
2.1、 对于复杂的sql,尽量使用子查询因子化进行拆分。这样看起来一目了然。
2.2、 对于拆分后的子查询,不需要刚开始就加上materialize进行优化。oracle会自动选择是否创建临时表来缓存子查询的结果集来进行最优的查询。如果oracle没有给出最优的速度,可以考虑使用materialize进行优化。
20131011 周五
oracle sql 高级编程 第11章 半连接
- in和= any是一模一样的。
- 半连接的执行计划是在普通的哈希联接 循环迭代联接 等的基础上,加上semi,而反连接是加上anti。这两种是专门为in ,exist, not in,not exist准备的高效的执行计划。低效的处理in ,exists、, not in,not exists的 执行计划是filter。
- 在or中使用in exists将不会使用半连接执行计划,而是filter。如
PLAN_TABLE_OUTPUT |
SQL_ID arcp0y6pauqmc, child number 0 |
------------------------------------- |
select /*+ gather_plan_statistics zjf_flag */ * from pub_functions s where |
s.function_name like ‘%YJKH%‘ or exists (select null from pub_resources f where |
s.function_id = f.function_id) |
|
Plan hash value: 21887211 |
|
---------------------------------------------------------------------------------------------- |
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | |
---------------------------------------------------------------------------------------------- |
|* 1 | FILTER | | 1 | | 100 |00:00:00.01 | 1297 | |
| 2 | TABLE ACCESS FULL| PUB_FUNCTIONS | 1 | 383 | 102 |00:00:00.01 | 5 | |
|* 3 | TABLE ACCESS FULL| PUB_RESOURCES | 102 | 2 | 100 |00:00:00.01 | 1292 | |
---------------------------------------------------------------------------------------------- |
|
Predicate Information (identified by operation id): |
--------------------------------------------------- |
|
1 - filter(("S"."FUNCTION_NAME" LIKE ‘%YJKH%‘ OR IS NOT NULL)) |
3 - filter("F"."FUNCTION_ID"=:B1) |
|
使用(不加not的)in 还是exists,对于性能不会有影响。oralce会根据查询的特点,自动选择半连接,如果不能选择半连接,就使用filter,但这跟in或者exists的取舍已经无关了。
- 不要使用not in,使用 not exists。原因:not in大部分情况下不是我们想要的结果,因为null的问题。而且在oralce 11g之前(在11g中 有专门处理含空值的反连接的执行计划 anti na),如果oracle不能确保子查询的结果集不包含null值,就不会使用反连接,而是filter。如我将pub_resource的function_id列改为可为空之后,下列sql的执行计划是(甚至加上is not null都没用 我都被惊呆了):
PLAN_TABLE_OUTPUT |
SQL_ID as36cfwft7khf, child number 0 |
------------------------------------- |
select /*+ gather_plan_statistics zjf_flag1 */ * from pub_functions s where |
s.function_id not in (select f.function_id from pub_resources f ) |
|
Plan hash value: 21887211 |
|
---------------------------------------------------------------------------------------------- |
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | |
---------------------------------------------------------------------------------------------- |
|* 1 | FILTER | | 1 | | 8 |00:00:00.03 | 9572 | |
| 2 | TABLE ACCESS FULL| PUB_FUNCTIONS | 1 | 383 | 376 |00:00:00.01 | 7 | |
|* 3 | TABLE ACCESS FULL| PUB_RESOURCES | 376 | 2 | 368 |00:00:00.03 | 9565 | |
---------------------------------------------------------------------------------------------- |
|
Predicate Information (identified by operation id): |
--------------------------------------------------- |
|
1 - filter( IS NULL) |
3 - filter(LNNVL("F"."FUNCTION_ID"<>:B1))
|
oracle sql 高级编程 第12章 索引
- oracle索引的类型:常规索引(b+树索引),唯一索引,bitmap索引(位图索引)。
- 位图索引适用于值比较少的比如性别,人员表中的城市列等。位图索引不适合建立在经常更新的列上,所以常在数据仓库中使用。
- 索引列占用空间太大,建立的索引将占用很大的空间,性能也会随之降低。
- 建立复合索引的时候应该将最常用的列放在前面。
- RBO只选择复合索引开头的一列或者几列进行取舍,CBO可以选择复合索引中间的列进行取舍,但是这种情况下效率不如开头的列。所以将常用的查询列放在开头是对的。
- 对列使用函数将不会使用到列的索引,可以建立基于函数的索引。
create index pub_functions_inx_subname on pub_functions(substr(function_name,1,1))
- 单列索引不会存储null值,所以:
create index pub_functions_idx_name on pub_functions(function_name)
PLAN_TABLE_OUTPUT |
SQL_ID 6q1h79t54h0dd, child number 0 |
------------------------------------- |
select /*+ gather_plan_statistics zjf_flag */ * from pub_functions s where |
s.function_name is null |
|
Plan hash value: 2496286864 |
|
------------------------------------------------------------------------------------ |
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | |
------------------------------------------------------------------------------------ |
|* 1 | FILTER | | 1 | | 0 |00:00:00.01 | |
| 2 | TABLE ACCESS FULL| PUB_FUNCTIONS | 0 | 383 | 0 |00:00:00.01 | |
------------------------------------------------------------------------------------ |
|
Predicate Information (identified by operation id): |
--------------------------------------------------- |
|
1 - filter(NULL IS NOT NULL) |
|
- 据说复合索引会储存null,但是在10g上没有试验成功。
- 索引表空间:
oralce没有为索引创建单独的表空间提供全局的语法支持。但是为了性能,把索引的表空间放在和数据的表空间不同的磁盘上,将会有效的防止IO问题。
方法:
首先创建一个表空间。create tablespace ts_index …
其次,在创建索引时指定表空间。create index… on … tablespace ts_index;
20131012 周六
oracle概念手册中文版 第4章 事务管理
- 事务具有原子性,要么全部提交,要么全部回滚。
- 事务开始于第一条可执行的sql语句,结束语commit或者rollback操作。执行一个ddl操作可以自动的触发commit操作。
- 下列java代码:
DataSource ds = DataSourceGen.getDataSourceFromXML();
Connection conn = ds.getConnection();
PreparedStatement ps = conn.prepareStatement("update pub_organ o set o.organ_name = ‘221112‘ where o.organ_id = ‘O50649824‘");
ps.executeUpdate();
ps = conn.prepareStatement("update pub_organ o set o.in_use = ‘1111‘ where o.organ_id = ‘O50649824‘");//此处会报错 因为in_use只能有一位
ps.executeUpdate();
conn.close();
每次执行executeUpdate都会自动触发commit操作,所以第二个报错,第一个仍然后执行成功。
如果加上conn.setAutoCommit(false);那么将不会再executeUpdate的时候自动触发commit,这种情形下将会在两种commit,一个是commit代码,一个是关闭conn自动触发commit。
也就是说如果不加conn.setAutoCommit(false);,那么每条sql都是一个事务。
- 执行一个ddl操作可以自动的触发commit操作。
begin
update pub_organ o set o.organ_name = ‘1‘ where o.organ_id = ‘O50649824‘; --事务开始第一个可执行语句
execute immediate ‘create or replace view vvvv as select * from pub_organ‘;--ddl操作 结束上一个事务 并commit(ddl操作本身要占用一条事务)
update pub_organ o set o.organ_name = ‘2‘ where o.organ_id = ‘O50649824‘; --新事务开始
update pub_organ o set o.in_use = ‘ddd0‘ where o.organ_id = ‘O50649824‘;--此处会报错 rollback
commit;
end;
oracle概念手册中文版 第13章 事务管理
- oracle强制实现语句级读一致性。一个再复杂的查询,即使耗时很长,它最终执行获取的数据都是执行开始那个时间点的数据,执行过程中其他用户提交的改变不会造成影响。
- 隐式的查询,如delete和update中的where字句,insert中的子查询等,都能保证语句级读一致性。
- 如果 SELECT 列表中存在 PL/SQL 函数,那么函数中包含的 SQL 语句将遵从其自身的语句级读一致性,而非其所在 SQL 的读一致性.
- 当一个事务运行在串行化模式(serializable mode)下时,Oracle 还能够实现事务级读一致性(transaction-level read consistency),事务内所有数据访问均反映的是事务开始时的数据状态。
- 需要预防的现象:
5.1 脏读 读取到其他用户未提交的数据 oracle实现了语句读一致性,所以不可能出现脏读的现象。
5.2 不可重复读 同一条数据两次读取的结果不一样
5.3 幻读 同一个where条件过滤 两次读取结果集不一样 - 隔离级别
已提交读取:默认的隔离级别。可以防止脏读。
串行化读取:oracle从已提交读取直接跳到了串行化读取,没有针对不可重复读和幻读分别提供隔离级别。
只读模式:只读事务只能看到事务执行前就已经提交的数据,且事务中不能执行 INSERT,UPDATE,及 DELETE 语句。这个是oracle的创新,不是sql的标准。
分析:oracle这种设计,你要么使用默认的隔离级别,要么就串行。只读模式应用场景较少。 - 设置事务的隔离级别。
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET TRANSACTION READ ONLY; - 已提交读取和串行化读取中:
读操作都不会阻塞其他用户的写操作。
已提交读取中sql的执行查询到的是sql执行那一点的数据状态。
串行化读取中slq的执行查询到的是事务开始那一点的数据状态。
- 串行化读取并不是真的串行的。串行事务中读取的内容可以被其他用户修改并提交,只是当前事务看不到而已。串行事务不能解决所有的问题,有时候需要在应用层开发代码来进行控制。
20131014 周一
oracle概念手册中文版 第13章 事务管理
- 如果系统中存在长时间运行的写事务,且其所操作的数据同时还会被大量的小事务更新,则此类系统不应采用串行化模式。因为长事务所需更新的数据可能会被其他事务抢先更新,则长事务可能需要重复地回滚,浪费系统资源。需要注意的是,其他数据库管理系统所实现的串行化隔离(使用读取锁(read-locking))同样不适合上述情况,因为长事务(即便是只执行读取操作的事务)会和短小的写事务相互阻塞。
- 使用select * form ttt for update;(在存储过程中需要在execute immediately中执行)可以获取对应行的排他锁。排他锁是一种阻止其他事务进行更新的锁。
- 获取表的排他锁:
lock table pub_organ in exclusive mode;锁定整张表 其他用户不可以执行dml操作,也不可以获取其他任何锁。获取表的共享锁:
lock table pub_organ in share mode;;锁定整张表 其他用户不可以执行dml操作,但是也可以获取共享锁。不管是那种模式,lock table的代码就像一句update的代码一样,遇到commit或者rollback后才会解除lock。如果表中的某一行被更新未提交,或者插入为提交,那么lock操作将阻塞。
20131015 周二
oracle sql 开发指南 第16章 SQL优化
- 对同一张表的不同where语句的查询,可以通过case简化。
如:select count(1) from pub_organ o where o.organ_type = 1;
select count(1) from pub_organ o where o.organ_type = 2;
两条语句可以简化为一条:
select count(case when o.organ_type = 1 then 1 else null end) c1, count(case when o.organ_type = 2 then 1 else null end) c2 from pub_organ o;
其他优化
- 删除重复数据:
方法1 PLAN_TABLE_OUTPUT |
SQL_ID 597rg6z4t563b, child number 0 |
------------------------------------- |
delete /*+ gather_plan_statistics zjf_flag */ from pub_organ o where rowid not in (select max(rowid) from |
pub_organ o1 group by o1.organ_name) |
|
Plan hash value: 1304593988 |
|
-------------------------------------------------------------------------------------------------------------------------- |
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | O/1/M | |
-------------------------------------------------------------------------------------------------------------------------- |
| 1 | DELETE | PUB_ORGAN | 2 | | 0 |00:00:00.06 | 5039 | | | | |
|* 2 | HASH JOIN RIGHT ANTI| | 2 | 2014 | 4028 |00:00:00.09 | 426 | 985K| 985K| 2/0/0| |
| 3 | VIEW | VW_NSO_1 | 2 | 10665 | 21330 |00:00:00.10 | 352 | | | | |
| 4 | SORT GROUP BY | | 2 | 10665 | 21330 |00:00:00.04 | 352 | 604K| 489K| 2/0/0| |
| 5 | TABLE ACCESS FULL| PUB_ORGAN | 2 | 12679 | 25358 |00:00:00.04 | 352 | | | | |
| 6 | INDEX FULL SCAN | PUBORGAN_PK | 2 | 12679 | 25358 |00:00:00.03 | 74 | | | | |
-------------------------------------------------------------------------------------------------------------------------- |
|
Predicate Information (identified by operation id): |
--------------------------------------------------- |
|
|
|
方法2 PLAN_TABLE_OUTPUT | |||||||||||||||||||||||||
SQL_ID bud1rjjndxukr, child number 0 | |||||||||||||||||||||||||
------------------------------------- | |||||||||||||||||||||||||
delete /*+ gather_plan_statistics zjf_flag */ from pub_organ o where exists (select null from pub_organ o1 | |||||||||||||||||||||||||
where o.organ_name = o1.organ_name and o.rowid > o1.rowid) | |||||||||||||||||||||||||
| |||||||||||||||||||||||||
Plan hash value: 3527544481 | |||||||||||||||||||||||||
| |||||||||||||||||||||||||
------------------------------------------------------------------------------------------------------------------------ | |||||||||||||||||||||||||
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | O/1/M | | |||||||||||||||||||||||||
------------------------------------------------------------------------------------------------------------------------ | |||||||||||||||||||||||||
| 1 | DELETE | PUB_ORGAN | 2 | | 0 |00:00:00.09 | 13238 | | | | | |||||||||||||||||||||||||
|* 2 | HASH JOIN RIGHT SEMI| | 2 | 634 | 4028 |00:00:00.04 | 704 | 1191K| 1059K| 2/0/0| | |||||||||||||||||||||||||
| 3 | TABLE ACCESS FULL | PUB_ORGAN | 2 | 12679 | 25358 |00:00:00.04 | 352 | | | | | |||||||||||||||||||||||||
| 4 | TABLE ACCESS FULL | PUB_ORGAN | 2 | 12679 | 25358 |00:00:00.04 | 352 | | | | | |||||||||||||||||||||||||
------------------------------------------------------------------------------------------------------------------------ | |||||||||||||||||||||||||
| |||||||||||||||||||||||||
Predicate Information (identified by operation id): | |||||||||||||||||||||||||
--------------------------------------------------- | |||||||||||||||||||||||||
| |||||||||||||||||||||||||
2 - access("O"."ORGAN_NAME"="O1"."ORGAN_NAME") | |||||||||||||||||||||||||
filter("O1".ROWID<"O".ROWID) | |||||||||||||||||||||||||
|
第一种方式先做group by,然后半连接。第三种方法是先做group by,然后连接。第二种方法(也可以用in)只需一次半连接。第二种方法更优。
这个需求中,group by 是毫无必要的,耗时耗力不讨好。
- 对于一些不能保证过滤后的列的值较少时,或者对索引列进行运算时,将不会使用索引。
对索引列使用<>运算。
使用like,并将%和_放在第一位。
在索引列上使用函数运算。
索引列上进行字符串连接,日期加减,数值加减乘除,以及所有的显式隐式的类型转换。例如select * from PUB_FUNCTIONS s where s.function_name = ‘1‘会用到索引,去掉引号就不会了。 - 用union all替换union。如果去重不去重无关紧要,一定要加上all。不加all的去重操作耗费很大。
- CBO和RBO
举个例子,如select * from PUB_FUNCTIONS s where s.function_name = ‘1‘
如果使用RBO,那么肯定会使用索引。但是如果使用CBO,它会根据据簇因子情况分析之后,决定是否使用索引。
在10g之后,oracle已经决定不再对RBO的bug进行修补。 - 使用distinct和group by的区别:
| PLAN_TABLE_OUTPUT |
1 | SQL_ID gp7kzxzjfwx37, child number 0 |
2 | ------------------------------------- |
3 | select /*+ gather_plan_statistics zjf_flag */ o.organ_name,o.organ_code from |
4 | pub_organ o group by o.organ_name,o.organ_code |
5 |
|
6 | Plan hash value: 647668416 |
7 |
|
8 | ------------------------------------------------------------------------------------------ |
9 | | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | |
10 | ------------------------------------------------------------------------------------------ |
11 | | 1 | HASH GROUP BY | | 1 | 12679 | 100 |00:00:00.01 | 176 | |
12 | | 2 | TABLE ACCESS FULL| PUB_ORGAN | 1 | 12679 | 12679 |00:00:00.01 | 176 | |
13 | ------------------------------------------------------------------------------------------ |
14 |
|
| PLAN_TABLE_OUTPUT |
1 | SQL_ID d3q8bz6h4msgg, child number 0 |
2 | ------------------------------------- |
3 | select /*+ gather_plan_statistics zjf_flag */ distinct |
4 | o.organ_name,o.organ_code from pub_organ o |
5 |
|
6 | Plan hash value: 4103017490 |
7 |
|
8 | ------------------------------------------------------------------------------------------ |
9 | | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | |
10 | ------------------------------------------------------------------------------------------ |
11 | | 1 | HASH UNIQUE | | 1 | 12679 | 100 |00:00:00.01 | 176 | |
12 | | 2 | TABLE ACCESS FULL| PUB_ORGAN | 1 | 12679 | 12679 |00:00:00.03 | 176 | |
13 | ------------------------------------------------------------------------------------------ |
14 |
|
这两种那个快还真不知道,不过是distinct不是group by还是用distinct的好,毕竟group by要分组,应该有耗费。
- or和索引
or查询中会影响到索引的使用:
如select * from PUB_FUNCTIONS s where s.function_id = ‘2‘ or s.function_name = ‘2‘,虽然两列都有索引,但是还是选择了全部扫描。这种情况下,如果不影响数据集的大小,使用union all或者union可能会快点(使用union可能会比前面的sql获取的数据少,使用union all可能会多,而且要去重还有耗费)。
但是select * from PUB_FUNCTIONS s where s.function_name = ‘1‘ or s.function_name = ‘2‘,这种情况会用到索引(和in一样,使用INDEX RANGE SCAN)。所以有的开发人员把or改成in没有意义。 - 在索引列上使用not操作会影响索引的选择。
其他sql的问题
- 在left join的时候,如果where字句有右侧表的列的限制,那么oracle将不适用left join,而是inner join。所以右侧的限制应该放在on中。最左侧的第一个表条件应该放在where中,放在on中将会不起作用。
- 在树查询的时候,所有的限定上下级的关系的条件前都要加上prior,否则是同级的限制。
- 修改object的时候,必须先drop掉对他的引用,如table。否则不能修改。
- left join中on的条件,并不是与任何一个表关联都一样,如果关联的是前一个的left join过的表的某列,那么结果跟关联第一个表的某列是不一样的。
- 在树形查询中,是先执行树形查询,在进行where过滤的,如果要在过滤的结果上执行树形查询,需要将条件写在start with和connect by中。或者在子查询中执行树形查询。
- 如果在关联中的某张表,根据条件能够取出多条数据,但是需要取某个列最大或者最小的的那一条记录,那么不要使用min或者max来做,使用not exists最好。
如:
select *
from tt t1
where t1.name = ‘name‘
and not exists (select null
from tt t2
where t1.name = t2.name
and t1.age > t2.age)
- 从一个表的数据更新另外一个表
update zjf_test1
set name1 =
(select name1 from zjf_test2 where zjf_test1.id1 = zjf_test2.id1);
--如果id1是主键 可以这么写
update (select zjf_test1.name1, zjf_test2.name1 name_new
from zjf_test1, zjf_test2
where zjf_test1.id1 = zjf_test2.id1)
set name1 = name_new;
--如果id1不是主键 上面写法将会报错 但是下面这种写法不会报错
merge into zjf_test1
using zjf_test2
on (zjf_test1.id1 = zjf_test2.id1)
when matched then
update set zjf_test1.name1 = zjf_test2.name1;
oracle sql 高级编程 历史笔记整理