1. 程式人生 > >關於SQL語句的佔位符使用及動態SQL

關於SQL語句的佔位符使用及動態SQL

在PLSQL中使用EXECUTE IMMEDIATE語句處理動態SQL語句。

語法如下:
EXECUTE IMMEDIATE dynamic_string
[INTO {define_variable[, define_variable]... | record}]
[USING [IN | OUT | IN OUT] bind_argument
    [, [IN | OUT | IN OUT] bind_argument]...]
[{RETURNING | RETURN} INTO bind_argument[, bind_argument]...];

dynamic_string是代表一條SQL語句或一個PL/SQL塊的字串表示式,
define_variable是用於存放被選出的欄位值的變數,
record是使用者定義或%ROWTYPE型別的記錄,用來存放被選出的行記錄。
輸入bind_argument引數是一個表示式,它的值將被傳入(IN模式)或傳出(OUT模式)或先傳入再傳出(IN OUT模式)到動態SQL語句或是PL/SQL塊中。一個輸出bind_argument引數就是一個能儲存動態SQL返回值的變數。

除了多行查詢外,動態字串可以包含任何SQL語句(不含終結符)或PL/SQL塊(含終結符)。
字串中可以包括用於引數繫結的佔位符。
但是,不可以使用繫結引數為動態SQL傳遞模式物件。

在用於單行查詢時,INTO子句要指明用於存放檢索值的變數或記錄。
對於查詢檢索出來的每一個值,INTO子句中都必須有一個與之對應的、型別相容的變數或欄位。
在用於DML操作時,RETURNING INTO子句要指明用於存放返回值的變數或記錄。
對於DML語句返回的每一個值,INTO子句中都必須有一個與之對應的、型別相容的變數或欄位。

我們可以把所有的繫結引數放到USING子句中。預設的引數模式是IN。
對於含有RETURNING子句的DML語句來說,我們可以把OUT引數放到RETURNING INTO之後,並且不用指定它們的引數模式,因為預設就是OUT。
如果我們既使用了USING又使用RETURNING INTO,那麼,USING子句中就只能包含IN模式的引數了。

執行時,動態字串中的繫結引數會替換相對應的佔位符。所以,每個佔位符必須與USING子句和/或RETURNING INTO子句中的一個繫結引數對應。我們可以使用數字、字元和字串作為繫結引數,但不能使用布林型別(TRUE,FALSE和NULL)。要把空值傳遞給動態字串,我們就必須使用工作區。

動態SQL支援所有的SQL型別。所以,定義變數和繫結變數都可以是集合、LOB,物件型別例項和引用。
作為一項規則,動態SQL是不支援PL/SQL特有的型別的。這樣,它就不能使用布林型或索引表。

我們可以重複為繫結變數指定新值執行動態SQL語句。但是,每次都會消耗很多資源,因為EXECUTE IMMEDIATE在每次執行之前都需要對動態字串進行預處理。

1、動態SQL例項
下面的PL/SQL塊包含了幾個動態SQL的例子:

  1. DECLARE
  2.   sql_stmt      VARCHAR2(200);  
  3.   plsql_block   VARCHAR2(500);  
  4.   emp_id        NUMBER(4)     := 7566;  
  5.   salary        NUMBER(7, 2);  
  6.   dept_id       NUMBER(2)     := 50;  
  7.   dept_name     VARCHAR2(14)  := 'PERSONNEL';  
  8.   location      VARCHAR2(13)  := 'DALLAS';  
  9.   emp_rec       emp%ROWTYPE;  
  10. BEGIN
  11.   EXECUTE IMMEDIATE 'CREATE TABLE bonus (id NUMBER, amt NUMBER)';  
  12.   sql_stmt := 'INSERT INTO dept VALUES (:1, :2, :3)';  
  13.   EXECUTE IMMEDIATE sql_stmt  
  14.               USING dept_id, dept_name, location;  sql_stmt := 'SELECT * FROM emp WHERE empno = :id';  
  15.   EXECUTE IMMEDIATE sql_stmt  
  16.               INTO emp_rec  
  17.               USING emp_id;  plsql_block := 'BEGIN emp_pkg.raise_salary(:id, :amt); END;';  
  18.   EXECUTE IMMEDIATE plsql_block  
  19.               USING 7788, 500;  sql_stmt := 'UPDATE emp SET sal = 2000 WHERE empno = :1 RETURNING sal INTO :2';  
  20.   EXECUTE IMMEDIATE sql_stmt  
  21.               USING emp_id  
  22.               RETURNING INTO salary;  EXECUTE IMMEDIATE 'DELETE FROM dept WHERE deptno = :num'
  23.               USING dept_id;  EXECUTE IMMEDIATE 'ALTER SESSION SET SQL_TRACE TRUE';  
  24. END;  

