PL-SQL

PL/SQL（Procedural Language/SQL）是甲骨文公司專有的SQL擴展語言，應用在甲骨文公司的Oracle數據庫系統。一些的SQL數據庫管理系統也提供了類似的擴展SQL語言。PL/SQL的語法非常類似於Ada，而且像1980年代的Ada編譯器一樣，PL/SQL的運作系統使用Diana作為中介語言。
重要的是PL/SQL緊密的結合在Oracle數據庫裡面。
PL/SQL是Oracle數據庫使用的三種語言的其中之一，另外兩個是SQL和Java。

**歷史**

**特性**

**SQL連接操作**
Oracle連接操作(left join ,right join,full join)的語法確與SQL標準完全不同，沒有左連接與右連接的概念，也不支持全外連接。Oracle語法如下所示：
select * from t1,t2 where t1.id=t2.id(+)
採用(+)來表示外連接，在Oracle中它相當於左連接。Oracle9i中增加了標準外連接的語法支持，但使用不廣。

**物化視圖**
物化視圖（materialized view）或譯為實體化視圖。與普通視圖關係不同，物化視圖更像是一個表，保存了實實在在的數據，並且可以與表一樣定義存儲參數，可以與表一樣使用select,insert,update,delete。在其它數據庫中也有和物化視圖相似的解決方案，DB2叫物化查詢表(materialized query table)，sqlserver有索引視圖，但是索引視圖僅是起優化作用，與oracle的物化視圖還不太一樣。

**數據類型**

**數值類型**
採用本地的number類型做指數或對數運算，與標準的浮點數性能可能會相差50倍。好在Oracle10g中增加了高效的浮點類型binary_float,binary_double，從而彌補了浮點數性能的問題。
variable_name number(P, S) = 0;- NUMBER可選指定precision (P)與scale (S)。精度表示十進制有效數字的個數，最多不能超過38個有效數字（實際支持39-40位十進制數字）NoteTag|p是精度，即十進制數字的有效位數，其中最重要有效數字是在最左邊的非零數字，最不重要有效數字是最右邊的數字。Oracle保證數字可移植性，其精度可達基於100進制的20個數字（centesimal digit），這等效於依賴小數點位置的39位或40位十進制數字。原文：p is the precision, or the total number of significant decimal digits, where the most significant digit is the left-most nonzero digit, and the least significant digit is the right-most known digit. Oracle guarantees the portability of numbers with precision of up to 20 base-100 digits, which is equivalent to 39 or 40 decimal digits depending on the position of the decimal point.。Scale的範圍為-84，127。Scale為正數時，表示從小數點到最不重要的十進制有效數字的個數；為負數時，其絕對值表示從最不重要的十進制有效數字到小數點的位數。如果沒有指定精度，precision與scale默認為最大的取值區間。如果指定了精度，沒有指定scale，scale默認為0。內部存儲格式是變長阿拉伯數字的字節數組:
**首字節為長度值，最大22；如果為NULL，則該字節值為255(0xFF)
**第二字節是符號和指數字節(sign bit/exponent)，其最高比特為符號位，1表示正數，0表示負數；其餘7比特構成基為100的指數值，取值範圍-65，62，NUMBER數據類型的取值範圍為10-130,10126)；
***第二字節值大於128，則：指數值=字節值 - 128 - 64= 字節值-192，即去除符號比特後偏移了64。字節值最大為254
***第二字節值等於128，則NUMBER數據類型表示值0
***第二字節值小於128，則：指數值=(255-字節值)-128-64=63-ZV，即取反後去除符號比特再偏移64
**其餘字節保存了基數為100的數值00-99
***對於正數：實際值＝存儲值－1
***對於負數：實際值＝存儲值-101；字節值102 (0x66)標誌字節數組的結束。
***兩個字節255與101表示正無窮
***單字節0表示負無窮- INTEGER是NUMBER的子類型，它等同於NUMBER（38,0），用來存儲整數。若插入、更新的數值有小數，則會被四捨五入。- FLOAT類型也是NUMBER的子類型。Float(n),數 n 指示位的精度，可以存儲的值的數目。n 值的範圍可以從 1 到 126。若要從二進制轉換為十進制的精度，請將 n 乘以 0.30103。要從十進制轉換為二進制的精度，請用 3.32193 乘小數精度。126 位二進制精度的最大值是大約相當於 38 位小數精度。- BINARY_FLOAT 是 32 位、 單精度浮點數字數據類型。可以支持至少6位精度,每個 BINARY_FLOAT 的值需要 5 個字節，包括長度字節。- BINARY_DOUBLE 是為 64 位，雙精度浮點數字數據類型。每個 BINARY_DOUBLE 的值需要 9 個字節，包括長度字節。

