Showing posts with label PLSQL/SQL. Show all posts
Showing posts with label PLSQL/SQL. Show all posts

Thursday, April 2, 2009

Oracle explicit cursor and implicit cursor

Oracle 有隐式游标和显式游标之分.

对于那些仅仅返回一行记录的SQL语句, 本身又没有显式地使用Open语句, 其实就是隐式游标.
Implicit cursors

For SQL queries returning single row PL/SQL declares implicit cursors. Implicit cursors are simple SELECT statements and are written in the BEGIN block (executable section) of the PL/SQL. Implicit cursors are easy to code, and they retrieve exactly one row. PL/SQL implicitly declares cursors for all DML statements. The most commonly raised exceptions here are NO_DATA_FOUND or TOO_MANY_ROWS.
Syntax:

SELECT ename, sal INTO ena, esa FROM EMP WHERE EMPNO = 7844;

Note: Ename and sal are columns of the table EMP and ena and esa are the variables
used to store ename and sal fetched by the query.

显式游标, 其实显式游标也可以分为显式打开的游标, 和隐式打开的游标, 对于前者, 应该在最后关闭游标.

这是一个显式打开的显式游标
DECLARE
CURSOR trip_cursor IS
SELECT bt_id_pk, bt_hotel_id
FROM business_trips;
trip_record trip_cursor%ROWTYPE

BEGIN
OPEN trip_cursor;
LOOP
FETCH trip_cursor INTO trip_record;
EXIT WHEN trip_cursor%NOTFOUND;
INSERT INTO copy_of_business_trips (bt_id_pk, bt_hotel_id)
VALUES (trip_record.bt_id_pk, trip_record.bt_hotel_id);
END LOOP;
CLOSE job_cursor;
END;



--这个是隐式打开的显式游标, 在使用之后, 你不必手动地关闭这个游标
--Retrieve all trips from the t_business_trips table using a cursor
and print only those with a duration of one day:

SET SERVEROUTPUT ON
DECLARE
CURSOR trip_cursor IS
SELECT bt_id_pk, bt_duration
FROM business_trips;
BEGIN
FOR trip_record IN trip_cursor LOOP
-- implicit open/fetch occur
IF trip_record.bt_duration = 1 THEN
DBMS_OUTPUT_LINE ('Trip Number ' || trip_record.bt_id_pk
|| ' is a one day trip');
END IF;
END LOOP; -- IMPLICIT CLOSE OCCURS
END;

参考1
http://www.dba-oracle.com/plsql/t_plsql_cursors.htm
http://www.exforsys.com/tutorials/oracle-9i/oracle-cursors.html

Monday, March 9, 2009

invoke the cursor valued stored function/procedure

这节主要讨论, 如何在PL/SQL中怎样调用另一个存储函数或存储过程呢?
如果一个函数返回的是scalar值, 那么调用这个自定义的函数和调用内建的函数是完全一样的.

如果你的自定义的函数返回的是一个游标, 调用这个函数本身并不复杂, 但是怎样循环这个游标却比较诡异. 如下面的例子, FUN_GET_JOB_INFO返回的游标不是直接来源于个表, 而是动态生成的一个recordset, 你不能将这个游标函数看作一个普通表一样,放在from从句中, 只能像普通游标一样使用它, 比如用Loop语句来遍历这个游标. 因为调用这个游标函数, 获得的其实就是一个游标.

Fetch这个cursor的row时候, 不能将它直接将整个row放到你自定义的row 类型变量中, 这会引起convert错误. 你应当将这个row分解成若干个字段, 将row放到这些字段中.
比如
FETCH CURSOR1 INTO V_JOB_CODE, V_JOB_NAME;




--create one object type outside of one package
create or replace type TYPE_JOB_ROW as object
(
JOB_CODE VARCHAR(50),
JOB_NAME VARCHAR(200)
);

--create one table type outside of one package
CREATE OR REPLACE TYPE TYPE_JOB_TABLE is TABLE OF TYPE_JOB_ROW;


包的代码是:


create or replace package PKG_TEST4 is

type type_result_table is ref cursor ;

function FUN_GET_JOB_INFO RETURN type_result_table ;
function FUN_GET_ONE_JOB(v_arg varchar2, v_Arg_Is_JOB_Code in char) return varchar2;

end PKG_TEST4;
/
create or replace package body PKG_TEST4 is

/*
Purpose: To fetch ALL JOB code or name
*/
function FUN_GET_JOB_INFO RETURN type_result_table
IS
result_table type_result_table ;
JOB_TABLE TYPE_JOB_TABLE:=TYPE_JOB_TABLE() ;
JOB_ROW TYPE_JOB_ROW ;
BEGIN
JOB_ROW:=TYPE_JOB_ROW(JOB_CODE => 'A',JOB_NAME => 'A123') ;
JOB_ROW.JOB_NAME:='A12' ; --Change the job_name from A123->A12
JOB_TABLE.extend();
JOB_TABLE(JOB_TABLE.count) := JOB_ROW ;

OPEN result_table FOR
SELECT * FROM table(JOB_TABLE) ;

return(result_table);
END ;


/*
Purpose: To fetch on specific JOB code or name
*/
function FUN_GET_ONE_JOB(v_arg varchar2, v_Arg_Is_JOB_Code in char)
return varchar2 is
result varchar2(50);
cursor1 type_result_table;
V_JOB_CODE VARCHAR2(20);
V_JOB_NAME VARCHAR2(20);
V_JOB_ROW TYPE_JOB_ROW;
begin
result := '';
--call the cursor valued function
cursor1 := FUN_GET_JOB_INFO;
LOOP
--Fetch the record into 2 variables
FETCH CURSOR1 INTO V_JOB_CODE, V_JOB_NAME;

--the following code is wrong, because it will fail to convert the row type.
--FETCH CURSOR1 INTO V_JOB_ROW;

if (v_Arg_Is_JOB_Code = 'Y') or (v_Arg_Is_JOB_Code = 'y') then
if (V_JOB_CODE = v_arg) then
result := V_JOB_name;
end if;
else
if (V_JOB_name = v_arg) then
result := V_JOB_code;
end if;
end if;
EXIT WHEN CURSOR1%NOTFOUND;

END LOOP;
return(Result);
end;

end PKG_TEST4;
/

Wednesday, February 25, 2009

Using PL/SQL Table variable

在SQL Server的存储过程中, 可以非常方便地使用临时表#TempTable, 你可以随意地将数据插入到临时表, 可以将临时表的记录作为存储过程的结果集. #TempTable用过后, 它就干干净净地消失了, 包括表的structure.
而Oracle的临时表是global temporary table, 这表明它的定义是persistent, 只是内容是临时的.
那么在Oracle中如何才能做到像SQL Server一样的临时表呢? Oracle为我们提供了PL/SQL Table表变量, 其实就是内存表.


create or replace package PKG_TEST2 is

TYPE TYPE_NESTED_JOB_ROW IS RECORD
(JOB_CODE VARCHAR(50),
JOB_NAME VARCHAR(200));

TYPE TYPE_NESTED_JOB_TABLE is TABLE OF TYPE_NESTED_JOB_ROW ;

type type_result_table is ref cursor ;

procedure SP_GET_JOB_INFO(result_table out type_result_table) ;

end PKG_TEST2;
/
create or replace package body PKG_TEST2 is

procedure SP_GET_JOB_INFO(result_table out type_result_table)
IS
JOB_TABLE TYPE_NESTED_JOB_TABLE:=TYPE_NESTED_JOB_TABLE() ;
JOB_ROW TYPE_NESTED_JOB_ROW ;
BEGIN
JOB_ROW.JOB_CODE:='A' ;
JOB_ROW.JOB_NAME:='A12123' ;
JOB_TABLE.extend();
JOB_TABLE(JOB_TABLE.count) := JOB_ROW ;

JOB_ROW.JOB_CODE:='B' ;
JOB_ROW.JOB_NAME:='B12123' ;
JOB_TABLE.extend();
JOB_TABLE(JOB_TABLE.count) := JOB_ROW ;

DBMS_OUTPUT.put_line(JOB_TABLE.COUNT) ;
--OUTPUT RESULT IS 2

--The following code can not compile succeeded
--OPEN result_table for
-- select * from table(JOB_ROWS) ;
END ;

procedure SP_GET_JOB_INFO_2(IN_Job_ID in number, result_table out type_result_table)
IS
JOB_TABLE TYPE_NESTED_JOB_TABLE:=TYPE_NESTED_JOB_TABLE() ;
JOB_ROW TYPE_NESTED_JOB_ROW ;

cursor CURSOR_JOBS is
SELECT * FROM EMPLOYEE_JOBS
WHERE JOB_ID>=IN_Job_ID;
BEGIN
for r_job in CURSOR_JOBS
loop
JOB_ROW.JOB_CODE:=r_job.JOB_CODE;
JOB_ROW.JOB_NAME:=r_job.JOB_NAME;
JOB_TABLE.extend();
JOB_TABLE(JOB_TABLE.count) := JOB_ROW ;
end loop;


DBMS_OUTPUT.put_line(JOB_TABLE.COUNT) ;
--OUTPUT RESULT IS 2

--The following code can not compile succeeded
--OPEN result_table for
-- select * from table(JOB_ROWS) ;
END ;

end PKG_TEST2;
/

在上面的例子中, 我们在package中声明了一个record类型(TYPE_NESTED_JOB_ROW)和一个table类型(TYPE_NESTED_JOB_TABLE), 在存储过程SP_GET_JOB_INFO中, 可以实例化这个table类型变量JOB_TABLE, 你可以向JOB_TABLE插入单个的记录. 如果使用Loop语句的话, 你可以批量地插入许多记录到这个表变量中, 就像存储过程SP_GET_JOB_INFO2.

这种在package中定义的table类型变量, 对于一般的DML操作是非常实用的, 但如果你要将table表变量的结果作为ref cursor类型的存储过程out参数暴露出来, 就不行了. 在上面的示例代码中, 如果将取消最后两句代码的注释, 你就发现这个存储过程不能被编译. 为什么呢? 详见http://forums.oracle.com/forums/thread.jspa?threadID=623197

简单讲, 原因就是, 我们是在package中定义的TYPE_NESTED_JOB_TABLE的table类型, 这个类型对于该package之外其实是不可见的, 而存储过程却要将这个nested的表类型暴露到package之外, 所以Oracle会报错. 知道这个原因之后, 解决也很简单, Nested Record和Nested Table的定义放在包之外, 就行了. 需要说明的是, 需要修改一下Nested Record的定义格式(从Record类型改到Object类型).



--create one object type outside of one package
create or replace type TYPE_JOB_ROW as object
(
JOB_CODE VARCHAR(50),
JOB_NAME VARCHAR(200)
);

--create one table type outside of one package
CREATE OR REPLACE TYPE TYPE_JOB_TABLE is TABLE OF TYPE_JOB_ROW;


create or replace package PKG_TEST3 is

type type_result_table is ref cursor ;

procedure SP_GET_JOB_INFO(result_table out type_result_table) ;
end PKG_TEST3;
/
create or replace package body PKG_TEST3 is

procedure SP_GET_JOB_INFO(result_table out type_result_table)
IS
JOB_TABLE TYPE_JOB_TABLE:=TYPE_JOB_TABLE() ;
JOB_ROW TYPE_JOB_ROW ;
BEGIN
JOB_ROW:=TYPE_JOB_ROW(JOB_CODE => 'A',JOB_NAME => 'A123') ;
JOB_ROW.JOB_NAME:='A12' ; --Change the job_name from A123->A12
JOB_TABLE.extend();
JOB_TABLE(JOB_TABLE.count) := JOB_ROW ;

OPEN result_table FOR
SELECT * FROM table(JOB_TABLE) ;
END ;

end PKG_TEST3;
/

Oracle temp table

在SQL Server的存储过程中, 可以非常方便地使用临时表#TempTable, 你可以随意地将数据插入到临时表, 也可以将临时表的记录作为存储过程的结果集. #TempTable用过后, 它就干干净净地消失了, 包括表的structure.

而Oracle的临时表是global temporary table, 这表明它的定义是persistent, 只是内容是临时的. 除此之外, 临时表还有几个特点:

1. 临时表是不能加logging等选项, 所以它的redo log和I/O和普通表相比都非常小,

2. 不能为临时表指定tablespace, 临时表一定是在TEMPORARY tablespace中.

定义一般Heap表的格式通常是: CREATE TABLE Table_1(col_1 number) TABLESPACE USERS;

而定义临时表的格式通常是:

CREATE GLOBAL TEMPORARY TABLE Temp_Table_1 (a NUMBER) ON COMMIT

PRESERVE ROWS; --临时表的内容在整个Session中一直保持, 即使是一个transaction结束.

或者是:
CREATE GLOBAL TEMPORARY TABLE Temp_Table_1 (a NUMBER) ON COMMIT

DELETE ROWS; --临时表的内容仅仅在一个transaction中保持.

Wednesday, May 14, 2008

use PLSQL in SQLPLUS

正常情况下, SqlPlus是一个行命令工具, 你只能在其中运行标准的SQL语句或者是SqlPlus特有的命令, 比如print或desc. 但有两种方法, 可以使得SqlPlus进入PL-SQL模式(可以执行诸如dbms_out.output_line()等PL-SQL函数).

方法1:在SqlPlus的提示符下输入begin并回车, 接下来几行输入你的代码, 然后输入end并回车, 最后一行输入/
方法2:在SqlPlus的提示符下输入declare 然后回车, 然后声明变量(可选项), 接下来输入Begin并回车, 接下来几行输入你的代码, 然后输入end并回车, 最后一行输入/

格式如下:
[declare]

[variable1 type;]



--some statements;



/


示例如下:

SQL> DECLARE

v_dname VARCHAR2(14);

BEGIN

v_dname := 'HR';

dbms_output.put_line(v_dname);

END;

/

Wednesday, April 30, 2008

call stored procedure that return one result set using sqlplus Toad SqlTools

http://www.devshed.com/c/a/Oracle/Working-with-REF-CURSOR-in-PL-SQL/
PLSQL Developer调用和测试一个存储过程非常简单, 找到一个存储过程后, 在快捷菜单中选择Test菜单项目, 输入存储过程的参数, 就可以了.
1. 在SQLPlus中调用这个过程的方法
用SQLPlus来调用/测试一个自定义的存储过程, 相对比较麻烦, 不像用PLSQL Developer方便. 尤其是如果该存储过程返回一个记录集, 我用SQLPLUS一直没有调用成功过. 最主要的原因是: 原来在SQLPlus中不知道怎样使用bind variable. 下面给出一个例子.
假如在PKG_1包中有 PROC_1存储过程, 它接受一个Input参数, 然后返回一个记录集.



PROCEDURE PROC_1(IN_SRVY_ID IN VARCHAR , OUT_TABLE OUT SYS_REFCURSOR )
IS
BEGIN
OPEN OUT_TABLE FOR
SELECT * FROM SRVY WHERE SRVY.SRVY_ID=IN_SRVY_ID;
END PROC_1;

在SQLPlus中调用这个过程的方法, 然后打印出结果集

SQL> VARIABLE CURSOR_1 REFCURSOR

SQL > VARIABLE IN_SRVY_D VARCHAR2 ( 5 )

SQL > EXEC :IN_SRVY_D := 'SU07'

SQL > EXEC PKG_1.PROC_1(:IN_SRVY_D, :CURSOR_1)

SQL >PRINT :CURSOR_1

http://www.indiastudychannel.com/resources/8519-SQL-SQL-Plus-Interview-Questions-with-Answers-Set.aspx

http://lbdwww.epfl.ch/f/teaching/courses/oracle8i/server.815/a66736/ch35.htm

2. 在Toad或SQLTools就不像SQLPLus那么容易了, 主要是在于打印记录集比较复杂



--假设emp表中有三个字段分别是 eid, ename, sal. -- select eid, ename, sal from emp;
create table emp
(
ename VARCHAR2 ( 5 ) not null ,
eid NUMBER ( 6 ) not null ,
sal NUMBER ( 6 )
);

