不知道從哪裏得到這個文檔,覺得有用就放上來了
-數據庫端SQL語法的遷移
以下爲常用的SQL語法遷移,包括數據類型、ID列向SEQUENCE遷移、表(主鍵、外鍵、CHECK、UNIQUE、DEFAULT、INDEX)、遊標、存儲過程、函數、觸發器、常用SQL語法與函數幾個方面,考慮SQL SERVER的實際情況,沒有涉及Oracle特有的PACKAGE、EXCEPTION等。在以下的描述中,將SQL SERVER的TRANSACT-SQL簡稱爲T-SQL。在ORACLE中,其語法集稱爲PL/SQL。
<一> 數據類型的遷移
<1>、ORACLE端語法說明
在ORACLE中,分析其數據類型,大致可分爲數字、字符、日期時間和非凡四大類。其中,數字類型有NUMBER;字符類型有CHAR與VARCHAR2;日期時間類型只有DATE一種;除此之外,LONG、RAW、LONG RAW、BLOB、CLOB和BFILE等數據類型都可視爲非凡數據類型。
<2>、SQL SERVER端語法說明
在SQL SERVER中,參照上面對ORACLE的劃分,數據類型也大致可分爲數字、字符、日期時間和非凡四大類。數字類型又可分爲精確數值、近似數值、整數、二進制數、貨幣等幾類,其中,精確數值有DECIMAL[(P[, S])]與NUMERIC[(P[, S])];近似數值有FLOAT[(N)];整數有INT、SMALLINT、TINYINT;二進制數有BINARY[(N)]、VARBINARY[(N)];貨幣有MONEY、SMALLMONEY。字符類型有CHAR[(N)]與VARCHAR[(N)]。日期時間類型有DATETIME、SMALLDATETIME。除此之外,BIT、TIMESTAMP、TEXT和IMAGE、BINARY VARING等數據類型都可視爲非凡數據類型。
<3>、從SQL SERVER向ORACLE的遷移方案
比較ORACLE與SQL SERVER在數據類型上的不同,當從SQL SERVER向ORACLE遷移時,可以做如下調整:
SQL SERVER
ORACLE
數字類型
DECIMAL[(P[, S])]
NUMBER[(P[, S])]
NUMERIC[(P[, S])]
NUMBER[(P[, S])]
FLOAT[(N)]
NUMBER[(N)]
INT
NUMBER
SMALLINT
NUMBER
TINYINT
NUMBER
MONEY
NUMBER[19,4]
SMALLMONEY
NUMBER[19,4]
字符類型
CHAR[(N)]
CHAR[(N)]
VARCHAR[(N)]
VARCHAR2[(N)]
日期時間類型
DATETIME
DATE
SMALLDATETIME
DATE
其它
TEXT
CLOB
IMAGE
BLOB
BIT
NUMBER(1)
方法:
公司原系統中的Money 用於金額時轉換用number(14,2);用於單價時用 number(10,4)代替;
<二> ID列向SEQUENCE遷移
<1>、SQL SERVER端語法說明
在SQL SERVER中,可以將數據庫中的某一字段定義爲IDENTITY列以做主鍵識別,如:
jlbh numeric(12,0) identity(1,1) /*記錄編號字段*/
CONSTRAINT PK_tbl_example PRIMARY KEY nonclustered (jlbh) /*主鍵約束*/
在這裏,jlbh是一個ID列,在向具有該列的表插入記錄時,系統將從1開始以1的步長自動對jlbh的值進行維護。
<2>、ORACLE端語法說明
但在ORACLE中,沒有這樣的ID列定義,而是採用另一種方法,即創建SEQUENCE。
如:
/*--1、創建各使用地區編碼表--*/
drop table LT_AREA;
create table LT_AREA
(
area_id number(5,0) NOT NULL, /*地區編碼*/
area_name varchar2(20) NOT NULL, /*地區名稱*/
constraint PK_LT_AREA PRIMARY KEY(area_id)
);
/*--2、創建SEQUENCE,將列area_id 類ID化--*/
drop sequence SEQ_LT_AREA;
create sequence SEQ_LT_AREA increment by 1 /*該SEQUENCE以1的步長遞增*/
start with 1 maxvalue 99999; /*從1開始,最大增長到99999*/
/*--3、實際操作時引用SEQUENCE的下一個值--*/
insert into LT_AREA(area_id, area_name) values(SEQ_LT_AREA.NEXTVAL, '深圳');
insert into LT_AREA(area_id, area_name) values(SEQ_LT_AREA.NEXTVAL, '廣州');
insert into LT_AREA(area_id, area_name) values(SEQ_LT_AREA.NEXTVAL, '北京');
/*--4、新插入連續三條記錄後,下一條語句運行後,‘上海’地區的area_id爲4--*/
insert into LT_AREA(area_id, area_name) values(SEQ_LT_AREA.NEXTVAL, '上海');
<3>、從SQL SERVER向ORACLE的遷移方案
根據以上分析,當從SQL SERVER向ORACLE遷移時,可以做如下調整:
1、去掉建表語句中有關ID列的identity聲明要害字;
2、創建SEQUENCE,將此SEQUENCE與需類ID化的列對應;
3、在INSERT語句中對相應列引用其SEQUENCE值:SEQUENCENAME.NEXTVAL
實際上,處理以上情況在ORACLE中採用的方法爲對有自動增長字段的表增加一插入前觸發器(具體資料見後“觸發器”一節),如下:
CREATE OR REPLACE TRIGGER GenaerateAreaID
BEFORE INSERT ON LT_AREA
FOR EACH ROW
Select SEQ_LT_AREA.NEXTVAL INTO :NEW.ID
FROM DUAL;
BEGIN
END GenaerateAreaID;
GenaerateAreaID實際上修改了僞記錄:new的area_id值。 :new最有用的一個特性----當該語句真正被執行時,:new中的存儲內容就會被使用。所以系統每次都能自動生成新的號碼。
<三> 表(主鍵、外鍵、CHECK、UNIQUE、DEFAULT、INDEX)
<1>、SQL SERVER端語法說明
有如下SQL SERVER語句:
/* ------------------------ 創建employee 表------------------------ */
IF EXISTS(SELECT 1 FROM SYSOBJECTS WHERE NAME = ‘employee’
AND TYPE = ‘U’)
DROP TABLE employee
GO
CREATE TABLE employee
(
emp_id empid /*empid爲用戶自定義數據類型*/
/*創建自命名主鍵約束*/
CONSTRAINT PK_employee PRIMARY KEY NONCLUSTERED
/*創建自命名CHECK約束*/
CONSTRAINT CK_emp_id CHECK (emp_id LIKE
'[A-Z][A-Z][A-Z][1-9][0-9][0-9][0-9][0-9][FM]' or
emp_id LIKE '[A-Z]-[A-Z][1-9][0-9][0-9][0-9][0-9][FM]'),
/* CHECK約束說明:Each employee ID consists of three characters that
represent the employee's initials, followed by a five
digit number ranging from 10000 to 99999 and then the
employee's gender (M or F). A (hyphen) - is acceptable
for the middle initial. */
fname varchar(20) NOT NULL,
minit char(1) NULL,
lname varchar(30) NOT NULL,
ss_id varchar(9) UNIQUE, /*創建唯一性約束*/
job_id smallint NOT NULL
DEFAULT 1, /*設定DEFAULT值*/
job_lvl tinyint
DEFAULT 10, /*設定DEFAULT值*/
/* Entry job_lvl for new hires. */
pub_id char(4) NOT NULL
DEFAULT ('9952') /*設定DEFAULT值*/
REFERENCES publishers(pub_id), /*創建系統命名外鍵約束*/
/* By default, the Parent Company Publisher is the company
to whom each employee reports. */
hire_date datetime NOT NULL
DEFAULT (getdate()), /*設定DEFAULT值*/
/* By default, the current system date will be entered. */
CONSTRAINT FK_employee_job FOREIGN KEY (job_id)
REFERENCES jobs(job_id) /*創建自命名外鍵約束*/
)
GO
/* --------------------- 創建employee表上的index --------------------- */
IF EXISTS (SELECT 1 FROM sysindexes
WHERE name = 'emp_pub_id_ind')
DROP INDEX employee. emp_pub_id_ind
GO
CREATE INDEX emp_pub_id_ind
ON employee(pub_id)
GO
<2>、ORACLE端語法說明
在ORACLE端的語法如下:
/* ---------------------- 創建employee 表---------------------- */
DROP TABLE employee;
CREATE TABLE employee
(
emp_id varchar2(9) /*根據用戶自定義數據類型的定義調整爲varchar2(9)*/
/*創建自命名主鍵約束*/
CONSTRAINT PK_employee PRIMARY KEY NONCLUSTERED
/*創建自命名CHECK約束*/
CONSTRAINT CK_emp_id CHECK (emp_id LIKE
'[A-Z][A-Z][A-Z][1-9][0-9][0-9][0-9][0-9][FM]' or
emp_id LIKE '[A-Z]-[A-Z][1-9][0-9][0-9][0-9][0-9][FM]'),
/* CHECK約束說明:Each employee ID consists of three characters that
represent the employee's initials, followed by a five
digit number ranging from 10000 to 99999 and then the
employee's gender (M or F). A (hyphen) - is acceptable
for the middle initial. */
fname varchar2(20) NOT NULL,
minit varchar2(1) NULL,
lname varchar2(30) NOT NULL,
ss_id varchar2(9) UNIQUE, /*創建唯一性約束*/
job_id number(5,0) NOT NULL
/*這裏考慮了SMALLINT的長度,也可調整爲number*/
DEFAULT 1, /*設定DEFAULT值*/
job_lvl number(3,0)
/*這裏考慮了TINYINT的長度,也可調整爲number*/
DEFAULT 10, /*設定DEFAULT值*/
/* Entry job_lvl for new hires. */
pub_id varchar2(4) NOT NULL
DEFAULT ('9952') /*設定DEFAULT值*/
REFERENCES publishers(pub_id), /*創建系統命名外鍵約束*/
/* By default, the Parent Company Publisher is the company
to whom each employee reports. */
hire_date date NOT NULL
DEFAULT SYSDATE, /*設定DEFAULT值*/
/*這裏,SQL SERVER的getdate()調整爲ORACLE的SYSDATE*/
/* By default, the current system date will be entered. */
CONSTRAINT FK_employee_job FOREIGN KEY (job_id)
REFERENCES jobs(job_id) /*創建自命名外鍵約束*/
);
/* -------------------- 創建employee表上的index -------------------- */
DROP INDEX employee. emp_pub_id_ind;
CREATE INDEX emp_pub_id_ind ON employee(pub_id);
<3>、從SQL SERVER向ORACLE的遷移方案
比較這兩段SQL代碼,可以看出,在創建表及其主鍵、外鍵、CHECK、UNIQUE、DEFAULT、INDEX時,SQL SERVER 與ORACLE的語法大致相同,但時遷移時要注重以下情況:
(1) Oracle定義表字段的default屬性要緊跟字段類型之後,如下:
Create table MZ_Ghxx
( ghlxh number primay key ,
rq date default sysdate not null,
….
)
而不能寫成
Create table MZ_Ghxx
( ghlxh number primay key ,
rq date not null default sysdate,
….
)
(2)T-SQL定義表結構時,假如涉及到用默認時間和默認修改人員,全部修改如下:
ZHXGRQ DATE DEFAULT SYSDATE NULL,
ZHXGR CHAR(8) DEFAULT ‘FUTIAN’ NULL,
(3)如表有identity定段,要先將其記錄下來,建完表之後,馬上建相應的序列和表觸發器,並作爲記錄。
<四> 遊標
<1>、SQL SERVER端語法說明
1、DECLARE CURSOR語句
語法:
DECLARE cursor_name [INSENSITIVE] [SCROLL] CURSOR
FOR select_statement
[FOR {READ ONLY UPDATE [OF column_list ]}]
例:
DECLARE authors_cursor CURSOR FOR
SELECT au_lname, au_fname
FROM authors
WHERE au_lname LIKE ‘B%’
ORDER BY au_lname, au_fname
2、OPEN語句
語法:
OPEN cursor_name
例:
OPEN authors_cursor
3、FETCH語句
語法:
FETCH
[ [ NEXT PRIOR FIRST LAST ABSOLUTE n RELATIVE n ]
FROM cursor_name
[INTO @variable_name1, @variable_name2,… ]
例:
FETCH NEXT FROM authors_cursor
INTO @au_lname, @au_fname
4、CLOSE語句
語法:
CLOSE cursor_name
例:
CLOSE authors_cursor
5、DEALLOCATE語句
語法:
DEALLOCATE cursor_name
例:
DEALLOCATE authors_cursor
6、遊標中的標準循環與循環終止條件判定
(1)FETCH NEXT FROM authors_cursor INTO @au_lname, @au_fname
(2)-- Check @@FETCH_STATUS to see if there are any more rows to fetch.
WHILE @@FETCH_STATUS = 0
BEGIN
-- Concatenate and display the current values in the variables.
PRINT "Author: " + @au_fname + " " + @au_lname
-- This is executed as long as the previous fetch sUCceeds.
FETCH NEXT FROM authors_cursor INTO @au_lname, @au_fname
END
(3)CLOSE authors_cursor
7、隱式遊標
MSSQLServer中對於數據操縱語句受影響的行數,有一個全局的變量:@@rowcount,其實它是一個隱式的遊標,它記載了上條數據操縱語句所影響的行數,當@@rowcount小於1時,表時,上次沒有找到相關的記錄,如下:
Update students set lastname = ‘John’ where student_id = ‘301’
If @@rowcount < 1 then
Insert into students values (‘301’,’stdiv’,’john’,’996-03-02’)
表示假如數據表中有學號爲“301”的記錄,則修改其名字爲“John”,假如找不到相應的記錄,則向數據庫中插入一條“John”的記錄。
8、示例:
-- Declare the variables to store the values returned by FETCH.
DECLARE @au_lname varchar(40), @au_fname varchar(20)
DECLARE authors_cursor CURSOR FOR
SELECT au_lname, au_fname
FROM authors
WHERE au_lname LIKE ‘B%’
ORDER BY au_lname, au_fname
OPEN authors_cursor
-- Perform the first fetch and store the values in variables.
-- Note: The variables are in the same order as the columns
-- in the SELECT statement.
FETCH NEXT FROM authors_cursor INTO @au_lname, @au_fname
-- Check @@FETCH_STATUS to see if there are any more rows to fetch.
WHILE @@FETCH_STATUS = 0
BEGIN
-- Concatenate and display the current values in the variables.
PRINT "Author: " + @au_fname + " " + @au_lname
-- This is executed as long as the previous fetch succeeds.
FETCH NEXT FROM authors_cursor INTO @au_lname, @au_fname
END
CLOSE authors_cursor
DEALLOCATE authors_cursor
<2>、ORACLE端語法說明
1、 DECLARE CURSOR語句
語法:
CURSOR cursor_name IS select_statement;
例:
CURSOR authors_cursor IS
SELECT au_lname, au_fname
FROM authors
WHERE au_lname LIKE ‘B%’
ORDER BY au_lname, au_fname;
2、 OPEN語句
語法:
OPEN cursor_name
例:
OPEN authors_cursor;
3、 FETCH語句
語法:
FETCH cursor_name INTO variable_name1 [, variable_name2,… ] ;
例:
FETCH authors_cursor INTO au_lname, au_fname;
4、 CLOSE語句
語法:
CLOSE cursor_name
例:
CLOSE authors_cursor;
5、簡單遊標提取循環結構與循環終止條件判定
1> 用%FOUND做循環判定條件的WHILE循環
(1)FETCH authors_cursor INTO au_lname, au_fname ;
(2)WHILE authors_cursor%FOUND LOOP
-- Concatenate and display the current values in the variables.
DBMS_OUTPUT.ENABLE;
DBMS_OUTPUT.PUT_LINE( ‘Author: ‘ au_fname ‘ ‘ au_lname) ;
FETCH authors_cursor INTO au_lname, au_fname ;
END LOOP ;
(3)CLOSE authors_cursor ;
2> 用%NOTFOUND做循環判定條件的簡單LOOP...END LOOP循環
(1)OPEN authors_cursor;
(2)LOOP
FETCH authors_cursor INTO au_lname, au_fname ;
-- Exit loop when there are no more rows to fetch.
EXIT WHEN authors_cursor%NOTFOUND ;
-- Concatenate and display the current values in the variables.
DBMS_OUTPUT.ENABLE;
DBMS_OUTPUT.PUT_LINE( ‘Author: ‘ au_fname ‘ ‘ au_lname) ;
END LOOP ;
(3)CLOSE authors_cursor ;
3>用遊標式FOR循環,如下:
DECLARE
CURSOR c_HistoryStudents IS
SELECT id,first_name,last_name
FROM Students
WHERE major = ‘History’
BEGIN
FOR v_StudentData IN c_HistoryStudents LOOP
INSERT INTO registered_students
(student_id,first_name,last_name,department,course)
VALUES(v_StudentData.ID,v_StudentData.first_name, v_StudentData.last_name,’HIS’,301);
END LOOP;
COMMIT;
END;
首先,記錄v_StudentData沒有在塊的聲明部分進行聲明,些變量的類型是c_HistoryStudents%ROWTYPE,v_StudentData的作用域僅限於此FOR循環本身;其實,c_HistoryStudents以隱含的方式被打開和提取數據,並被循環關閉。
6、隱式遊標SQL%FOUND 與SQL%NOTFOUND
與MSSQL SERVER 一樣,ORACLE也有隱式遊標,它用於處理INSERT、DELETE和單行的SELECT..INTO語句。因爲SQL遊標是通過PL/SQL引擎打開和關閉的,所以OPEN、FETCH和CLOSE命令是無關的。但是遊標屬性可以被應用於SQL遊標,如下:
BEGIN
UPDATE rooms
SET number_seats = 100
WHERE room_id = 9990;
--假如找不相應的記錄,則插入新的記錄
IF SQL%NOTFOUND THEN
INSERT INTO rooms(room_id,number_seats)
VALUES (9990,100)
END IF
END;
7、示例:
-- Declare the variables to store the values returned by FETCH.
-- Declare the CURSOR authors_cursor.
DECLARE
au_lname varchar2(40) ;
au_fname varchar2(20) ;
CURSOR authors_cursor IS
SELECT au_lname, au_fname
FROM authors
WHERE au_lname LIKE ‘B%’
ORDER BY au_lname, au_fname;
BEGIN
OPEN authors_cursor;
FETCH authors_cursor INTO au_lname, au_fname ;
WHILE authors_cursor%FOUND LOOP
-- Concatenate and display the current values in the variables.
DBMS_OUTPUT.ENABLE;
DBMS_OUTPUT.PUT_LINE( ‘Author: ‘ au_fname ‘ ‘ au_lname) ;
FETCH authors_cursor INTO au_lname, au_fname ;
END LOOP ;
CLOSE authors_cursor ;
END ;
<3>、從SQL SERVER向ORACLE的遷移方案
比較上述SQL代碼,在遷移過程中要做如下調整:
(1)T-SQL對CURSOR的聲明在主體代碼中,而PL/SQL中對CURSOR的聲明與變
量聲明同步,都要在主體代碼(BEGIN要害字)之前聲明,所以在遷移時要
將遊標聲明提前,MSSQL SERVER的Cursor定義後的參數省去;
(2)對CUOSOR操作的語法中PL/SQL沒有T-SQL裏DEALLOCATE CURSOR這一部分,
遷移時要將該部分語句刪除。
(3)PL/SQL 與T-SQL對遊標中的循環與循環終止條件判定的處理不太一樣,根
據前面的討論並參考後面對兩種語法集進行控制語句對比分析部分的敘述,
建議將T-SQL中的遊標提取循環調整爲PL/SQL中的WHILE遊標提取循環結
構,這樣可保持循環的基本結構大致不變,同時在進行循環終止條件判定時
要注重將T-SQL中的對@@FETCH_STATUS全局變量的判定調整爲對
CURSOR_NAME%FOUND語句進行判定。
(4)對於T-SQL,沒有定義語句結束標誌,而PL/SQL用“;”結束語句。
(5)對於原MSSQL SERVER類型的遊標,假如遊標取出的值沒有參與運算的,全部採用FOR循環方式來替換;而對於取出的值還要進行其它運算的,可以採用直接在定義變量位置定義變量。
(6)MSSQL中對於同一遊標重複定義幾次的情況在ORACLE中可通過遊標變量來解決.如下:
MSSQL SERVER 中:
Declare cur_ypdm cursor for
Select * from yp
Open cur_yp
Fetch cur_yp into @yp,@mc …
While @@fetch_status <> -1
Begin
If @@fetch_status <> -2
Begin
….
End
Fetch cur_yp into @yp,@mc …
End
Close cur_ypdm
Deallocate cur_ypdm
..
Declare cur_ypdm cursor for
Select * from yp where condition 1
Open cur_yp
Fetch cur_yp into @yp,@mc …
While @@fetch_status <> -1
Begin
If @@fetch_status <> -2
Begin
….
End
Fetch cur_yp into @yp,@mc …
End
Close cur_ypdm
Deallocate cur_ypdm
..
Declare cur_ypdm cursor for
Select * from yp where condition 2
Open cur_yp
Fetch cur_yp into @yp,@mc …
While @@fetch_status <> -1
Begin
If @@fetch_status <> -2
Begin
….
End
Fetch cur_yp into @yp,@mc …
End
Close cur_ypdm
Deallocate cur_ypdm
..
在程序中,三次定義同一遊標cur_yp
在遷移過程中,最好先定義一遊標變量,在程序中用open打開,如下:
declare
type cur_type is ref cur_type;
cur_yp cur_type;
…
begin
open cur_yp for select * from yp;
loop
fetch cur_yp into yp,mc …
Exit When cur_yp%NotFound;
….
end loop;
close cur_yp;
open cur_yp for select * from yp where condition1;
loop
fetch cur_yp into yp,mc …
Exit When cur_yp%NotFound;
….
end loop;
close cur_yp;
open cur_yp for select * from yp where condition2;
loop
fetch cur_yp into yp,mc …
Exit When cur_yp%NotFound;
….
end loop;
close cur_yp;
end;
(7)請注重,遊標循環中中一定要退出語名,要不然執行時會出現死循環。
<五> 存儲過程/函數
<1>、SQL SERVER端語法說明
1、語法:
CREATE PROC[EDURE] [owner.]procedure_name [;number]
[ (parameter1[, parameter2]…[, parameter255])]
[ {FOR REPLICATION} {WITH RECOMPILE}
[ {[WITH] [ , ] } ENCRYPTION ] ]
AS
sql_statement [...n]
其中,Parameter = @parameter_name datatype [=default] [output]
說明:T-SQL中存儲過程的結構大致如下
CREATE PROCEDURE procedure_name
/*輸入、輸出參數的聲明部分*/
AS
DECLARE
/*局部變量的聲明部分*/
BEGIN
/*主體SQL語句部分*/
/*遊標聲明、使用語句在此部分*/
END
2、示例:
IF EXISTS(SELECT 1 FROM sysobjects
WHERE name = 'titles_sum' AND type = 'P')
DROP PROCEDURE titles_sum
GO
CREATE PROCEDURE titles_sum
@TITLE varchar(40) = '%', @SUM money OUTPUT
AS
BEGIN
SELECT 'Title Name' = title
FROM titles
WHERE title LIKE @TITLE
SELECT @SUM = SUM(price)
FROM titles
WHERE title LIKE @TITLE
END
<2>、ORACLE端PROCEDURE語法說明
1、語法:
CREATE [OR REPLACE] PROCEDURE procedure_name
[ (parameter1 [ {IN OUT IN OUT } ] type ,
…
parametern [ {IN OUT IN OUT } ] type ) ]
{ IS AS }
[BEGIN]
sql_statement [...n] ;
[END] ;
說明:PL/SQL中存儲過程的結構大致如下
CREATE OR REPLACE PROCEDURE procedure_name
( /*輸入、輸出參數的聲明部分*/ )
AS
/*局部變量、遊標等的聲明部分*/
BEGIN
/*主體SQL語句部分*/
/*遊標使用語句在此部分*/
EXCEPTION
/*異常處理部分*/
END ;
2、示例:
CREATE OR REPLACE PROCEDURE drop_class
( arg_student_id IN varchar2,
arg_class_id IN varchar2,
status OUT number )
AS
counter number ;
BEGIN
status := 0 ;
-- Verify that this class really is part of the student’s schedule.
select count (*) into counter
from student_schedule
where student_id = arg_student_id
and class_id = arg_class_id ;
IF counter = 1 THEN
delete from student_schedule
where student_id = arg_student_id
and class_id = arg_class_id ;
status := -1 ;
END IF ;
END ;
<3>ORACLE端FUNCTION語法說明
(1) 語法
CREATE [OR REPLACE] FUNCTION function_name
[(argument [{IN OUT IN OUT }] ) type,
…
[(argument [{IN OUT IN OUT }] ) type
RETURN return_type {IS AS}
BEGIN
…
END;
要害字return 指定了函數返回值的數據類型。它可以是任何合法的PL/SQL數據類型。每個函數都必須有一個return 子句,因爲在定義上函數必須返回一個值給調用環境。
(2)示例
CREATE OR REPLACE FUNCTION blanace_check(Person_Name IN varchar2)
RETURN NUMBER
IS
Balance NUMBER(10,2);
BEGIN
Select sum(decode(acton,’BOUGHT’,Amount,0))
INTO balance
FROM ledger
WHERE Person = Person_name;
RETURN (balance);
END;
(3)過程與函數的區別
函數可以返回一個值給調用環境;而過程不能,過程只能通過返回參數(帶“OUT”或“IN OUT”)傳回去數據。
<4>從SQL SERVER向ORACLE的遷移方案
通過比較上述SQL語法的差異,在遷移時必須注重以下幾點:
1、對於有返回單值的MSSQL存儲過程,在數據庫移值最好轉換成ORALCE的函數;對於MSSQL有大量數據的處理而又不需返回值的存儲過程轉換成ORACLE的過程
2、在T-SQL中,輸入、輸出參數定義部分在“CREATE…”和“AS”之間,前後
沒有括號;而在PL/SQL中必須有“(”和“)”與其他語句隔開。
3、在T-SQL中,聲明局部變量時,前面要有DECLARE要害字;
而在PL/SQL中不用DECLARE要害字。
4、在T-SQL中,參數名的第一個字符必須是“@”,並符合標識符的規定;
而在PL/SQL中,參數名除符合標識符的規定外沒有非凡說明,T-SQL中,對於參數可其數據類型及其長度和精度;但是PL/SQL中除了引用%TYPE和%ROWTYPE之外,不能在定義參數數據類型時給出長度和精度,如下:
CREATE OR REPLACE PROCEDURE PROC_SELE_YS
(YSDM CHAR(6),GZ NUMBER(14,4))
AS
BEGIN
…
END;
是錯誤的,應如下定義
CREATE OR REPLACE PROCEDURE PROC_SELE_YS
(YSDM CHAR,GZ NUMBER)
AS
BEGIN
…
END;
或者
CREATE OR REPLACE PROCEDURE PROC_SELE_YS
(YSDM YSDMB.YSDM%TYPE,GZ YSDMB.GZ%TYPE)
AS
BEGIN
…
END;
5、對於T-SQL,遊標聲明在主體SQL語句中,即聲明與使用語句同步;
而在PL/SQL中,遊標聲明在主體SQL語句之前,與局部變量聲明同步。
6、對於T-SQL,在主體SQL語句中用如下語句對局部變量賦值(初始值或
數據庫表的字段值或表達式):
“SELECT 局部變量名 = 所賦值(初始值或數據庫表的字段值或表達式)”;
而在PL/SQL中,將初始值賦給局部變量時,用如下語句:
“局部變量名 : = 所賦值(初始值或表達式);” ,
將檢索出的字段值賦給局部變量時,用如下語句:
“SELECT 數據庫表的字段值 INTO 局部變量名 …” 。
7、在PL/SQL中,可以使用%TYPE來定義局部變量的數據類型。說明如下:
例如,students表的first_name列擁有類型VARCHAR2(20),基於這點,
我們可以按照下述方式聲明一個變量:
V_FirstName VARCHAR2(20) ;
但是假如改變了first_name列的數據類型則必須修改該聲明語句,因此可以採
用%TYPE進行變量數據類型聲明:
V_FirstName students.first_name%TYPE ;
這樣,該變量在存儲過程編譯時將由系統自動確定其相應數據類型。
8、對於T-SQL,沒有定義語句結束標誌,而PL/SQL用“END <過程名>;”結束語句。
9、存儲過程的調用要注重:在MSSQLSERVER中的格式爲“EXEC Procedure_Name {arg1,arg2,…},但在ORACLE中直接引用過程名即可,如要執行存儲過程DefaltNo,其參數爲“9”,則執行時爲 Default(“9”)。
10、ORACLE 數據庫的存儲過程不支持用select 子句直接返回一個數據集,要做到通過程產生一記錄集有兩種方案:
方案一:採用包和遊標變量
第一步,創建一個包,定義一個遊標變量
create package p_name
is
type cursor_name is ref cursor;
end;
第二步,創建過程,但是基返回參數用包中的遊標類型
create procedure procedure_name(s in out p_name.cursor_name) is
begin
open s for select * from table_name...;
end;
這樣,通過存儲過程就可以返回一個數據集了,但用到這種情況,過程的參數中只這返回結果的遊標參數可以帶要害字”OUT”,其它不能帶”out”,否則,系統會出現導常。
方案二:通過中間表,建一中間表,其表格的列爲所需數據列再加上一個序列字段。過程的處理爲將數據插入到中間表中,同時通過
select userenv(‘sessionid’) from dual;取得當前連接會話的序號,將取得的序號值放置到序列字段中,同時存儲過程返回連接會話的序號,前臺PB程序直接訪問中間表,數據窗口在檢索時通過序號參數可將所需的數據檢索出來。
<六> 觸發器
<1>、SQL SERVER端語法說明
1、語法:
CREATE TRIGGER [owner.]trigger_name
ON [owner.]table_name
FOR { INSERT, UPDATE, DELETE }
[WITH ENCRYPTION]
AS
sql_statement [...n]
或者使用IF UPDATE子句:
CREATE TRIGGER [owner.]trigger_name
ON [owner.]table_name
FOR { INSERT, UPDATE }
[WITH ENCRYPTION]
AS
IF UPDATE (column_name)
[{AND OR} UPDATE (column_name)…]
sql_statement [ ...n]
2、示例:
IF EXISTS (SELECT 1 FROM sysobjects
WHERE name = 'reminder' AND type = 'TR')
DROP TRIGGER reminder
GO
CREATE TRIGGER employee_insupd
ON employee
FOR INSERT, UPDATE
AS
/* Get the range of level for this job type from the jobs table. */
DECLARE @min_lvl tinyint,
@max_lvl tinyint,
@emp_lvl tinyint,
@job_id smallint
SELECT @min_lvl = min_lvl,
@max_lvl = max_lvl,
@emp_lvl = i.job_lvl,
@job_id = i.job_id
FROM employee e, jobs j, inserted i
WHERE e.emp_id = i.emp_id AND i.job = j.job_id
IF (@job_id = 1) and (@emp_lvl <> 10)
BEGIN
RAISERROR ('Job id 1 eXPects the default level of 10.', 16, 1)
ROLLBACK TRANSACTION
END
ELSE
IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl)
BEGIN
RAISERROR ('The level for job_id:%d should be between %d and %d.',
16, 1, @job_id, @min_lvl, @max_lvl)
ROLLBACK TRANSACTION
END
GO
<2>、ORACLE端語法說明
1、語法:
CREATE [OR REPLACE] TRIGGER trigger_name
{ BEFORE AFTER } triggering_event ON table_name
[ FOR EACH ROW ]
[ WHEN trigger_condition ]
trigger_body ;
2、使用說明與示例:
(1)、上語法中,trigger_event 是對應於DML的三條語句INSERT、UPDATE、
DELETE;table_name是與觸發器相關的表名稱;FOR EACH ROW是可選
子句,當使用時,對每條相應行將引起觸發器觸發;condition是可選的
ORACLE BOOLEAN條件,當條件爲真時觸發器觸發;trigger_body是觸發
器觸發時執行的PL/SQL塊。
(2)、ORACLE觸發器有以下兩類:
1> 語句級(Statement-level)觸發器,在CREATE TRIGGER語句中不
包含FOR EACH ROW子句。語句級觸發器對於觸發事件只能觸發一次,
而且不能訪問受觸發器影響的每一行的列值。一般用語句級觸發器處理
有關引起觸發器觸發的SQL語句的信息——例如,由誰來執行和什麼時
間執行。
2> 行級(Row-level)觸發器,在CREATE TRIGGER語句中
包含FOR EACH ROW子句。行級觸發器可對受觸發器影響的每一行觸
發,並且能夠訪問原列值和通過SQL語句處理的新列值。行級觸發器的
典型應用是當需要知道行的列值時,執行一條事務規則。
(3)在觸發器體內,行級觸發器可以引用觸發器觸發時已存在的行的列值,這些
值倚賴於引起觸發器觸發的SQL語句。
1> 對於INSERT語句,要被插入的數值包含在new.column_name,這裏的
column_name是表中的一列。
2> 對於UPDATE語句,列的原值包含在old.column_name中,數據列的新
值在new.column_name中。
3> 對於DELETE語句,將要刪除的行的列值放在old.column_name中。
觸發語句
:old
:new
INSERT
無定義——所有字段都是NULL
當該語句完成時將要插入的數值
UPDATE
在更新以前的該行的原始取值
當該語句完成時將要更新的新值
DELETE
在刪除行以前的該行的原始取值
未定義——所有字段都是NULL
4> 在觸發器主體中,在new和old前面的“:”是必需的。而在觸發器的
WHEN子句中,:new和:old記錄也可以在WHEN子句的condition內部
引用,但是不需要使用冒號。例如,下面CheckCredits觸發器的主體僅
當學生的當前成績超過20時纔會被執行:
CREATE OR REPLACE TRIGGER CheckCredits
BEFORE INSERT OR UPDATE OF current_credits ON students
FOR EACH ROW
WHEN (new.current_credits > 20)
BEGIN
/*Trigger body goes here. */
END ;
但CheckCredits也可以按下面方式改寫:
CREATE OR REPLACE TRIGGER CheckCredits
BEFORE INSERT OR UPDATE OF current_credits ON students
FOR EACH ROW
BEGIN
IF :new.current_credits > 20 THEN
/*Trigger body goes here. */
END IF ;
END ;
注重,WHEN子句僅能用於行級觸發器,假如使用了它,那麼觸發器主體
僅僅對那些滿足WHEN子句指定的條件的行進行處理。
(4)觸發器的主體是一個PL/SQL塊,在PL/SQL塊中可以使用的所有語句在觸
發器主體中都是合法的,但是要受到下面的限制:
1> 觸發器不能使用事務控制語句,包括COMMIT、ROLLBACK或
SAVEPOINT。ORACLE保持這種限制的原因是:假如觸發器碰到錯誤時,
由觸發器導致的所有數據庫變換均能被回滾(roll back)取消;但假如
觸發器確認(commit)了對數據庫進行的部分變換,ORACLE就不能完全
回滾(roll back)整個事務。
2> 在觸發器主體中調用到的存儲過程的實現語句裏也不能使用事務控制語
句。
3> 觸發器主體不能聲明任何LONG或LONG RAW變量。而且,:new和:old
不能指向定義觸發器的表中的LONG或LONG RAW列。
4> 當聲明觸發器的表中有外鍵約束時,假如將定義觸發器的表和需要作爲
DELETE CASCADE參考完整性限制的結果進行更新的表稱爲變化表,
將外鍵相關聯的表稱爲限制表,則在此觸發器主體中的SQL語句不答應
讀取或修改觸發語句的任何變化表,也不答應讀取或修改限制表中的主
鍵、唯一值列或外鍵列。
(5)以下是建立一個事前插入觸發器的示例:
CREATE OR REPLACE TRIGGER Credit_Charge_Log_Ins_Before
BEFORE insert ON Credit_Charge_Log
FOR EACH ROW
DECLARE
Total_for_past_3days number ;
BEGIN
-- Check the credit charges for the past 3 days.
-- If they total more than $1000.00, log this entry
-- int the Credit_Charge_Attempt_Log for further handling.
select sum ( amount ) into total_for_past_3days
from Credit_Charge_Log
where Card_Number = :new.Card_Number
and Transaction_Date >= sysdate – 3;
IF total_for_past_3days > 1000.00 THEN
insert into credit_Charge_Attemp_Log
(Card_Number, Amount, Vendor_ID, Transaction_Date)
values
(:new.Card_Number, :new.Amount,
:new.Vendor_ID, :new.Transaction_Date);
END IF ;
END ;
<3>、從SQL SERVER向ORACLE的遷移方案
1、通過比較上面SQL語法的不同並考慮現有SQL SERVER的實際編程風格,在從
T-SQL向PL/SQL遷移時,要遵守下面規則:
1> 在CREATE TRIGGER定義中採用AFTER要害字,即調整爲事後觸發器。
2> 在CREATE TRIGGER定義中採用FOR EACH ROW要害字,即調整爲行級觸發
器。
3> 將觸發器主體中的“inserted”調整爲“:new”,將“deleted”調整爲“:old”。
4> 在觸發器主體中禁用CURSOR操作:new與:old。
5> 在觸發器主體中禁用COMMIT、ROLLBACK、SAVEPOINT等事務控制語句。
2、用觸發器解決ID列向SEQUENCE遷移的問題:
下面的GenerateStudentID觸發器使用了:new。這是一個before INSERT觸
發器,其目的是使用student_sequence序列所產生的數值填寫
students表的ID字段。
例:
CREATE OR REPLACE TRIGGER GenerateStudentID
BEFORE INSERT ON students
FOR EACH ROW
BEGIN
SELECT student_sequence.nextval
INTO :new.ID
FROM dual;
END;
在上面的觸發器主體中,GenerateStudentID實際上修改了:new.ID的值。這
是:new最有用的一個特性——當該語句真正被執行時,:new中的存儲內容就
將被使用。有了這個觸發器,我們就可以使用下面這樣的INSERT語句,而不
會產生錯誤:
INSERT INTO students (first_name, last_name)
VALUES (‘LUO’, ‘TAO’) ;
儘管我們沒有爲主鍵列ID(這是必需的)指定取值,觸發器將會提供所需要
的取值。事實上,假如我們爲ID指定了一個取值,它也將會被忽略,因爲觸
發器修改了它。假如我們使用下面的語句:
INSERT INTO students (ID, first_name, last_name)
VALUES (-789, ‘LUO’, ‘TAO’) ;
其處理結果還是相同的。無論在哪種情況下,student_sequence.nextval都
將用作ID列值。
由此討論,可以採用這種方法處理SQL SERVER中ID列向ORACLE的SEQUENCE
轉換的問題。
另外,由於上面的原因,我們不能在after行級觸發器中修改 :new,因爲該
語句已經被處理了。通常,:new僅僅在before行級觸發器中被修改,而:old
永遠不會被修改,僅僅可以從它讀出數據。
此外,:new和:old記錄僅僅在行級觸發器內部是有效的。假如試圖要從語句
級觸發器進行引用,將會得到一個編譯錯誤。因爲語句級觸發器只執行一次
——儘管語句要處理許多行——所以:new和:old是沒有意義的,因爲怎麼確
定它們引用的會是哪一行呢?
<七> 常用SQL語法與函數
<1>、SQL SERVER端常用語法說明
1、使用局部變量:
1> 變量定義:
DECLARE @variable_name datatype [,…]
例:
declare
@name varchar(30),
@type int
2> 給變量賦值:
方法一:
例:
declare @int_var int
select @int_var = 12
方法二:
例:
declare
@single_auth varchar(40),
@curdate datetime
select @single_auth = au_lname,
@curdate = getdate()
from authors
where au_id = ‘123-45-6789’
2、使用T-SQL標準控制結構:
1> 定義語句塊
語法:
BEGIN
Statements
END
2> IF ... ELSE語句
語法:
IF boolean_expression
{ statement statement_block }
ELSE
{ statement statement_block }
示例:
if (select avg(price) from titles where type = ‘business’) > $19.95
print ‘The average price is greater then $19.95’
else
print ‘The average price is less then $19.95’
3> IF EXISTS語句
語法:
IF [not] EXISTS (select_statement)
{ statement statement_block }
[ELSE
{ statement statement_block }]
示例:
declare
@lname varchar(40),
@msg varchar(255)
select @lname = ‘Smith’
if exists(select * from titles where au_lname = @lname)
begin
select @msg = ‘There are authors named’ + @lname
print @msg
end
else
begin
select @msg = ‘There are no authors named’ + @lname
print @msg
end
4> 循環語句:
WHILE
語法:
WHILE boolean_condition
[{ statement statement_block }]
[BREAK]
[condition]
示例:
declare
@avg_price money,
@max_price money,
@count_rows int,
@times_thru_the_loop int
select @avg_price = avg(price),
@max_price = max(price),
@count_rows = count(*),
@times_thru_the_loop = 0
from titles
while @avg_price < $25 and (@count_rows < 10 or @max_price < $50)
begin
select @avg_price = avg(price) * 1.05,
@max_price = max(price) * 1.05,
@time_thru_the_loop = @time_thru_the_loop + 1
end
if @time_thru_the_loop = 0
select @time_thru_the_loop = 1
update titles
set price = price * power(1.05, @time_thru_the_loop)
4> GOTO語句
語法:
GOTO label
...
label:
示例:
begin transaction
insert tiny(c1) values(1)
if @@error != 0 goto error_handler
commit transaction
return
error_handler:
rollback transaction
return
5> RETURN語句
語法:
RETURN
(1)用於無條件退出一個批處理、存儲過程或觸發器。
示例:
if not exists(select 1 from inventory
where item_num = @item_num)
begin
raiseerror 51345 ‘Not Found’
return
end
print ‘No error found’
return
(2)用於存儲過程中返回狀態值。
示例:
create procedure titles_for_a_pub
(@pub_name varchar(40) = null)
as
if @pub_name is null
return 15
if not exists(select 1 from publishers
where pub_name = @pub_name)
return –101
select t.tile from publishers p, titles t
where p.pub_id = t.pub_id
and pub_name = @pub_name
return 0
3、T-SQL中的遊標提取循環語句:
(1)FETCH [NEXT FROM] cursor_name INTO @variable_1, ...@variable_n
(2)WHILE @@FETCH_STATUS = 0
BEGIN
Other_statements
FETCH [NEXT FROM] cursor_name INTO @variable_1, ...@variable_n
END
(3)CLOSE cursor_name
4、T-SQL中的事務處理語句:
1> 開始一個事務:
BEGIN TRAN[SACTION [transaction_name]]
2> 提交一個事務:
COMMIT TRAN[SACTION [transaction_name]]
3> 回滾一個事務:
ROLLBACK TRAN[SACTION [transaction_name]]
4> 使用事務保存點:
BEGIN TRAN[SACTION [transaction_name]]
SAVE TRAN[SACTION] savepoint_name
ROLLBACK TRAN[SACTION] savepoint_name
COMMIT TRAN[SACTION [transaction_name]]
5、T-SQL中可用於錯誤判定或其它處理的全局變量:
1> @@rowcount: 前一條命令處理的行數
2> @@error: 前一條SQL語句報告的錯誤號
3> @@trancount: 事務嵌套的級別
4> @@transtate: 事務的當前狀態
5> @@tranchained: 當前事務的模式(鏈接的(chained)或非鏈接的)
6> @@servername: 本地SQL SERVER的名稱
7> @@version : SQL SERVER和O/S的版本級別
8> @@spid: 當前進程的id
9> @@identity: 上次insert操作中使用的identity值
10> @@nestlevel: 存儲過程/觸發器中的嵌套層
11> @@fetch_status: 遊標中上條fetch語句的狀態
6、使用標準內置錯誤消息發送函數:
函數說明:
RAISERROR ({msg_id msg_str}, severity, state
[, argument1 [,argument2][,...] )
[WITH LOG]
其中,msg_id表示錯誤號,用戶定義錯誤消息的錯誤號在50001到2147483647之
間,特定的消息會引起錯誤50000。msg_str是錯誤消息正文,最多可有255個字
符。Severity描述了與這個消息關聯的用戶定義的嚴重性級別,取值包括0和10
至25之間的任何整數。State描述了錯誤的“調用狀態”,它是1到127之間的整
數值。Argument定義用於代替在msg_str中定義的變量或對應與msg_id的消息的
參數。WITH LOG表示要在服務器錯誤日誌和事件日誌中記錄錯誤。
例1:
RAISEERROR( ‘Invalid customer id in order.’, 16, 1)
則返回:
Msg 50000, Level 16, State 1
Invalid customer id in order.
例2:
sp_addmessage 52000, 16, ‘Invalid customer id %s in order’
RAISEERROR( 52000, 16, 1, ‘ID52436’)
則返回:
Msg 52000, Level 16, State 1
Invalid customer id ID52436 in order.
<2>、ORACLE端常用語法說明
1、使用局部變量:
1> 定義變量:
VARIABLE_NAME DATA TYPE [ := INITIAL VALUE ] ;
例:定義變量
v_Num number;
v_string varchar2(50);
例:定義變量並賦初值
v_Num number := 1 ;
v_string varchar2(50) := ‘Hello world!’ ;
2> 給變量賦值:
方法一:
例:
v_Num := 1;
v_string := ‘Hello world!’;
方法二:
例:
SELECT first_name INTO v_String
FROM students
WHERE id = v_Num ;
2、使用PL/SQL標準控制結構:
1> 定義語句塊
語法:
BEGIN
Statements ;
END ;
2> IF ... THEN ... ELSE語句
語法:
IF boolean_expression THEN
{ statement statement_block } ;
[ELSIF boolean_expression THEN /*注重此處的寫法—— ELSIF */
{ statement statement_block } ;]
...
[ELSE
{ statement statement_block } ;]
END IF ;
示例:
v_NumberSeats rooms.number_seats%TYPE;
v_Comment VARCHAR2(35);
BEGIN
/* Retrieve the number of seats in the room identified by ID 99999.
Store the result in v_NumberSeats. */
SELECT number_seats
INTO v_NumberSeats
FROM rooms
WHERE room_id = 99999;
IF v_NumberSeats < 50 THEN
v_Comment := 'Fairly small';
ELSIF v_NumberSeats < 100 THEN
v_Comment := 'A little bigger';
ELSE
v_Comment := 'Lots of room';
END IF;
END;
3> 循環語句:
(1)簡單循環語句:
語法:
LOOP
{ statement statement_block } ;
[EXIT [WHEN condition] ;]
END LOOP ;
其中,語句EXIT [WHEN condition];等價於
IF condition THEN
EXIT ;
END IF ;
示例1:
v_Counter BINARY_INTEGER := 1;
BEGIN
LOOP
-- Insert a row into temp_table with the current value of the
-- loop counter.