首页 > 代码库 > 01 Developing Successful Oracle Application
01 Developing Successful Oracle Application
本章提要
-------------------------------
本章是概述性章节
1. 介绍了了解数据库内部结构对于开发的重要性
2. 介绍了如何才能开发好的数据库应用程序
-------------------------------
1.
基本上 95% 的问题都可以通过 SQL解决, %5 PL/SQL 和 C 解决
自治事物的作用( 不推荐使用自治事物 )
1) error-logging: 记录错误, 无论你的transaction是否提交, 都需要知道你曾经做过的内容
2) demonstration concept: 用来演示, 比如你想演示两个独立的session 等
作为一个developer必须要基本上清楚database内部结构, 不能将database看成是一个"黑盒"
举例: bitmap索引, 你需要了解bitmap索引是如何工作的, 否则就会出问题
-- 01 test_autonomous_transaction&bitmap.sql
/* * Test autonomous_transaction, simulation two session * bitmap will lock the column, when uncommit transaction exist */create table t( test_flag varchar2(1));create bitmap index t_idx on t(test_flag);-- uncommitinsert into t values(‘N‘);-- antonomous_transactiondeclarepragma autonomous_transaction;begin insert into t values(‘Y‘);end;/
例子说明: 因为bitmap索引的特点是, 会对column增加锁, 所以正确的做法是对该列增加B*tree索引
举例: 函数索引
-- 02 function_index.sql
/* * This program will test index on function, * so when you want to use index on where condition, * You need to use function. */create table t( id number primary key, test_flag varchar2(1), payload varchar2(20));-- if test_flag == ‘N‘, return ‘N‘create index t_idx on t(decode(test_flag, ‘N‘, ‘N‘)); insert into tselect r, case when mod(r, 2) = 0 then ‘N‘ else ‘Y‘ end, ‘payload ‘ || r from (select level r from dual connect by level <= 5);/select * from t;create or replace function get_first_unlocked_rowreturn t%rowtypeas resource_busy exception; pragma exception_init(resource_busy, -54); l_rec t%rowtype;begin for x in (select rowid rid from t where decode(test_flag, ‘N‘, ‘N‘) = ‘N‘) -- index function loop begin select * into l_rec from t where rowid = x.rid and test_flag = ‘N‘ for update nowait; return l_rec; exception when resource_busy then null; when no_data_found then null; end; end loop; return null;end;/-- test functiondeclarel_rec t%rowtype;begin l_rec := get_first_unlocked_row; dbms_output.put_line(‘I got row ‘ || l_rec.id || ‘, ‘ || l_rec.payload); commit;end;/-- result is 2.declarel_rec t%rowtype;cursor cisselect * from t where decode(test_flag, ‘N‘, ‘N‘) = ‘N‘ -- use decode function is for index for update skip locked;begin open c; fetch c into l_rec; if (c%found) then dbms_output.put_line(‘I got row ‘ || l_rec.id || ‘, ‘ || l_rec.payload); end if; close c;end;/-- result is 2declarel_rec t%rowtype;pragma autonomous_transaction;cursor cisselect * from t where decode(test_flag, ‘N‘, ‘N‘) = ‘N‘ -- use decode function is for index for update skip locked; -- if you don‘t use this statement, the process will hang and wait begin open c; fetch c into l_rec; if (c%found) then dbms_output.put_line(‘I got row ‘ || l_rec.id || ‘, ‘ || l_rec.payload); end if; close c; commit;end;/-- result is 4
2.
怎样才能更好的创建application ?
1) understanding oracle architecture
2) Use a single connection in Oracle( 即多个查询使用一个connection, 这样当然节省资源)
3) Use Bind variables
这里涉及到软解析和硬解析的概念, 另外, 如果不使用绑定变量, 有可能引起 SQL INJECTION(SQL注入),
即用户输入了不合法数据, 使应用存在安全隐患.
-- 03 & 04
/* * This program will show use bind variable is very effect */create table t(x int);create or replace procedure proc1asbegin for i in 1..10000 loop execute immediate ‘insert into t values(:x)‘ using i; end loop;end;/create or replace procedure proc2asbegin for i in 1..10000 loop execute immediate ‘insert into t values(‘|| i ||‘)‘; end loop;end;/-- use runstats_pkg test to procedureexec runstats_pkg.rs_start;exec proc1;exec runstats_pkg.rs_middle;exec proc2;exec runstats_pkg.rs_stop(10000);
/* * This program will test SQL injection when you don‘t use bind variable. */create or replace procedure inj(p_date in date)as l_rec all_users%rowtype; c sys_refcursor; l_query long;begin l_query := ‘ select * from all_users where created = ‘‘‘ || p_date || ‘‘‘‘; dbms_output.put_line(l_query); open c for l_query; for i in 1..5 loop fetch c into l_rec; exit when c%notfound; dbms_output.put_line(l_rec.username || ‘.....‘); end loop; close c;end;/-- you want to showexec inj(sysdate);-- show the dangerous part about this inj procedurecreate table user_pw( uname varchar2(30) primary key, pw varchar2(30));insert into user_pw(uname, pw)values(‘TKYTE‘, ‘TO SECRET‘);COMMIT;-- now, some user don‘t know user_pw table exist, simulation this table-- +is very important.grant execute on inj to scott;-- so now, scott connect the database, and do as belowalter session set nls_date_format = ‘"‘‘union select tname, 0, null from tab--"‘;exec leon.inj(sysdate);/* * The result is: * ----------------------------------------- select * from all_users where created = ‘‘union select tname, 0, null from tab--‘ * ----------------------------------------- * we know table information, some important table. */-- in this way, scott can see the table user_pw(very important table)-- now they want to try to select this important table.select * from leon.user_pw; -- but they can not, because they don‘t have privilege.alter session set nls_date_format = ‘"‘‘union select tname || cname, 0, null from col--"‘;exec leon.inj(sysdate);/* * The result is: * ----------------------------------------- select * from all_users where created = ‘‘union select tname || cname, 0, null from col--‘ * ----------------------------------------- * we know the column information in some important table. */-- use bind variable to pertect you.create or replace procedure NOT_inj(p_date in date)as l_rec all_users%rowtype; s sys_refcursor; l_query long;begin l_query := ‘ select * from all_users where created = :x‘; dbms_output.put_line(l_query); open c for l_query using P_DATE; for i in 1..5 loop fetch c into l_rec; exit when c%notfound; dbms_output.put_line(l_rec.username || ‘....‘); end loop; close c;end;/-- test not_injexec not_inj(sysdate);/* * The result is: * ----------------------------------------- select * from all_users where created = :x * ----------------------------------------- */-- so from now on, you must use bind variable. ^^
4) understanding concurrency control
并发是很难控制的, 而且很容易导致我们的应用程序出现问题, 锁可以用来处理并发, 但是如果锁利用不当,
就会给程序的可扩展和多用户操作造成阻碍, 所以, in your database is vital if you are to develop a scalable,
correct application.
举例: 控制并发
-- 05 control_concurrency.sql
create table resources( resource_name varchar2(25) primary key, other_data varchar2(25));create table schedules( resource_name varchar2(25) references resources, start_time date, end_time date);-- the purpose about schedules is when someone want to modify table resources-- he needs to check the schedules status, but if someone-- modify the table schedules, and you select schedules, you can not find -- the result modified.select count(*) from schedules where resource_name = :resource_name and (start_time < :new_start_time) and (end_time > :new_end_time);-- so the correct selectment is as below:select * from resources where resource_name := resource_name for update;-- for update is very important, it is lock on the row. so if someone want to-- modify the table, you will know it.
5) implementing locking ( 使用锁, 上边已经有例子了)
6) flashback
SCN: This SCN is Oracle’s internal clock: every time a commit occurs, this clock ticks upward (increments).
flashback举例:
-- 06 flashback_example.sql
variable scn number;exec :scn := dbms_flashback.get_system_change_number;print scn;select count(*) from emp; -- 14 rowsdelete from emp;select count(*) from emp; -- 0 rows-- use flashback, as of scn, as of timestampselect count(*) :scn then_scn, dbms_flashback.get_system_change_number now_scn from emp as of scn :scn; -- get the time point at :scn-- the result is/*COUNT(*) THEN_SCN NOW_SCN ---------- ---------- ---------- 14 6294536 6294537*/-- you can see the result is 14.commit; -- commit the transactionselect cnt_now, cnt_then, :scn then_scn, dbms_flashback.get_system_change_number now scn from (select count(*) cnt_now from emp), (select count(*) cnt_then, from emp as of scn :scn)//*CNT_NOW CNT_THEN THEN_SCN NOW_SCN ---------- ---------- ---------- ---------- 14 14 6294536 6294552 */flashback table emp to scn :scn; -- the data return
7) Read Consistency and Non-Blocking Reads
读一致性, 并且没有读锁.
8) Database Independence
当需要数据库迁移时, 你要知道, 即便是相同的数据库, 比如都是 oracle, 那么它们的内部运作机制也可能不一样, 这也有可能
给你带来一些问题, 更别提那些不同数据库之间的迁移问题, 比如: 将标准SQL转换成plsql:
07 convert_SQL_to_PLSQL.sql
declare l_some_varibale varchar2(25);begin if (some_condition) then l_some_variable := f(...); end if; for x in (select * from t where x = l_some_variable) loop ... end loop;-- as this statement-- in oracle, this query return no data when l_some_variable was not set to a specific value-- in sybase or sql server, the query would find rows where x was set to a null value.-- in oracle null can not use = to set condition, like as below:select * from dual where null = null; -- return 0 rows selectedselect * from dual where null <> null; -- return 0 rows selectedselect * from dual where null is null; -- return X.-- to solve this problem select * from t where nvl(x, -1) = nvl(l_some_variable, -1);-- and you need to create a function indexcreate index t_idx on t(nvl(x, -1));
9) The impact of standards
SQL99 is an ANSI/ISO standard for databases, 各个数据库在实现这个标准时有不同, 另外这个标准有些时候也是有问题的, 所以
你只要专注你目前使用的数据库的标准.
10) Layered Programming (分层Programming)
Let’s say you are programming using JDBC, 不同的数据库之间, 要使用不同的方法实现, 并且利用存储过程实现.
11) Knowing What‘s Out There
不是完全了解SQL的特性
example: inline views
-- inline viewsselect p.id, c1_sum1, c2_sum2 from p, (select id, sum(q1) c1_sum1 from c1 group by id) c1, (select id, sum(q2) c2_sum2 from c2 group by id) c2 where p.id = c1.id and p.id = c2.id-- sub query that run another query per rowselect p.id, (select sum(q1) from c1 where c1.id = p.id) c1_sum1, (select sum(q2) from c2 where c2.id = p.id) c2_sum2 from p where p.name = ‘1234‘-- sub factory with clausewith c1_vw as(select id, sum(q1) c1_sum1 from c1 group by id),c2_vw as(select id, sum(q2) c2_sum2 from c2 group by id),c1_c2 as(select c1.id, c1.c1_sum1, c2.c2_sum2 from c1_vw c1, c2_vw c2 where c1.id = c2.id)select p.id, c1_sum1, c2_sum2 from p, c1_c2 where p.id = c1_c2.id
12) sloving problems simply
选择简单的办法来解决事情, 比如想控制某个用户只能一个session连接上来, 简单的办法是:
09 do_job_easy_way.sql
create profile one_session limit sessions_per_user 1;alter user scott profile one_session;alter system set resource_limit = true;
13) How Do i make it run faster ?
80%以上的性能问题都出现在设计实现级, 而不是数据库级. 在对数据库上运行的应用进行优化之前, 不要对数据库进行优化.