create or replace package PKG_2
IS
TYPE c_return_table IS REF CURSOR ;
PROCEDURE SP_GET_EMP_INFO(IN_EMP_ID IN VARCHAR , OUT_TABLE OUT c_return_table);
END PKG_2;


create or replace package BODY PKG_2
IS
PROCEDURE SP_GET_EMP_INFO(IN_EMP_ID IN VARCHAR , OUT_TABLE OUT c_return_table)
BEGIN
OPEN OUT_TABLE FOR
SELECT eid, ename, sal FROM EMP WHERE eid= IN_EMP_ID;
END SP_GET_EMP_INFO ;
END PKG_2;


--***************************示例说明*************************************************
--在PLSQL Developer中,调用和显示一个返回记录集的存储过程非常简单(因为它提供了Test Procedure功能),
--但对于其他工具, 却没有这样的功能, 你必须自己写代码来调用, 然后显示
--***********************************************************************************

DECLARE
IN_EMP_ID emp.eid% TYPE;
OUT_C_RETURN_TABLE PKG_2.C_RETURN_TABLE;

--如果从一个Cursor中整条整条地取数据, 则下面的类型必须和Cursor中每个字段的类型都匹配, 这包括类型必须匹配和字段个数必须一样.
TYPE REC_EMP IS RECORD(
ename VARCHAR2(5),
eid NUMBER(6),
sal NUMBER(6)
);

EMP_ROW REC_EMP;

--如果Cursor的字段和emp的字段刚好一样, 你不必先定义一个Record类型, 直接声明EMP_ROW
--EMP_ROW emp%ROWTYPE ;
BEGIN
IN_EMP_ID := 10;

--STEP1: OPEN CURSOR BY EXECUTING STORED PROCEDURE
PKG_2.SP_GET_EMP_INFO(IN_EMP_ID, OUT_C_RETURN_TABLE);

--STEP1: OR OPEN CURSOR BY SELECT STATEMENT
/*
OPEN C_EMP FOR SELECT * FROM EMP where eid=IN_EMP_ID;
*/

LOOP
--STEP2: FETCH EVERY ROW FROM CURSOR
FETCH OUT_C_RETURN_TABLE
INTO EMP_ROW;
EXIT WHEN OUT_C_RETURN_TABLE% NOTFOUND;
--PRINT EVERY ROW
DBMS_OUTPUT.PUT_LINE(EMP_ROW.eid || ' - ' || EMP_ROW.ename);
END LOOP;

--STEP3: CLOSE CURSOR
CLOSE OUT_C_RETURN_TABLE;

DBMS_OUTPUT.PUT_LINE('EXECUTION OVER.');
END;

Use bind variable in SqlPlus and PL-SQL

Look through the whole article: http://www.rittmanmead.com/2004/03/24/bind-variables-explained/
http://www.akadia.com/services/ora_bind_variables.html


Bind Variables in SQL*Plus.

In SQL*Plus you can use bind variables as follows:

因为变量是专门为PLSQL语言设计的, 如果要在SqlPlus中使用, 那么绑定变量的写法有些特别. 除了声明绑定变量之外, 在其他地方使用绑定变量, 必须在绑定变量之前加上:, 告诉SqlPlus, 这是一个绑定变量. 为绑定变量赋值, 需要使用Exec 语句, 比如exec :deptno:=10 . 调用函数或存储过程也需要使用exec.

SQL>variable deptno number --You can replace variable with var

SQL> exec :deptno := 10 --注意这是为绑定变量赋值的正确写法, 必须加exec

SQL>select * from emp where deptno = :deptno;


What we've done to the SELECT statement now is take the literal value out of it, and replace it with a placeholder (our bind variable), with SQL*Plus passing the value of the bind variable to Oracle when the statement is processed. This bit is fairly straighforward (you declare a bind variable in SQL*Plus, then reference the bind variable in the SELECT statement)

Bind Variables in PL/SQL

Taking PL/SQL first of all, the good news is that PL/SQL itself takes care of most of the
issues to do with bind variables
, to the point where most code that you write already uses bind variables without you
knowing. Take, for example, the following bit of PL/SQL:

下面是一个存储过程, 在存储过程中,你使用的各种变量(包括formal argument和local variable)都是绑定变量.


create or replace procedure dsal(p_empno in number)
as
begin
update emp set sal=sal*2
where empno = p_empno;
commit;
end dsal;

Now you might be thinking that you've got to replace the p_empno with a bind variable.
However, the good news is that
every reference to a PL/SQL variable is in fact a bind
variable.

Dynamic SQL
在PLSQL中, 你需要注意怎样使用绑定变量的场合, 只有在PLSQL语句块中包含了Dynamic SQL.
In fact, the only time you need to consciously decide to use bind variables when working with PL/SQL is when using Dynamic SQL.
Dynamic SQL, allows you to execute a string containing SQL using the EXECUTE IMMEDIATE command. For next example would always require a hard parse when it is submitted:
create or replace procedure dsal(p_empno in number)
as
begin
execute immediate
'update emp set sal = sal*2 where empno = '||p_empno;
commit;
end;
The way to use bind variables instead is to change the EXECUTE IMMEDIATE command as follows:
create or replace procedure dsal(p_empno in number)
as
begin
execute immediate
'update emp set
sal = sal*2 where empno = :x'
using p_empno;
commit;
end;
And that's all there is to it. One thing to bear in mind, though, is that you can't substitute actual object names (tables, views, columns etc) with bind variables - you can only subsitute literals. If the object name is generated at runtime, you'll still need to string concatenate these parts, and the SQL will only match with those already in the shared pool when the same object name comes up. However, whenever you're using dynamic SQL to build up the predicate part of a statement, use bind variables instead and you'll reduce dramatically the amount of latch contention going on.

Monday, April 28, 2008

return resultset from stored procedure

如果要在procedure中返回一个记录集, 有两种方法, 一种是使用ref cursor, 另一种方法是使用pipelined function, 这里只讲述ref cursor.

Oracle8i, 如果你的存储过程需要返回一个记录集, 那么必须要将这个存储过程放在一个package中, 因为你必须首先定义一个ref cursor类型, 而这个类型只能放在package中定义, 对你而言, 没有其他地方可以定义类型. 记录集类型可以是强类型的, 也可以定义成非强类型的.





create or replace package sprs_temp_obj.PKG_SPRS_SMPLU_H_IND
IS
TYPE c_return_table IS REF CURSOR; --这是一个非强类型记录集类型

TYPE emp_cur_type IS REF CURSOR RETURN emp%ROWTYPE; --这里定义了一个强类型的记录类型

END PKG_SPRS_SMPLU_H_IND;




定义之后, 就可以在PKG_SPRS_SMPLU_H_IND package的body中使用这两个记录集类型了.

在Oracle9i, 系统已经有一个SYS_REFCURSOR类型, 所以你的存储过程不必一定要放在某个package里了.




procedure proce1( emp_refcur out SYS_REFCURSOR) -- 这定义了一个非强类型的记录集
is
begin
Open emp_refcur For
select * from emp;
end proce1;



那么非强类型的Ref cursor 和sys_refcursor的区别是什么呢?
A REF CURSOR that does not specify the return type such as SYS_REFCURSOR. Meaning the SYS_REFCURSOR can be opened for a dynamic SQL query, where as simple REF CURSOR can not be opened for a query dynamically built at execution time.

Thursday, April 24, 2008

How to choose the right primary key

之前, 我并不知道Oracle是否能创建Compound primary key, 因为我选择Primary Key的原则是尽量用sequence, 或者其他一些选择性很强的字段. 所以我没有在意其他情况.


我在review script时候, 看到下面这个表, 它的结构和key是这样创建的.


create table RMR_USBLTY_VALUES(


SRVY_ID VARCHAR2(5) not null,


SRVY_PHASE_NMBR VARCHAR2(2) not null,


OPERAND_NMBR NUMBER(2) not null,


SMPLU_ID NUMBER(5) not null,


ARBSU_ID VARCHAR2(6) not null,


CNTY_TYPE_CODE VARCHAR2(5) not null,


CMPD_DEMO_ID VARCHAR2(5) not null,


TOTAL_SMPL_CNT NUMBER(6)


);


-- Create/Recreate primary, unique and foreign key constraints


alter table RMR_USBLTY_VALUES


add constraint RMR_USBLTY_VALUES_PK primary key (SRVY_ID, SRVY_PHASE_NMBR, OPERAND_NMBR, ARBSU_ID, CMPD_DEMO_ID, SMPLU_ID, CNTY_TYPE_CODE)


粗体字field都被作为复合主键了, 第一眼, 我就认为这不是一个好的设计, 我猜想, 原设计者认为只有将几个粗体字field组合在一起, 才能在该表中唯一区分每个记录, 所以将这些字段组合起来作为复合主键, 这是十足的为了有个主键而建主键的, 是完全错误的. 理由是: 每次更新与索引相关的字段, oracle将不得不重新计算索引. 上例中, 主键中有这么多字段, 更新字段的几率是比较大的, 也就是说, 随便更新其中一个字段, 就要计算一次索引.


那么什么样的字段可以作为主键呢 ?


先看看, 做主键的条件是什么? (article: Candidate keys: Choosing the right primary key)


1. The candidate key must be unique within its domain (the entity it represents, and beyond, if you also intend to access external entities).


2. The candidate key can not hold NULL values (NULL is not zero. Zero is a number. NULL is 'nonexistent value').


3. The candidate key can never change. It must hold the same value for a given occurrence of an entity for the lifetime of that entity.


知道了作为主键应该具备哪些条件之后, 那么到底哪些字段适合作为主键呢? (article: Database Design Guidelines and Rules of Thumb )


通常我们可选择的主键有: You have three choices for defining the primary key for most tables: a 'natural' key, a system generated or 'internal' key, and (for smaller lookup tables) a mnemonic code.


最适合的应该是 a system generated field


Rule of thumb: for any table over 100 records, use a system-generated number as the primary key.


exception in plsql

PL sql的exception必须是放在Begin和end块中, 而且每个begin-end 块中只能有一个exception. 这并不表示一个存储过程只能有一个exception.



通常的写法是:


BEGIN
SELECT price / earnings INTO pe_ratio FROM stocks
WHERE symbol = 'XYZ'; -- might cause division-by-zero error
INSERT INTO stats (symbol, ratio) VALUES ('XYZ', pe_ratio);
COMMIT;
EXCEPTION -- exception handlers begin
WHEN ZERO_DIVIDE THEN -- handles 'division by zero' error
INSERT INTO stats (symbol, ratio) VALUES ('XYZ', NULL);
COMMIT;
...
WHEN OTHERS THEN -- handles all other errors
ROLLBACK;
END; -- exception handlers and block end here





一个存储过程中包含多个exception handler的写法是:




BEGIN
DELETE FROM stats WHERE symbol = 'XYZ';
BEGIN ---------- sub-block begins
SELECT price / NVL(earnings, 0) INTO pe_ratio FROM stocks
WHERE symbol = 'XYZ';
EXCEPTION
WHEN ZERO_DIVIDE THEN
pe_ratio := 0;
END; ---------- sub-block ends


INSERT INTO stats (symbol, ratio) VALUES ('XYZ', pe_ratio);
EXCEPTION
WHEN OTHERS THEN
...
END;





我们也可以主动的抛出一个异常, 语法和Pascal出奇的一致. 下面代码是先定义了一个past_due异常类型, 然后在代码中主动抛出一个past_due异常.



Exceptions can be declared only in the declarative part of a PL/SQL block, subprogram, or package. You declare an exception by introducing its name, followed by the keyword EXCEPTION. In the following example, you declare an exception named past_due:


DECLARE


past_due EXCEPTION;


begin


...


if ... then


RAISE past_due; -- this is not handled


end if;


,,


end;

Sunday, December 30, 2007

Blog Label Placeholder


Keywords: Label, Placeholder



I add some labels in this blog. The labels will act as categories. The label list will update according to posts' categories.



NETCONFIG, RMAN, PLSQL/SQL, SQLPLUS, TOOLS, DATA_PUMP, DICTIONARY, OFA, INSTALLATION, PERMISSION, AUTHENICATION, MISC, Oracle_Link



Powered by Qumana


CNOUG.net