下例中,過程接受一個數據表名(如"emp")和一個可選的WHERE子句(如"sal > 2000")。
如果我們沒有提供WHERE條件,程式會刪除指定表中所有的行,否則就會按照給定的條件刪除行:

  1. CREATEORREPLACEPROCEDURE delete_rows(  
  2.   table_name   IN   VARCHAR2,  
  3.   condition    IN   VARCHAR2 DEFAULTNULL
  4. AS
  5.   where_clause   VARCHAR2(100) := ' WHERE ' || condition;  
  6. BEGIN
  7.   IF condition ISNULLTHEN
  8.     where_clause    := NULL;  
  9.   END IF;  
  10.   EXECUTE IMMEDIATE 'DELETE FROM ' || table_name || where_clause;  
  11. END;  


2、USING子句的向後相容

當動態INSERT、UPDATE或DELETE語句有一個RETURNING子句時,
輸出繫結引數可以放到RETURNING INTO或USING子句的後面。
在新的應用程式中要使用RETURNING INTO,而舊的應用程式可以繼續使用USING,如下例:

  1. DECLARE
  2.   sql_stmt   VARCHAR2(200);  
  3.   my_empno   NUMBER(4)     := 7902;  
  4.   my_ename   VARCHAR2(10);  
  5.   my_job     VARCHAR2(9);  
  6.   my_sal     NUMBER(7, 2)  := 3250.00;  
  7. BEGIN
  8.   sql_stmt := 'UPDATE emp SET sal = :1 WHERE empno = :2 RETURNING ename, job INTO :3, :4';  
  9.   /* Bind returned values through USING clause. */  
  10.   EXECUTE IMMEDIATE sql_stmt  
  11.               USING my_sal, my_empno, OUT my_ename, OUT my_job;  
  12.   dbms_output.put_line(my_ename || ',' || my_job);  
  13.   /* Bind returned values through RETURNING INTO clause. */  
  14.   EXECUTE IMMEDIATE sql_stmt  
  15.               USING my_sal, my_empno  
  16.               RETURNING INTO my_ename, my_job;  
  17.   dbms_output.put_line(my_ename || ',' || my_job);  
  18. END;  


3、指定引數模式

使用USING子句時,我們不需要為輸入引數指定模式,因為預設的就是IN;
而RETURNING INTO子句中我們是不可以指定輸出引數的模式的,因為定義中它就是OUT模式。
在適當的時候,我們必須為繫結引數指定OUT或IN OUT模式。例如,假定我們想呼叫下面的過程:

  1. CREATEPROCEDURE create_dept(  
  2.   deptno   INOUT   NUMBER,  
  3.   dname    IN       VARCHAR2,  
  4.   loc      IN       VARCHAR2  
  5. AS
  6. BEGIN
  7.   SELECT deptno_seq.NEXTVAL INTO deptno FROM DUAL;  
  8.   INSERTINTO dept VALUES (deptno, dname, loc);  
  9. END;  

要從動態PL/SQL塊呼叫過程,就必須為與形參關聯的繫結引數指定IN OUT模式,如下:

  1. DECLARE
  2.   plsql_block   VARCHAR2(500);  
  3.   new_deptno    NUMBER(2);  
  4.   new_dname     VARCHAR2(14)  := 'ADVERTISING';  
  5.   new_loc       VARCHAR2(13)  := 'NEW YORK';  
  6. BEGIN
  7.   plsql_block := 'BEGIN create_dept(:a, :b, :c); END;';  
  8.   EXECUTE IMMEDIATE plsql_block  
  9.               USING INOUT new_deptno, new_dname, new_loc;  
  10.   dbms_output.put_line(new_deptno);  
  11. END;  


4、開啟遊標變數OPEN-FOR
OPEN {cursor_variable | :host_cursor_variable} FOR dynamic_string
[USING bind_argument[, bind_argument]...];
OPEN-FOR的動態形式有一個可選的USING子句。
在執行時,USING子句中的繫結變數可以替換動態SELECT語句中相對應的佔位符

5、使用批量動態SQL

批量繫結能減少PL/SQL和SQL引擎之間的切換,改善效能。
使用下面的命令、子句和遊標屬性,我們就能構建批量繫結的SQL語句,然後在執行時動態地執行:

BULK FETCH 語句
BULK EXECUTE IMMEDIATE 語句
FORALL 語句
COLLECT INTO 子句
RETURNING INTO 子句
%BULK_ROWCOUNT 遊標屬性


動態批量繫結語法

批量繫結能讓Oracle把SQL語句中的一個變數與一個集合相繫結。
集合型別可以是任何PL/SQL集合型別(索引表、巢狀表或變長陣列)。
但是,集合元素必須是SQL資料型別,如CHAR、DATE或NUMBER。
有三種語句支援動態批量繫結:EXECUTE IMMEDIATE、FETCH和FOR ALL。

批量EXECUTE IMMEDIATE這個語句能讓我們把變數或OUT繫結引數批量繫結到一個動態的SQL語句,語法如下:
EXECUTE IMMEDIATE dynamic_string
  [[BULK COLLECT] INTO define_variable[, define_variable ...]]
  [USING bind_argument[, bind_argument ...]]
  [{RETURNING | RETURN}
  BULK COLLECT INTO bind_argument[, bind_argument ...]];

在動態多行查詢中,我們可以使用BULK COLLECT INTO子句來繫結變數。
在返回多行結果的動態INSERT、UPDATE或DELETE語句中,我們可以使用RETURNING BULK COLLECT INTO子句來批量繫結輸出變數。

批量FETCH這個語句能讓我們從動態遊標中取得資料,就跟從靜態遊標中取得的方法是一樣的。語法如下:
FETCH dynamic_cursor
  BULK COLLECT INTO define_variable[, define_variable ...];
*如果在BULK COLLECT INTO中的變數個數超過查詢的欄位個數,Oracle就會產生錯誤。

批量FORALL這個語句能讓我們在動態SQL語句中批量繫結輸入引數。
此外,我們還可以在FORALL內部使用EXECUTE IMMEDIATE語句。語法如下:
FORALL index IN lower bound..upper bound
  EXECUTE IMMEDIATE dynamic_string
  USING bind_argument | bind_argument(index)
    [, bind_argument | bind_argument(index)] ...
  [{RETURNING | RETURN} BULK COLLECT
    INTO bind_argument[, bind_argument ... ]];

*動態字串必須是一個INSERT、UPDATE或DELETE語句(不可以是SELECT語句)。


例如

  1. DECLARE
  2.   TYPE numlist ISTABLEOF NUMBER;  
  3.   TYPE namelist ISTABLEOF VARCHAR2(15);  
  4.   empcur   sys_refcursor;  
  5.   empnos   numlist;  
  6.   enames   namelist;  
  7.   sals     numlist;  
  8. BEGIN
  9.   OPEN empcur FOR'SELECT empno, ename FROM emp';  
  10.   FETCH empcur  
  11.     BULK COLLECT INTO empnos, enames;  
  12.   CLOSE empcur;  
  13.   EXECUTE IMMEDIATE 'SELECT sal FROM emp'
  14.     BULK COLLECT INTO sals;  
  15. END;  



只有INSERT、UPDATE和DELETE語句才能擁有輸出繫結引數。
我們可以在EXECUTE IMMDIATE的BULK RETURNING INTO子句中進行繫結:

  1. DECLARE
  2.   TYPE namelist ISTABLEOF VARCHAR2(15);  
  3.   enames      namelist;  
  4.   sal_amt   NUMBER       := 500;  
  5.   sql_stmt    VARCHAR(200);  
  6. BEGIN
  7.   sql_stmt    := 'UPDATE emp SET sal = :1 RETURNING ename INTO :2';  
  8.   EXECUTE IMMEDIATE sql_stmt  
  9.               USING sal_amt  
  10.               RETURNING BULK COLLECT INTO enames;  
  11. END;  


使用FORALL語句和USING子句,但這時的SQL語句不能是查詢語句,如下例:

  1. DECLARE
  2.   TYPE numlist ISTABLEOF NUMBER;  
  3.   TYPE namelist ISTABLEOF VARCHAR2(15);  
  4.   empnos   numlist;  
  5.   enames   namelist;  
  6. BEGIN
  7.   empnos := numlist(7369, 7499, 7521);  
  8.   FORALL i IN 1 .. 3  
  9.     EXECUTE IMMEDIATE 'UPDATE emp SET sal = sal*2 WHERE empno = :1 RETURNING ename INTO :2'
  10.                 USING empnos(i)  
  11.                 RETURNING BULK COLLECT INTO enames;  
  12. END;  


6、動態SQL的技巧與陷阱

1)使用繫結變數來改善效能
我們可以使用繫結變數來改善效能,如下例所示,讓Oracle為不同的emp_id值重用同一個遊標。

  1. CREATEPROCEDURE fire_employee(emp_id NUMBER) AS
  2. BEGIN
  3.   EXECUTE IMMEDIATE 'DELETE FROM emp WHERE empno = :num'
  4.               USING emp_id;  
  5. END;  


2)讓過程對任意模式物件起作用

