2009年1月13日星期二

ORACLE11g的几个新特性(针对开发者)

一 pivot操作符
这个东西应该是从sql server里学过来的,不过话说回来,这个功能的确很好用。说白了,就是做行转列。举个例子说明吧。要运行以下的例子,必须要有scott账户下的emp和dept两个表,如果没有的话,可以运行以下语句:
create table emp as select * from scott.emp;
create table dept as select * from scott.dept;

通常来说我们做行转列都是使用以下做法(在做报表的时候):
select deptno,
sum( decode( job, 'CLERK', sal ) ) clerk,
sum( decode( job, 'SALESMAN', sal ) ) salesman,
sum( decode( job, 'MANAGER', sal ) ) manager,
sum( decode( job, 'ANALYST', sal ) ) analyst,
sum( decode( job, 'PRESIDENT', sal ) ) president
from scott.emp
group by deptno
order by deptno;

这个查询得到的结果如下:
DEPTNO CLERK SALESMAN MANAGER ANALYST PRESIDENT
---------- ---------- ---------- ---------- ---------- ----------
10 1300 2450 5000
20 1900 2975 6000
30 950 5600 2850

而在11g中,我们可以这么写查询:
select *
from (select deptno, job, sal
from scott.emp ) e
pivot( sum(sal) for job in
( 'CLERK', 'SALESMAN', 'MANAGER',
'ANALYST', 'PRESIDENT' ) )
order by deptno;

更好的是,我们现在可以不用指定列表:
select *
from (select deptno, job, sal
from scott.emp ) e
pivot( sum(sal) for job in
(select distinct JOB from emp))
order by deptno;

二 sequence
在11g以前,在PLSQL里使用sequence的值必须用使用这种方法:
select t_seq.nextval() into var from dual;
而11g里可以直接赋值:
var:=t_seq.nextval();

三 log_error脚本
这是ORACLE提供的一个记录错误的日志脚本,特别适合在PLSQL编程的时候碰见WHEN OTHERS THEN。

CREATE OR REPLACE FUNCTION getosuser RETURN VARCHAR2 IS
vOSUser user_users.username%TYPE;
BEGIN
SELECT osuser
INTO vOSUser
FROM sys.v_$session
WHERE sid = (
SELECT sid
FROM sys.v_$mystat
WHERE rownum = 1);

RETURN vOSUser;
EXCEPTION
WHEN OTHERS THEN
RETURN 'UNK';
END getosuser;

CREATE TABLE errorlog (
procname VARCHAR2(61),
loadfilename VARCHAR2(40),
runtime DATE DEFAULT SYSDATE,
osuser VARCHAR2(30),
mesgtext VARCHAR2(250));

CREATE OR REPLACE PROCEDURE log_error (
pProcName VARCHAR2,
pLoadFile VARCHAR2,
pMesgText VARCHAR2)
IS

PRAGMA AUTONOMOUS_TRANSACTION;

BEGIN
INSERT INTO errorlog
(procname, loadfilename, osuser, mesgtext)
VALUES
(pProcName, pLoadFile, getOSUser, pMesgText);
COMMIT;

-- No exception handler intentionally. Why?

END log_error;
/
当然这个脚本也是自治事务的一个好例子。

以下是ORACLE提供的另外一个处理异常的脚本:
CREATE TABLE error_log (
error_timestamp TIMESTAMP(9),
database_name VARCHAR(50),
instance_number NUMBER,
error_number NUMBER,
error_message VARCHAR2(255),
logged_on_as VARCHAR2(30),
client_host VARCHAR2(50),
service_name VARCHAR2(30));

CREATE OR REPLACE PROCEDURE error_trap IS
odbname VARCHAR2(50); -- Oracle database name
oinst NUMBER; -- Oracle instance number
enum NUMBER; -- Error Message number
emsg VARCHAR2(250); -- Error text
curschema VARCHAR2(30);
clihost VARCHAR2(50);
serv_name VARCHAR2(30);

-- PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
enum := sqlcode;
emsg := sqlerrm;

odbname := ora_database_name;
oinst := ora_instance_num;

SELECT sys_context('USERENV', 'CURRENT_SCHEMA')
INTO curschema
FROM dual;

SELECT sys_context('USERENV', 'HOST')
INTO clihost
FROM dual;

SELECT sys_context('USERENV', 'SERVICE_NAME')
INTO serv_name
FROM dual;

INSERT INTO error_log
(error_timestamp, database_name, instance_number,
error_number, error_message, logged_on_as,
client_host, service_name)
VALUES
(SYSTIMESTAMP, odbname, oinst, enum, emsg,
curschema, clihost, serv_name);
COMMIT;
END error_trap;
/

CREATE OR REPLACE TRIGGER error_trig
AFTER SERVERERROR ON DATABASE
CALL error_trap
/