其它數值類型：
dec, decimal, double precision, int, numeric, real, smallint, binary_integer.

**字符類型**
字符串數據類型依據存儲空間分為兩種：
*固定長度類型：CHAR/NCHAR，自動補足空格，最多可以存儲2,000字節
*可變長度類型：VARCHAR2/NVARCHAR2，最大字節數都是4000，自動刪除首尾的空格
串的開頭存儲了串的長度。如果串的長度小於或等於250（0x01~0xFA）， Oracle 會使用1 個字節來表示長度。對於所有長度超過250 的串，都會在一個標誌字節0xFE 後跟有兩個字節來表示長度。
chr(0)表示的不可見字符，即我們通常所說的\0- CHAR類型： CHAR(size  CHAR) 固定長度字符串； - NCHAR類型： 根據字符集而定的UNICODE格式固定長度字符串 最大長度2000 bytes。- VARCHAR類型: 不建議使用。雖然VARCHAR數據類型目前是VARCHAR2的同義詞，VARCHAR數據類型將被重新定義為一個單獨的數據類型用於可變長度的字符串相比，與VARCHAR2具有不同的比較語義- varchar2類型：變長字符串- nvarchar2()類型：包含UNICODE格式數據的變長字符串

--  字段translated_name是NCHAR類型，則需要如下書寫：
SELECT translated_description FROM product_descriptions 
WHERE translated_name = N'LCD Monitor 11/PM';  
variable_name varchar2(20) = 'Text';
-- e.g.: 
address varchar2(20) := 'lake view road';

**日期類型**
variable_name date = to_date('01-01-2005 14:20:23', 'DD-MM-YYYY hh24:mi:ss');- Date類型可以表示日期與時間。精度到秒。日期範圍可以是公元前4712年1月1日至公元9999年12月31日。佔用7個字節的存儲空間。第1字節：世紀+100；第2字節：年； 第3字節：月； 第4字節：天； 第5字節：小時+1； 第6字節：分+1；第7字節：秒+1。其中時間可以忽略。但無法只表示時間而忽略日期。 Oracle Datatypesdate=20111119065442 - TIMESTAMP類型：7字節或11字節的定寬日期/時間數據類型。可以包含小數秒，小數位數可以指定為0-9，默認為6位，所以最高精度可以到ns(納秒).如果精度為0，則用7字節存儲，與date類型功能相同，如果精度大於0則用11字節存儲。- TIMESTAMP WITH TIME ZONE類型：TIMESTAMP類型的變種，它包含了時區偏移量的值- TIMESTAMP WITH LOCAL TIME ZONE類型：- INTERVAL YEAR TO MOTH：- INTERVAL DAY TO SECOND：
函數TO_DATE把字符串轉換為日期值。
to_date('31-12-2004', 'dd-mm-yyyy')
to_date ('31-Dec-2004', 'dd-mon-yyyy', 'NLS_DATE_LANGUAGE = American')
函數TO_CHAR (date_string, format_string)把日期值轉換為字符串。
PL/SQL支持使用ANSI日期與時間間隔值 The following clause gives an 18-month range:
示例：
WHERE dateField BETWEEN DATE '2004-12-30' - INTERVAL '1-6' YEAR TO MONTH
AND DATE '2004-12-30'
create table T
(
C1 DATE,
C2 TIMESTAMP(9)
);

insert into t(c1,c2) values(date'2010-2-12',timestamp'2010-2-12 13:24:52.234123211');
insert into t(c1,c2) values(
to_date('2010-2-12 10:20:30','YYYY-MM-DD HH24:MI:SS'),
to_timestamp('2010-2-12 13:24:52.123456','YYYY-MM-DD HH24:MI:SS.FF6')
);

SQL> select c1,dump(c1) c1_d,c2,dump(c2) c2_d from t;- sysdate--返回當前系統日期和時間，精確到秒- systimestamp--返回當前系統日期和時間，精確到毫秒

日期型數據可以與數值加減得到新的日期，加減數值單位為天- sysdate+1--取明天的當前時間- sysdate-1/24--取當前時間的前一個小時

**LOB類型**
內置的LOB數據類型包括BLOB、CLOB、NCLOB、BFILE（外部存儲）的大型化和非結構化數據，如文本、圖像、視屏、空間數據存儲。- BLOB 數據類型：存儲非結構化的二進制數據大對象，它可以被認為是沒有字符集語義的比特流，一般是圖像、聲音、視頻等文件。BLOB對象最多存儲(4 gigabytes-1) * (database block size)的二進制數據。 - CLOB 數據類型：存儲單字節和多字節字符數據。支持固定寬度和可變寬度的字符集。CLOB對象可以存儲最多 (4 gigabytes-1) * (database block size) 大小的字符- NCLOB 數據類型：存儲UNICODE類型的字符數據，支持固定寬度和可變寬度的字符集，NCLOB對象可以存儲最多(4 gigabytes-1) * (database block size)大小的文本數據。- BFILE 數據類型：存儲在數據庫外的二進制文件，只讀，最大長度4G


**LONG類型，RAW類型，LONG RAW類型**
均為較老的數據類型，Oracle不建議使用。
*LONG類型存儲變長字符串，最多達2G的字節數據。存儲在LONG 類型中的文本要進行字符集轉換。支持LONG 列只是為了保證向後兼容性。LONG類型的限制如下：
**一個表中只有一列可以為LONG型。 
**LONG列不能定義為主鍵或唯一約束
**不能建立索引
**LONG數據不能指定正則表達式。
**函數或存儲過程不能接受LONG數據類型的參數。
**LONG列不能出現在WHERE子句或完整性約束（除了可能會出現NULL和NOT NULL約束）
*LONG RAW 類型，能存儲2GB 的原始二進制數據（不用進行字符集轉換的數據）
*RAW類型用於存儲二進制或字符類型數據，變長二進制數據類型，這說明採用這種數據類型存儲的數據不會發生字符集轉換。這種類型最多可以存儲2,000字節的信息

**ROWID & UROWID類型**
在數據庫中的每一行都有一個地址。然而，一些錶行的地址不是物理或永久的，或者不是ORACLE數據庫生成的。例如，索引組織錶行地址存儲在索引的葉子，可以移動。例如,外部表的ROWID（如通過網關訪問DB2表）不是標準的ORACLE的rowid。
ORACLE使用通用的ROWID（UROWIDs）的存儲地址的索引組織表和外表。10個字節，格式為： ********.****.****，*為0或1。NROWID類型為二進制數據表中記錄的唯一行號，最大長度4000字節
索引組織表有邏輯urowids的，和外表的外urowids。UROWID這兩種類型的存儲在ROWID（堆組織的表的物理行id）。
創建基於邏輯的rowid在表中的主鍵。邏輯的rowid不會改變，只要主鍵不改變。索引組織表的ROWID偽UROWID數據類型。你可以訪問這個偽列，你會堆組織表的ROWID偽（即使用一個SELECT ...ROWID語句）。如果你想存儲的rowid索引組織表，那麼你就可以定義一列的表型UROWID到列檢索值的ROWID偽。

**指定列的數據類型**
定義一個變量，其類型是指定表的指定列的數據類型：
Variable_name Table_name.Column_name%type;

**自定義類型**
程序員自定義類型：
type data_type is record (field_1 type_1 = xyz, field_2 type_2 := xyz, ..., field_n type_n = xyz);
例如：
declare
type t_address is record (
name address.name%type,
street address.street%type,
street_number address.street_number%type,
postcode address.postcode%type);
v_address t_address;
begin
select name, street, street_number, postcode into v_address from address where rownum = 1;
end;
可以使用點表示（dot-notation）獲取結構中的域：
v_address.street = 'High Street';"

**自增長數據類型**
Oracle的數據類型裡沒有自增長（auto-incremental）字段類型，Oracle的官方解決方案是採用sequence實現。insert的時候需要用sequence.nextval。需要增加一張專用表來保存自增長字段的表和字段名，每次新增記錄時都把這個記錄值加1再取出使用。

**基本程式**

**條件語句**
以下的代碼展示了IF-THEN-ELSIF結構。ELSIF和ELSE部分是可選的，從而可以創建更簡單的IF-THEN或者IF-THEN-ELSE結構。
IF x = 1 THEN
sequence_of_statements_1;
ELSIF x = 2 THEN
sequence_of_statements_2;
ELSIF x = 3 THEN
sequence_of_statements_3;
ELSIF x = 4 THEN
sequence_of_statements_4;
ELSIF x = 5 THEN
sequence_of_statements_5;
ELSE
sequence_of_statements_N;
END IF;
CASE語句簡化了一些大的IF-THEN-ELSE結構。
CASE
WHEN x = 1 THEN sequence_of_statements_1;
WHEN x = 2 THEN sequence_of_statements_2;
WHEN x = 3 THEN sequence_of_statements_3;
WHEN x = 4 THEN sequence_of_statements_4;
WHEN x = 5 THEN sequence_of_statements_5;
ELSE sequence_of_statements_N;
END CASE;
CASE語句可以使用預定義的選擇符：
CASE x
WHEN 1 THEN sequence_of_statements_1;
WHEN 2 THEN sequence_of_statements_2;
WHEN 3 THEN sequence_of_statements_3;
WHEN 4 THEN sequence_of_statements_4;
WHEN 5 THEN sequence_of_statements_5;
ELSE sequence_of_statements_N;
END CASE;

**陣列**
PL/SQL 將數組稱為“集合”（collection）。 該語言提供三種類型的集合：- 關聯數組（索引表）- 嵌套表- Varray（可變大小數組）
程序員必須為變量指定上限，但不需要為索引表或嵌套表指定上限。 該語言包含多種用於操作集合元素的集合方法：例如 FIRST、LAST、NEXT、PRIOR、EXTEND、TRIM、DELETE 等。索引表可用於模擬關聯數組。

**關聯數組（索引表）**
對於索引表，可以通過數字或字符串對數組進行索引。 它與Java映射類似，由鍵值對組成。 只有一維且無界。

**嵌套表**
對於嵌套表，程序員需要了解嵌套的內容。 在這裡，創建了一個可能由多個組件組成的新類型。 然後可以使用該類型在表中創建一列，並在該列中嵌套這些組件。
With nested tables the programmer needs to understand what is nested. Here, a new type is created that may be composed of a number of components. That type can then be used to make a column in a table, and nested within that column are those components.

**Varray（可變大小數組）**
對於 Varray，需要了解“可變大小數組”短語中的“變量”一詞並不像想象的那樣適用於數組的大小。 聲明數組的大小實際上是固定的。 數組中元素的數量是可變的，最多可達聲明的大小。 可以說，可變大小的數組的大小並不是那麼可變。

**迴圈**
從過程語言的，PL/SQL提供了多種迭代結構，包括基本的LOOP語句、WHILE 循環、FOR循環和Cursor FOR循環。 從 Oracle 7.3 開始，引入了REF CURSOR 類型，以允許從存儲過程和函數返回記錄集。 Oracle 9i引入了預定義的SYS_REFCURSOR類型，這意味著我們不再需要定義自己的 REF CURSOR 類型。
如果退出循環，則必須使用exit語句終止循環。exit語句分兩種格式：
exit：該格式的語句用於無條件強迫終止循環。
exit...when：該格式用於有條件終止循環，首先檢測when的條件是否滿足。
例1：
loop
if...then exit；
end if；
end loop；
例2：
loop
exit when；
end loop；

**LOOP語句**
>
LOOP
	statements
	>
	loop
		statements
		exit parent_loop when ; -- Terminates both loops
		exit when ; -- Returns control to parent_loop
	end loop child_loop;
if  then
continue; -- continue to next iteration
end if;
	exit when ;
END LOOP parent_loop;
Loop可用EXIT關鍵字或拋出異常來終止。

**FOR循環**
DECLARE
var NUMBER;
BEGIN
/* N.B. for loop variables in PL/SQL are new declarations, with scope only inside the loop */
FOR var IN 0 .. 10 LOOP
DBMS_OUTPUT.PUT_LINE(var);
END LOOP;
IF var IS NULL THEN
DBMS_OUTPUT.PUT_LINE('var is null');
ELSE
DBMS_OUTPUT.PUT_LINE('var is not null');
END IF;
END;
輸出:
0
1
2
3
4
5
6
7
8
9
10
var is null
for...loop循環可以限定循環的次數例如：
declare
div_name varchar2(20);
div_num integer:=1;
begin
for div_num in 1..9 loop
select name into div_name from div_tab where div_author='A000'
to_char(div_num);
end loop;
end;
例如：
declare
div_name varchar2(20);
div_num integer=1;
begin
for div_num in 1..9 loop
EXIT WHEN div_num >7;
DBMS_OUTPUT.put_line(div_num);
end loop;
end;