假設我們需要一個過程,讓它接受資料表名,然後將指定的表從資料庫中刪除。
我們可能會下面這樣編寫使用動態SQL的過程:

  1. CREATEPROCEDURE drop_table(table_name IN VARCHAR2) AS
  2. BEGIN
  3.   EXECUTE IMMEDIATE 'DROP TABLE :tab'
  4.               USING table_name;  
  5. END;  


但是,在執行的時候,這個過程會報一個表名無效的錯誤。
原因是不可以使用繫結引數為動態SQL傳遞模式物件。
解決方法是直接把引數巢狀到字串中。把上面的EXECUTE IMMEDIATE語句修改一下:

  1. CREATEORREPLACEPROCEDURE drop_table(table_name IN VARCHAR2) AS
  2. BEGIN
  3.   EXECUTE IMMEDIATE 'DROP TABLE ' || table_name;  
  4. END;  

這樣,我們就可以向動態SQL語句傳遞任意資料表名稱了。


3)使用重複佔位符

動態SQL語句中的佔位符與USING子句中的繫結引數是位置關聯的,而不是名稱關聯。
所以,如果在SQL語句中同樣的佔位符出現兩次或多次,
那麼,它的每次出現都必須與一個USING子句中的繫結引數相關聯。
例如下面的動態字串:
sql_stmt := 'INSERT INTO payroll VALUES (:x, :x, :y, :x)';
我們可以為動態字串編寫對應的USING子句:
EXECUTE IMMEDIATE sql_stmt USING a, a, b, a;