BEGIN
RAISE zero_divide;
END;
/

CREATE TABLE errors (
module VARCHAR2(50),
seq_number NUMBER,
error_number NUMBER,
error_mesg VARCHAR2(100),
error_stack VARCHAR2(2000),
call_stack VARCHAR2(2000),
timestamp DATE);

ALTER TABLE errors
ADD CONSTRAINT pk_errors
PRIMARY KEY (module, seq_number)
USING INDEX
TABLESPACE indx_sml;

CREATE TABLE call_stacks (
module VARCHAR2(50),
seq_number NUMBER,
call_order NUMBER,
object_handle VARCHAR2(10),
line_num NUMBER,
object_name VARCHAR2(80));

ALTER TABLE call_stacks
ADD CONSTRAINT pk_call_stacks
PRIMARY KEY (module, seq_number, call_order)
USING INDEX
TABLESPACE indx_sml;

ALTER TABLE call_stacks
ADD CONSTRAINT fk_cs_errors
FOREIGN KEY (module, seq_number)
REFERENCES errors (module, seq_number)
ON DELETE CASCADE;

CREATE TABLE error_stacks (
module VARCHAR2(50),
seq_number NUMBER,
error_order NUMBER,
facility CHAR(3),
error_number NUMBER(5),
error_mesg VARCHAR2(100));

ALTER TABLE error_stacks
ADD CONSTRAINT pk_error_stacks
PRIMARY KEY (module, seq_number, error_order)
USING INDEX
TABLESPACE indx_sml;

ALTER TABLE error_stacks
ADD CONSTRAINT fk_es_errors
FOREIGN KEY (module, seq_number)
REFERENCES errors (module, seq_number)
ON DELETE CASCADE;

CREATE SEQUENCE error_seq
START WITH 1
INCREMENT BY 1;

CREATE OR REPLACE PACKAGE ErrorPkg AS

/* Generic error handling package, using DBMS_UTILITY.FORMAT_ERROR_STACK and DBMS_UTILITY.FORMAT_CALL_STACK. This package stores general error information in the errors table, with detailed call stack and error stack information in the call_stacks and error_stacks tables, respectively.

Entry point for handling errors. HandleAll should be called from all exception handlers where you want the error to be logged. p_Top should be TRUE only at the topmost level of procedure nesting. It should be FALSE at other levels.
*/

PROCEDURE HandleAll(p_Top BOOLEAN);

/*
Prints the error and call stacks (using DBMS_OUTPUT) for the given module and sequence number.
*/

PROCEDURE PrintStacks(p_Module IN errors.module%TYPE,
p_SeqNum IN errors.seq_number%TYPE);

/*
Unwinds the call and error stacks, and stores them in the errors and call stacks tables. Returns the sequence number under which the error is stored. If p_CommitFlag is TRUE,
then the inserts are committed. In order to use StoreStacks, an error must have been handled. Thus HandleAll should have been called with p_Top = TRUE.
*/

PROCEDURE StoreStacks(p_Module IN errors.module%TYPE,
p_SeqNum OUT errors.seq_number%TYPE,
p_CommitFlag BOOLEAN DEFAULT FALSE);

END ErrorPkg;
/

CREATE OR REPLACE PACKAGE BODY ErrorPkg IS

v_NewLine CONSTANT CHAR(1) := CHR(10);

v_Handled BOOLEAN := FALSE;
v_ErrorStack VARCHAR2(2000);
v_CallStack VARCHAR2(2000);

PROCEDURE HandleAll(p_Top BOOLEAN) IS

BEGIN
IF p_Top THEN
v_Handled := FALSE;
ELSIF NOT v_Handled THEN
v_Handled := TRUE;
v_ErrorStack := DBMS_UTILITY.FORMAT_ERROR_STACK;
v_CallStack := DBMS_UTILITY.FORMAT_CALL_STACK;
END IF;
END HandleAll;
--===================================================
PROCEDURE PrintStacks(
p_Module IN errors.module%TYPE,
p_SeqNum IN errors.seq_number%TYPE)
IS

v_TimeStamp errors.timestamp%TYPE;
v_ErrorMsg errors.error_mesg%TYPE;

CURSOR c_CallCur IS
SELECT object_handle, line_num, object_name
FROM call_stacks
WHERE module = p_Module
AND seq_number = p_SeqNum
ORDER BY call_order;

CURSOR c_ErrorCur IS
SELECT facility, error_number, error_mesg
FROM error_stacks
WHERE module = p_Module
AND seq_number = p_SeqNum
ORDER BY error_order;

BEGIN
SELECT timestamp, error_mesg
INTO v_TimeStamp, v_ErrorMsg
FROM errors
WHERE module = p_Module
AND seq_number = p_SeqNum;