**Cursor FOR循環**
FOR RecordIndex IN (SELECT person_code FROM people_table)
LOOP
DBMS_OUTPUT.PUT_LINE(RecordIndex.person_code);
END LOOP;
Cursor-for循環自動打開一個cursor，讀入其數據並再次關閉遊標。
作為替代方案，PL/SQL 程序員可以提前預定義遊標的 SELECT 語句，以（例如）允許重用或使代碼更易於理解（在長查詢或複雜查詢的情況下特別有用）。 
DECLARE
CURSOR cursor_person IS
SELECT person_code FROM people_table;
BEGIN
FOR RecordIndex IN cursor_person
LOOP
DBMS_OUTPUT.PUT_LINE(recordIndex.person_code);
END LOOP;
END;
FOR 循環中 person_code 的概念用點符號（“.”）表示:
RecordIndex.person_code

**while循環=== 
while...loop;
while...loop循環在執行語句時，首先檢測條件的值。例如：
declare
div_name varchar2(20);
div_num integer=1;
begin
while div_num
create or replace procedure test() IS 
cusor_1 Cursor is select std_name from student where ...; --Cursor 的使用方式1 
cursor_2 Cursor; 
begin 
select class_name into cursor_2 from class where ...; --Cursor 的使用方式2 
-- 可使用For x in cursor LOOP .... end LOOP; 來實現對Cursor 的遍歷 
end test; 
-- SYS_REFCURSOR 型遊標，該遊標是Oracle 以預先定義的遊標，可作出參數進行傳遞 
create or replace procedure test1(rsCursor out SYS_REFCURSOR) IS 
cursor SYS_REFCURSOR; 
name varhcar(20); 
begin 
OPEN cursor FOR select name from student where ... --SYS_REFCURSOR 只能通過OPEN 方法來打開和賦值 
LOOP 
fetch cursor into name -- SYS_REFCURSOR 只能通過fetch into 來打開和遍歷 
exit when cursor%NOTFOUND; 
--SYS_REFCURSOR 中可使用三個狀態屬性： 
---%NOTFOUND( 未找到記錄信息) %FOUND( 找到記錄信息) 
---%ROWCOUNT( 然後當前遊標所指向的行位置) 
dbms_output.putline(name); 
end LOOP; 
rsCursor := cursor; 
end test1; 

**存儲過程**
查看存儲過程：
select text
from USER_SOURCE
where name='My_Store_Precedure_Name'
order by LINE;

**類似的語言**
功能近似PL/SQL的程序語言和其他關係型資料庫：
*Sybase ASE
*Microsoft SQL Server的Transact-SQL
*PostgreSQL資料庫的PL/pgSQL（模仿PL/SQL）
*IBM DB2的SQL PL
*符合ISO SQL的SQL/PSM標準。
PL/SQL 的工作方式類似於與其他關係數據庫關聯的嵌入式過程語言。 例如，Sybase ASE 和 Microsoft SQL Server 具有 Transact-SQL，PostgreSQL 具有 PL/pgSQL（在一定程度上模擬 PL/SQL），MariaDB 包括 PL/SQL 兼容性解析器，而 IBM Db2 包括 SQL 過程語言，符合ISO SQL的SQL/PSM標準。
PL/SQL的設計者將其語法建模為Ada的語法。 Ada 和 PL/SQL都以Pascal語言作為共同的祖先，因此 PL/SQL 在大多數方面也類似於 Pascal。 然而，PL/SQL 包的結構與 Borland Delphi 或Free Pascal 單元實現的基本 Object Pascal 程序結構並不相似。 程序員可以在 PL/SQL 包中定義公共和私有全局數據類型、常量和靜態變量。
PL/SQL 還允許定義類並將它們實例化為 PL/SQL 代碼中的對象。 這類似於面向對象編程語言（如 Object Pascal、C++ 和 Java）中的用法。 PL/SQL 將類稱為“抽象數據類型”(ADT) 或“用戶定義類型”(UDT)，並將其定義為 Oracle SQL 數據類型而不是 PL/SQL 用戶定義類型，從而允許 它在 Oracle SQL 引擎和 Oracle PL/SQL 引擎中的使用。 抽象數據類型的構造函數和方法是用 PL/SQL 編寫的。 生成的抽象數據類型可以作為 PL/SQL 中的對象類進行操作。 此類對象還可以作為 Oracle 數據庫表中的列值保留。
儘管表面上相似，但 PL/SQL 與 Transact-SQL 本質上是不同的。 將代碼從一種語言移植到另一種語言通常涉及不平凡的工作，這不僅是因為兩種語言的功能集存在差異，還因為 Oracle 和 SQL Server 處理併發的方式存在非常顯著的差異 和鎖定。

**註釋**

**參考文獻**

**外部連結**

**參見**
 Oracle數據庫- SQL- Transact-SQL- 關聯式資料庫管理系統