但是,
動態PL/SQL塊中只有唯一的佔位符才與USING子句中的繫結引數按位置對應。
所以,如果一個佔位符在PL/SQL塊中出現兩次或多次,
那麼所有這樣相同的佔位符都只與USING語句中的一個繫結引數相對應。
比如下面的例子,第一個佔位符(x)與第一個繫結引數(a)關聯,第二個佔位符(y)與第二個繫結引數(b)關聯。

  1. DECLARE
  2.   a   NUMBER := 4;  
  3.   b   NUMBER := 7;  
  4. BEGIN
  5.   plsql_block := 'BEGIN calc_stats(:x, :x, :y, :x); END';  
  6.   EXECUTE IMMEDIATE plsql_block  
  7.               USING a, b;  
  8. END;  


4)使用遊標屬性

每個顯式的遊標都有四個屬性:%FOUND、%ISOPEN、%NOTFOUND和%ROWCOUNT。它們都能返回與靜態或動態SQL語句執行結果相關的有用資訊。

為處理SQL資料操作語句,Oracle會開啟一個名為SQL的隱式遊標。它的屬性會返回最近一次執行的INSERT、UPDATE、DELETE或單行SELECT的相關資訊。例如,下面函式就使用%ROWCOUNT返回從資料表中刪除的行數:

  1. CREATEFUNCTION rows_deleted(table_name IN VARCHAR2, condition IN VARCHAR2)  
  2.   RETURNINTEGERAS
  3. BEGIN
  4.   EXECUTE IMMEDIATE 'DELETE FROM ' || table_name || ' WHERE ' || condition;  
  5.   RETURN SQL%ROWCOUNT;   -- return number of rows deleted
  6. END;  


5)傳遞空值
下面,我們來為動態SQL傳遞空值,見下面的EXECUTE IMMEDIATE語句:
EXECUTE IMMEDIATE 'UPDATE emp SET comm = :x' USING NULL;

但是,這個語句會因為在USING子句中使用NULL而執行失敗,
因為USING語句中所傳遞的引數是不能為空的。
所以,要想解決這個問題,直接使用字串就可以了:

  1. DECLARE
  2.   a_null   CHAR(1);   -- set to NULL automatically at run time
  3. BEGIN
  4.   EXECUTE IMMEDIATE 'UPDATE emp SET comm = :x'
  5.               USING a_null;  
  6. END;  


6)  遠端操作

如下例所示,PL/SQL子程式能夠執行引用遠端資料庫物件的動態SQL語句:

  1. PROCEDURE delete_dept(db_link VARCHAR2, dept_id INTEGERIS
  2. BEGIN
  3.   EXECUTE IMMEDIATE 'DELETE FROM [email protected]' || db_link || ' WHERE deptno = :num'
  4.               USING dept_id;  
  5. END;  


同樣,遠端過程呼叫(RPC)的目標也包括動態SQL語句。
例如,假設下面返回資料表中記錄個數的函式存放在Chicago的資料庫上:

  1. CREATEFUNCTION row_count(tab_name VARCHAR2)  
  2.   RETURNINTEGERAS
  3.   ROWSINTEGER;  
  4. BEGIN
  5.   EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || tab_name  
  6.                INTOROWS;  
  7. 相關推薦

    關於SQL語句佔位使用動態SQL

    在PLSQL中使用EXECUTE IMMEDIATE語句處理動態SQL語句。 語法如下: EXECUTE IMMEDIATE dynamic_string [INTO {define_variable[, define_variable]... | record}] [USING [IN | OUT |

    sql語句串包含

    數據 acl sele ins true sql 數據庫 oracl from rom select instr(‘1222‘,‘122‘) from dual//前者包含後者>0 oracle mysql 數據庫可中 select charindex(‘1‘

    Access sql語句建立表欄位型別

    建立一張空表: Sql="Create TABLE [表名]" 建立一張有欄位的表: Sql="Create TABLE [表名]([欄位名1] MEMO NOT NULL, [欄位名2] MEMO, [欄位名3] COUNTER NOT NULL, [欄位名4] DA

    mybaits(查詢與別名、日誌框架顯示sql語句、物件屬性和資料庫表字段不匹配resultMap使用、mysql資料查詢分頁、執行sql和儲存過程、動態SQL語句

    主要是各種配置檔案,建議把整個專案搬到自己電腦上慢慢看。 建立maven專案 首先是各種配置檔案: pom.xml: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://m

    mysql sql語句調優,索引總結

    Mysql的索引 1.btree索引,btree索引是二叉平衡樹的結構表有(myisam innodb), 2.Hash索引,通過hash演算法計算到的索引是隨機的沒有規律(memory),沒有回杭 一、Btree索引 索引同時只能用上一個 查詢一條sql的執行

    提高sql語句執行效率索引

    (1)選擇最有效率的表名順序(只在基於規則的優化器中有效):ORACLE的解析器按照從右到左的順序處理FROM子句中的表名,FROM子句中寫在最後的表(基礎表 driving table)將被最先處理,在FROM子句中包含多個表的情況下,你必須選擇記錄條數最少的表作為基礎表。如果有3個以上的表連線查詢,那就

    prepareStatement進行增刪改查---填充佔位(防止sql注入)

    首先建立表 然後構造一個實體類–封裝資料庫欄位 Student package com.godinsec; public class Student { private int id; private String name;

    SQL語句建立資料庫表和約束等

    建立資料庫: --drop database Mydatabase create database Mydatabase on primary ( --括號一定是圓括號 name='Mydatabase_data',--資料庫名稱 filename='d:

    Mac下Mysql匯出sql語句的方法可能遇到的mysqldump: command not found

    最近切換到了Mac os的開發平臺,於是乎自己又琢磨了一套方法 度娘了一下,尚無針對Mac的SQL語句匯出方法的介紹,在這裡與大家分享。 步驟一: 開啟Terminal,輸入mysqldum

    spring+ibatis【基本配置動態sql

    1、準備jar:commons-dbcp.jar、commons-logging.jar、commons-pool.jar、ibatis-common-2.jar、ibatis-sqlmap-2.jar、javax.servlet-5.1.12.jar、log4j-1.2.

    關於使用佔位來解決SQL注入

    總結: SQL已經預編譯好了,然後替換中間的佔位符,這個佔位符在編譯後就已經確定了它只是一個引數屬性。因此,用注入的程式碼去替換佔位符,這個SQL也不會再進行編譯了,所以也達不到注入的目的。 SQL注入並不是一個在SQL內不可解決的問題,這種攻擊方式的存在也不能完全歸

    Spring-boot mybatis 控制檯列印執行的SQL語句 SpringBoot中Mybatis列印sql

    【轉載】SpringBoot中Mybatis列印sql 1. 如果使用的是application.properties檔案,加入如下配置: 1 logging.level.com.example.demo.dao=debug   紅色部分指的是mybatis對應的方法介面所在的包路徑。 2.

    SQL SERVER-10-觸發器|遊標|動態sql

    1.觸發器 --可以理解為一個特殊的儲存過程,在某一個動作執行的時候,自動執行,而不需要人手動執行 --觸發器的分類 --1.DML觸發器 --insert,delete,update(不支援select) --after觸發器,instead of 觸發器(相當於其他資

    SQL Server建立儲存過程——動態SQL

    儲存過程(stored procedure)是一組為了完成特定功能的SQL語句集合,經編譯後儲存在伺服器端的資料庫中,利用儲存過程可以加速SQL語句的執行。 自定義儲存過程,由使用者建立並能完成某一特定功能的儲存過程,儲存過程既可以有引數又有返回值,但是它與函式不同,儲存過程的返回值只是指明執行是否成功,

    在SQLPLUS中oracle查看錶結構SQL語句 oracle修改表字段SQL語句 修改表字段SQL語句

    1、查看錶結構 desc 表名 2、修改表字段SQL語句 使用rename關鍵字來實現欄位名的修改: alter table 表名 rename column舊的欄位名 to 新的欄位名名; 使用modify關鍵字來實現對資料型別的修改: alter table 表名 mo

    通過分析SQL語句的執行計劃優化SQL(總結)

    如何幹預執行計劃 - - 使用hints提示 基於代價的優化器是很聰明的,在絕大多數情況下它會選擇正確的優化器,減輕了DBA的負擔。但有時它也聰明反被聰明誤,選擇了很差的執行計劃,使某個語句的執行變得奇慢無比。此時就需要DBA進行人為的干預,告訴優化器使用我們指定的存取路徑或連線型別生成執行計劃,從而使

    SSM框架day02-MyBatis——036 動態SQL-where、037 動態SQL-choose、038 動態SQL-foreach陣列、039 動態SQL-foreachList

    一、動態SQL-where(1)對映檔案(2)定義介面方法 (3)測試二、動態SQL-choose(1)對映檔案(2)介面實現方法(3)測試三、動態SQL-foreach陣列(1)對映檔案(2)定義介面 方法(3)測試四、動態SQL-foreachList(1)List集合中

    python 操作資料庫插入語句佔位問題

    1,在 Python 中使用 sqlite3 連線資料庫,插入語句的展位符為 "?" cur.execute("insert into user values(?,?,?)",(1,2,"zhang"))2、在 Python 中,使用 pymysql 連線 mysql 資

    檢視sql語句執行時間或測試sql語句效能

    寫程式的人,往往需要分析所寫的SQL語句是否已經優化過了,伺服器的響應時間有多快,這個時候就需要用到SQL的STATISTICS狀態值來查看了。     通過設定STATISTICS我們可以檢視執行SQL時的系統情況。選項有PROFILE,IO ,TIME。介紹如下:

    使用Freemarker解析佔位,構造可執行的SQL語句

    背景 最近遇到一個需求, 框架需要執行使用者給定的SQL語句,該SQL語句內包含佔位符, 佔位符表示的內容存在於在框架中,比如下面的sql select * from xxx where id =