-- Output general error information.
dbms_output.put_line(TO_CHAR(v_TimeStamp, 'DD-MON-YY HH24:MI:SS'));
dbms_output.put(' Module: ' || p_Module);
dbms_output.put(' Error #' || p_SeqNum || ': ');
dbms_output.put_line(v_ErrorMsg);

-- Output the call stack.
dbms_output.put('Complete Call Stack:');
dbms_output.put(' Object Handle Line Number Object Name');
dbms_output.put_line(' ------------- ----------- -----------');

FOR v_CallRec in c_CallCur
LOOP
dbms_output.put(RPAD(' ' || v_CallRec.object_handle, 15));
dbms_output.put(RPAD(' ' || TO_CHAR(v_CallRec.line_num), 13));
dbms_output.put_line(' ' || v_CallRec.object_name);
END LOOP;

-- Output the error stack.
dbms_output.put_line('Complete Error Stack:');

FOR v_ErrorRec in c_ErrorCur
LOOP
dbms_output.put(' ' || v_ErrorRec.facility || '-');
dbms_output.put(TO_CHAR(v_ErrorRec.error_number) || ': ');
dbms_output.put_line(v_ErrorRec.error_mesg);
END LOOP;
END PrintStacks;
--===================================================
PROCEDURE StoreStacks(p_Module IN errors.module%TYPE,
p_SeqNum OUT errors.seq_number%TYPE,
p_CommitFlag BOOLEAN DEFAULT FALSE)
IS

v_SeqNum NUMBER;
v_Index NUMBER;
v_Length NUMBER;
v_End NUMBER;
v_Call VARCHAR2(100);
v_CallOrder NUMBER := 1;
v_Error VARCHAR2(120);
v_ErrorOrder NUMBER := 1;

v_Handle call_stacks.object_handle%TYPE;
v_LineNum call_stacks.line_num%TYPE;
v_ObjectName call_stacks.object_name%TYPE;

v_Facility error_stacks.facility%TYPE;
v_ErrNum error_stacks.error_number%TYPE;
v_ErrMsg error_stacks.error_mesg%TYPE;

v_FirstErrNum errors.error_number%TYPE;
v_FirstErrMsg errors.error_mesg%TYPE;

BEGIN
-- Get the error sequence number.
SELECT error_seq.nextval
INTO v_SeqNum
FROM dual;

p_SeqNum := v_SeqNum;

-- Insert first part of header info. into the errors table
INSERT INTO errors
(module, seq_number, error_stack, call_stack, timestamp)
VALUES
(p_Module, v_SeqNum, v_ErrorStack, v_CallStack, SYSDATE);

/*
Unwind the error stack to get each error out by scanning the
error stack string. Start with the index at the beginning of
the string
*;
v_Index := 1;

/*
Loop through the string, finding each newline
A newline ends each error on the stack
*/
WHILE v_Index < LENGTH(v_ErrorStack) LOOP
-- v_End is the position of the newline.
v_End := INSTR(v_ErrorStack, v_NewLine, v_Index);

-- The error is between the current index and the newline
v_Error := SUBSTR(v_ErrorStack, v_Index, v_End - v_Index);

-- Skip over the current error, for the next iteration
v_Index := v_Index + LENGTH(v_Error) + 1;

/* An error looks like 'facility-number: mesg'. Get each
piece out for insertion. The facility is the first 3
characters of the error.
*/

v_Facility := SUBSTR(v_Error, 1, 3);

-- Remove the facility and the dash (always 4 characters)
v_Error := SUBSTR(v_Error, 5);

-- Next get the error number
v_ErrNum := TO_NUMBER(SUBSTR(v_Error, 1, INSTR(v_Error,
':') - 1));

-- Remove the error number, colon & space (always 7 chars)
v_Error := SUBSTR(v_Error, 8);

-- What's left is the error message
v_ErrMsg := v_Error;

/*
Insert the errors, and grab the first error number and
message while we're at it
*/

INSERT INTO error_stacks
(module, seq_number, error_order, facility, error_number,
error_mesg)
VALUES
(p_Module, p_SeqNum, v_ErrorOrder, v_Facility, v_ErrNum,
v_ErrMsg);

IF v_ErrorOrder = 1 THEN
v_FirstErrNum := v_ErrNum;
v_FirstErrMsg := v_Facility || '-' || TO_NUMBER(v_ErrNum)
|| ': ' || v_ErrMsg;
END IF;

v_ErrorOrder := v_ErrorOrder + 1;
END LOOP;

-- Update the errors table with the message and code
UPDATE errors
SET error_number = v_FirstErrNum,
error_mesg = v_FirstErrMsg
WHERE module = p_Module
AND seq_number = v_SeqNum;

/*
Unwind the call stack to get each call out by scanning the
call stack string. Start with the index after the first call
on the stack. This will be after the first occurrence of
'name' and the newline.
*/
v_Index := INSTR(v_CallStack, 'name') + 5;

/* Loop through the string, finding each newline. A newline
ends each call on the stack.
*/
WHILE v_Index < LENGTH(v_CallStack) LOOP
-- v_End is the position of the newline
v_End := INSTR(v_CallStack, v_NewLine, v_Index);

-- The call is between the current index and the newline
v_Call := SUBSTR(v_CallStack, v_Index, v_End - v_Index);

-- Skip over the current call, for the next iteration
v_Index := v_Index + LENGTH(v_Call) + 1;

/*
Within a call, we have the object handle, then the line
number, then the object name, separated by spaces. Separate
them out for insertion.

-- Trim white space from the call first.
*/
v_Call := TRIM(v_Call);

-- First get the object handle
v_Handle := SUBSTR(v_Call, 1, INSTR(v_Call, ' '));

-- Remove the object handle,then the white space
v_Call := SUBSTR(v_Call, LENGTH(v_Handle) + 1);
v_Call := TRIM(v_Call);

-- Get the line number
v_LineNum := TO_NUMBER(SUBSTR(v_Call,1,INSTR(v_Call,' ')));

-- Remove the line number, and white space
v_Call := SUBSTR(v_Call, LENGTH(v_LineNum) + 1);
v_Call := TRIM(v_Call);

-- What is left is the object name
v_ObjectName := v_Call;

-- Insert all calls except the call for ErrorPkg
IF v_CallOrder > 1 THEN
INSERT INTO call_stacks
(module, seq_number, call_order, object_handle, line_num,
object_name)
VALUES
(p_Module, v_SeqNum, v_CallOrder, v_Handle, v_LineNum,
v_ObjectName);
END IF;

v_Callorder := v_CallOrder + 1;
END LOOP;

IF p_CommitFlag THEN
COMMIT;
END IF;
END StoreStacks;

END ErrorPkg;
/

四 游标转化
DBMS_SQL.TO_CURSOR_NUMBER
DBMS_SQL.TO_REFCURSOR
这两个函数可以进行游标转换。
declare
l_rcursor sys_refcursor;
l_colCnt number;
l_descTbl dbms_sql.desc_tab;
begin
open l_rcursor for select * from all_users;

dbms_sql.describe_columns
( c => dbms_sql.to_cursor_number(l_rcursor),
col_cnt => l_colCnt,
desc_t => l_descTbl );

for i in 1 .. l_colCnt
loop
dbms_output.put_line( l_descTbl(i).col_name );
end loop;
end;
/

declare
l_cursor number := dbms_sql.open_cursor;
l_refcursor sys_refcursor;
l_data varchar2(30);
begin
dbms_sql.parse
( l_cursor,
'select username from all_users where rownum <= 5',
dbms_sql.native );
if ( dbms_sql.execute( l_cursor ) != 0 )
then
raise program_error;
end if;
l_refcursor := dbms_sql.to_refcursor(l_cursor);
loop
fetch l_refcursor into l_data;
exit when l_refcursor%notfound;
dbms_output.put_line( l_data );
end loop;
close l_refcursor;
end;

五 本地编译
本地编译比普通编译更慢而且不能调试,但运行速度更快。
alter procedure p compile
PLSQL_Warnings = 'enable:all'
PLSQL_Optimize_Level = 2
PLSQL_CCFlags = 'Simple:true'
PLSQL_Code_Type = interpreted
reuse settings
/

alter procedure p compile
PLSQL_Warnings = 'enable:all'
PLSQL_Optimize_Level = 2
PLSQL_CCFlags = 'Simple:false'
PLSQL_Code_Type = native
reuse settings
/

六 复合触发器:
create trigger t for update on t compound trigger
g_data varchar2(200) := 'begin state';

before statement is
begin
dbms_output.put_line( g_data );
dbms_output.put_line( 'before statement...' ); g_data := 'before statement, ';
end before statement;

before each row is
begin
dbms_output.put_line( 'before each row...' ); g_data := g_data || 'before each row, ';
end before each row;

after each row is
begin
dbms_output.put_line( 'after each row...' ); g_data := g_data || 'after each row, ';
end after each row;

after statement is
begin
dbms_output.put_line( 'after statement...' ); g_data := g_data || 'after statement';
dbms_output.put_line( g_data );
end after statement;
end;

七 Named and Mixed Notation in SQL
create or replace function f
( x in number default 1,
y in number default 2,
z in number default 3 )
return number
as
begin
return x+y+z;
end;
/
pause

clear screen
select f(4,5,6) from dual;
select f(10, z=>200) from dual;
select f(z=>10) from dual;
select f(y=>20,z=>10) from dual;

八 闪回数据归档
这个功能很强,如果有足够的归档闪回数据,你甚至可以闪回十年前的数据。

没有评论: