❶ sql,兩個表連接,列是動態的,如何能實現輸出列順序自定義
declare@sqlnvarchar(max);
declare@dynamic_sqlnvarchar(max);
set@dynamic_sql=(
select',a.['+c.[name]+'],b.['+c.[name]+'%]'
fromsysobjectsasowith(nolock)
innerjoinsyscolumnsascwith(nolock)onc.id=o.id
whereo.xtype=N'U'
andc.[name]notin(N'ID',N'NAME',N'DEP')
ando.id=object_id(N'T1')
orderbyc.colorderascforxmlpath('')
);
set@sql=N'selecta.[id],a.[name],a.[dep]'+isnull(@dynamic_sql,'')
+'from[dbo].[T1]asawith(nolock)innerjoin[dbo].[T2]asbwith(nolock)'
+'ona.id=b.idanda.[name]=b.[name]anda.dep=b.dep';
execsp_executesql@sql;
上面這個sql是用T-SQL寫的。使用的是動態sql,用到了資料庫的數據字典和xml。
使用前提:
表名分別為T1和T2,固定列為ID,NAME和DEP。
其它列在兩個表中的個數相同,只是列名不一樣。
❷ 存儲過程中如何執行帶輸出參數的動態SQL
SQL Server存儲過程中執行帶輸出參數的動態sql是很多人經常碰到的問題,比如根據一些條件查詢列表,並返回記錄數等。下面是一個參考示例,查詢用戶列表,它可以利用臨時表實現翻頁,並帶有死鎖和超時檢測功能。 CREATE PRocere pUserList ( @UserType char(2), @pagenum int, @perpagesize int, @pagetotal int out, @rowcount int out ) as set nocount on DECLARE @Err INT,@ErrCounter INT declare @sql nvarchar(2000) --聲明動態sql執行語句 declare @pagecount int --當前頁數 declare @sWhere nvarchar(200) declare @sOrder nvarchar(100) set @sWhere = ' where 1=1 ' if not(@UserType is null) set @sWhere = @sWhere + ' and UserType = ' + @UserType set @sOrder = ' order by UserID ' --取得當前資料庫的記錄總數 declare @row_num int LockTimeOutRetry: --創建臨時表,作為數據過濾 create table #change (T_id int) set @sql = 'select @row_num=count(*) from dbo.[User]' + @sWhere exec sp_executesql @sql,N'@row_num int output', @row_num output if @row_num % @perpagesize =0 set @pagetotal = @row_num/@perpagesize else set @pagetotal = @row_num/@perpagesize + 1 set @rowcount = @row_num if @row_num > @perpagesize begin set @row_num = @pagenum * @perpagesize if @row_num = @perpagesize begin set @sql = N'select top ' + cast(@perpagesize as varchar) + ' UserID,LoginName,RealName from dbo.[User]' + @sWhere + @sOrder exec sp_executesql @sql SET @Err = @@ERROR IF @Err <> 0 GOTO ErrorHandler return 0 end else begin set @row_num = (@pagenum-1) * @perpagesize set @pagecount = @row_num set @sql=N'insert #change (T_id) select top ' + cast(@pagecount as varchar) + ' UserID from dbo. [User] '+@sWhere+' and UserID not in (select T_id from #change)' + @sOrder exec sp_executesql @sql set @sql = N'select top ' + cast(@perpagesize as varchar) + ' UserID,LoginName,RealName from dbo.[User] '+@sWhere+' and UserID not in (select T_id from #change)' + @sOrder exec sp_executesql @sql SET @Err = @@ERROR IF @Err <> 0 GOTO ErrorHandler return 0 end end else begin set @sql = 'select UserID,LoginName,RealName from dbo.[User]' + @sWhere + @sOrder exec sp_executesql @sql SET @Err = @@ERROR IF @Err <> 0 GOTO ErrorHandler return 0 end ErrorHandler: IF (@Err = 1222 OR @Err = 1205) AND @ErrCounter = 5 BEGIN RAISERROR ('Unable to Lock Data after five attempts.', 16,1) return -100 END IF @Err = 1222 OR @Err = 1205 -- Lock Timeout / Deadlock BEGIN WAITFOR DELAY '00:00:00.25' SET @ErrCounter = @ErrCounter + 1 GOTO LockTimeOutRetry END -- else unknown error RAISERROR (@err, 16,1) WITH LOG return -100 GO SET QUOTED_IDENTIFIER OFF GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_NULLS ON GO
❸ MYSQL動態構造SQL語句時可以不讓它輸出嗎
將下面的SQL語句:
SELECT @COL_KEY:=CONCAT(@COL_KEY,'MAX(IF(RANK=\'',RANK,'\'',',RECORD_TIME,\'\')) AS record',RANK,',') FROM (SELECT DISTINCT RANK FROM dw_attendance_TMP) T;
改成:
SELECT CONCAT(@COL_KEY,'MAX(IF(RANK=\'',RANK,'\'',',RECORD_TIME,\'\')) AS record',RANK,',') into @COL_KEY FROM (SELECT DISTINCT RANK FROM dw_attendance_TMP) T;
即可!
這個屬於MySql的基礎知識!MySQL資料庫基礎與實例教程有具體描述!
❹ 如何在oracle存儲過程中執行動態sql語句
給你一個案例對這些,使用execute immediate就可以了,存儲過程和語句塊也是一樣的,自己改一改,沒區別的。
語法格式
EXECUTEIMMEDIATEdynamic_string
[INTO{define_variable[,define_variable]...|record}]
[USING[IN|OUT|INOUT]bind_argument[,[IN|OUT|INOUT]bind_argument]...]
[{RETURNING|RETURN}INTObind_argument[,bind_argument]...];
1,操作DDL語句,這也是動態SQL的常用操作之一
如下所示使用動態SQL創建資料庫表:
DECLARE
l_dync_sqlVARCHAR2(100);
BEGIN
l_dync_sql:='CREATETABLEcux_dync_test(idNUMBER,creation_dateDATE)';
EXECUTEIMMEDIATEl_dync_sql;
END;
2,操作DML語句,使用USING子句可以按照順序將輸入的值綁定到變數,如果動態SQL只有單行輸出的話可以直接使用INTO來接收輸出值,如下所示。
DECLARE
l_dync_sqlVARCHAR2(100);
l_person_nameVARCHAR2(140);
l_ageNUMBER;
BEGIN
l_dync_sql:='SELECTperson_name,ageFROMcux_cursor_testWHEREperson_id=:1';
EXECUTEIMMEDIATEl_dync_sql
INTOl_person_name,l_age--使用into語句接手動態SQL的輸出,如果輸出多行則出錯
USING101;--給綁定變數賦值
dbms_output.put_line('PersonName:'||l_person_name);
dbms_output.put_line('Age:'||l_age);
END;
❺ SQLSERVER里的動態sql語句最多2個輸出參數嗎
--買課人數:當日買課人數,本周買課人數,當月買課人數,當季買課人數
ALTER PROC [dbo].[Report_CustomerClassCourseOrder_BuyClass_SP]
@FreeData DATE ,
@FranchiseeID INT ,
@DayBuyClass INTEGER OUTPUT ,
@SumDayBuyClass INTEGER OUTPUT ,
@WeekBuyClass INTEGER OUTPUT ,
@SumWeekBuyClass INTEGER OUTPUT ,
@AprilBuyClass INTEGER OUTPUT ,
@SumAprilBuyClass INTEGER OUTPUT ,
@QuarterBuyClass INTEGER OUTPUT ,
@SumQuarterBuyClass INTEGER OUTPUT,
@YesterdayBuyClass INTEGER OUTPUT, ---昨天
@SumYesterdayBuyClass INTEGER OUTPUT, ---昨天
@LastweekBuyClass INTEGER OUTPUT,--Last week上周
@SumLastweekBuyClass INTEGER OUTPUT,--Last week上周
@LastmonthBuyClass INTEGER OUTPUT,--上月
@SumLastmonthBuyClass INTEGER OUTPUT,--上月
@LastquarterBuyClass INTEGER OUTPUT,--上季度
@SumLastquarterBuyClass INTEGER OUTPUT--上季度
AS
你看這有幾個了
❻ 靜態SQL和動態SQL的區別和測試實例
所謂SQL的動態和靜態,是指SQL語句在何時被編譯和執行,二者都是用在SQL嵌入式編程中的。
靜態SQL:在高級語言中,如果嵌入了SQL語句,而這個SQL語句的主體結構已經明確,例如在c的一段代碼中有一個待執行的SQL「select * from t1 where c1>5」,在編譯階段,就可以將這段SQL交給資料庫管理系統去分析,資料庫軟體可以對這段SQL進行語法解析,生成資料庫方面的可執行代碼,這樣的SQL稱為靜態SQL,即在編譯階段就可以確定資料庫要做什麼事情。
動態SQL:如果嵌入的SQL沒有明確給出,如在c中定義了一個字元數組類型的變數name:char name[32];,然後採用prepared Statement對象的execute方法去執行這個sql,該sql的值可能等於從文本框中讀取的一個SQL或者從鍵盤輸入的SQL,但具體是什麼,在編譯時無法確定,只有等到程序運行起來,在執行的過程中才能確定,這種SQL叫做動態SQL。例如每一種資料庫軟體都有能夠執行SQL語句的界面,那個界面接收的SQL就是動態SQL,因為資料庫廠商在做這個界面時,並不知道用戶會輸入哪些SQL,只有在該界面執行後,接收了用戶的實際輸入,才知道SQL是什麼。
注意:在SQL中如果某些參數沒有確定,如」select * from t1 where c1>? and c2#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
EXEC SQL INCLUDE SQLCA;
EXEC SQL INCLUDE SQLDA;
/*此函數屬於靜態SQL編程*/
int DBSelect_static(){
EXEC SQL BEGIN DECLARE SECTION;
char _typename[32];
short _length;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT typename,length
INTO :_typename,:_length
FROM syscat.columns
WHERE tabname='SYSCOLUMNS' and colname='DEFAULT';
printf("【typename:%s】【length:%d】\n",_typename,_length);
}
/*此函數屬於靜態SQL編程:指定函數參數作為SQL語句的變數*/
int DBSelect_static_param(char *tbl_str,char *col_str){
EXEC SQL BEGIN DECLARE SECTION;
char _typename1[32];
short _length1;
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT typename,length
INTO :_typename1,:_length1
FROM syscat.columns
WHERE tabname='tbl_str' and colname='col_str';
printf("【typename:%s】【length:%d】\n",_typename1,_length1);
}
/*此函數屬於動態SQL編程:SQL語句的結構是不確定的,需要根據用戶的輸入補全SQL語句*/
int DBUpdate_dynamic(){
EXEC SQL BEGIN DECLARE SECTION;
char _address1[32];
char _tablename[32];
char _tmp[32];
char buf[256];
EXEC SQL END DECLARE SECTION;
EXEC SQL SELECT count(address1) INTO :_tmp FROM cisaddressinfo WHERE customid='100000100000000000178';
if(0==_tmp) {printf("Not Found!\n");return -1;}
memset(buf,0x00,sizeof(buf));
sprintf(buf,"update ");
printf("Pls input : tablename ->");
scanf("%s",&_tablename);
strcat(buf,_tablename);
strcat(buf," set address1=? where customid='100000100000000000178'");
EXEC SQL PREPARE project FROM :buf;
if(sqlca.sqlcode) perror("PREPARE");
printf("Pls input : address1 ->");
scanf("%s",&_address1);
EXEC SQL EXECUTE project USING :_address1;
if(sqlca.sqlcode) perror("EXECUTE");
EXEC SQL COMMIT WORK;
if(sqlca.sqlcode) perror("COMMIT");
}
/*此函數屬於動態SQL編程:使用sqlda數據結構組裝復雜多變的動態SQL*/
int DBSelect_dynamic(){
EXEC SQL BEGIN DECLARE SECTION;
char hostVarStmt[256];
EXEC SQL END DECLARE SECTION;
// 聲明兩個 SQLDA 指針,minsqlda 將是一個最小的 SQLDA 結構,用於 PREPARE 語句,
// 此時結果集的欄位數量未知,所以只需一個最小的 SQLDA,即包含 HEADER 和一個 SQLVAR
struct sqlda * minsqlda = (struct sqlda*)malloc(SQLDASIZE(1));
struct sqlda * fulsqlda = NULL;
strcpy(hostVarStmt, "select WUID from workprocess where muid = '185001'");
// PREPARE 將填寫 minsqlda 的 header,sqldabc 為 SQLDA 總長度,sqln 為 SQLVAR 數量,即欄位數量
EXEC SQL PREPARE STMT INTO :*minsqlda FROM :hostVarStmt;
// 根據從 minsqlda 中獲取的長度,分配完整的 SQLDA 結構 fulsqlda,其中將包括合適數量的 SQLVAR 結構
//結構sqlda的成員變數sqld返回select查詢語句的欄位的數目,可以根據此變數分配內存
fulsqlda = (struct sqlda *)malloc(SQLDASIZE(minsqlda->sqld));
// 使用 DESCRIBE 語句,獲取結果集中每個欄位的描述信息,包括各欄位的類型 (sqltype) 和長度 (sqllen)
EXEC SQL DESCRIBE STMT INTO :*fulsqlda;
int i;
for(i=0;i<minsqlda->sqld;i++)
{
// 根據每個欄位的長度,分配內存,將地址存儲在對應 SQLVAR 的 sqldata 中
// fulsqlda->sqlvar[i].sqldata=malloc(fulsqlda->sqlvar[i].sqllen);
fulsqlda->sqlvar[i].sqldata=malloc(32);
fulsqlda->sqlvar[i].sqlind=malloc(sizeof(short));
}
// 聲明游標
EXEC SQL DECLARE c1 CURSOR FOR STMT;
EXEC SQL OPEN c1;
EXEC SQL WHENEVER not found goto no_more_data;
// 讀取記錄,記錄中每個欄位的內容將寫入 fulsqlda 中對應 SQLVAR 結構的 sqldata 指向的內存
// EXEC SQL FETCH c1 USING DESCRIPTOR :*fulsqlda;
// 循環讀取所有記錄
for (;;)
{
EXEC SQL FETCH c1 USING DESCRIPTOR :*fulsqlda;
for(i=0;i<minsqlda->sqld;i++){
printf("%d %s\n",fulsqlda->sqlvar[i].sqltype,fulsqlda->sqlvar[i].sqldata);
usleep(10000);
}
}
return 0;
no_more_data:
printf("\nEND of Data\n");
free(minsqlda);
free(fulsqlda);
EXEC SQL CLOSE c1;
return 0;
}
int main(){
/*連接資料庫*/
EXEC SQL CONNECT TO ezeelink USER ezeelink USING EA704075ezeelink;
DBSelect_static();
DBSelect_static_param("SYSCOLUMNS","DEFAULT");
DBUpdate_dynamic();
DBSelect_dynamic();
no_more_data:
;
return 0;
}
案例輸出結果如下:
【typename:VARCHAR】【length:254】
【typename:VARCHAR】【length:254】
Pls input : tablename ->cisaddressinfo
Pls input : address1 ->ShangHai
452 cis505
452 cis506
452 pub806
452 ips007
452 ips032
452 dps302
END of Data
注意:
如果使用動態SQL編程編寫select查詢語句並保存結果,需要使用sqlda數據結構的,同時使用SQL的特性和功能,如:PREPARE ,EXECUTE ,DESCRIBE , DECLARE CURSE C1 FOR … , OPEN CURSE , CLOSE CURSE ….等等
建議:
動態SQL適用於表名及查詢欄位名未知的情況。在已知查詢欄位名及表名的情況下,使用動態SQL(字元串拼接方式)會增加硬解析的開銷,在這種情況下,建議使用靜態SQL,這樣可以提高執行效率。在過程過程用拼湊的動態sql效率並不高,有時候還不如程序直接傳遞sql.靜態SQL是前置編譯綁定,動態SQL是後期執行時才編譯綁定
❼ 求動態SQL基本語法
1.什麼時候用+號?
為了組成一個字元串類型的語句,要用+號作連接符。
一般情況下,字元串與字元串變數連接時才用加號。
如
DECLARE @SQL NVARCHAR(1000),@tablename nvarchar(100),@SORTFIELD NVARCHAR(100)
SELECT @TABLENAME=N'MYTABLE',@SORTFIELD=N'USERCODE'
SET @SQL=N'SELECT * from '+@tablename+N' order by '+@FIELDSORT '
這樣的組成的SQL語句是:
SELECT * from MYTABLE order by USERCODE
2.什麼時候用引號:
常數字元串都要用引號,如上面例子的N'SELECT * FROM '
3.什麼時候用空格:
一般連接關鍵字的引號內側要加一空格,如:
N'SELECT * from '+@tablename
FROM 後面要加空格,否則連接後會成為:
SELECT * FROMMYTABLE
FROM 和MYTABLE連起來了,從而出錯。
4.其它要注意的:生成後字串中有引號的語句。
比如要生成這樣一個語句
SELECT * FROM MYTABLE WHERE USERCODE=''
這樣寫是不對的:
SET @SQL=N'SELECT * from '+@tablename+' WHERE USERCODE='' '
這樣才是對的:
SET @SQL=N'SELECT * from '+@tablename+' WHERE USERCODE=N'''' '
可以看出:
在字串中間的兩個引號變成了4個。
總結一下:
語句中間需要顯示出的引號,引號個數要加倍。
======這么麻煩,加點分啦!
❽ 怎樣SQL存儲過程中執行動態SQL語句
MSSQL為我們提供了兩種動態執行SQL語句的命令,分別是EXEC和sp_executesql;通常,sp_executesql則更具有優勢,它提供了輸入輸出介面,而EXEC沒有。還有一個最大的好處就是利用sp_executesql,能夠重用執行計劃,這就大大提供了執行性能,還可以編寫更安全的代碼。EXEC在某些情況下會更靈活。除非您有令人信服的理由使用EXEC,否側盡量使用sp_executesql.
1.EXEC的使用
EXEC命令有兩種用法,一種是執行一個存儲過程,另一種是執行一個動態的批處理。以下所講的都是第二種用法。
下面先使用EXEC演示一個例子,代碼1
代碼
DECLARE @TableName VARCHAR(50),@Sql NVARCHAR (MAX),@OrderID INT;
SET @TableName = 'Orders';
SET @OrderID = 10251;
SET @sql = 'SELECT * FROM '+QUOTENAME(@TableName) +'WHERE OrderID = '+ CAST(@OrderID AS VARCHAR(10))+' ORDER BY ORDERID DESC'
EXEC(@sql);
註:這里的EXEC括弧中只允許包含一個字元串變數,但是可以串聯多個變數,如果我們這樣寫EXEC:
EXEC('SELECT TOP('+ CAST(@TopCount AS VARCHAR(10)) +')* FROM '+ QUOTENAME(@TableName) +' ORDER BY ORDERID DESC');
SQL編譯器就會報錯,編譯不通過,而如果我們這樣:
EXEC(@sql+@sql2+@sql3);
編譯器就會通過;
所以最佳的做法是把代碼構造到一個變數中,然後再把該變數作為EXEC命令的輸入參數,這樣就不會受限制了。
EXEC的缺點是不提供介面,這里的介面是指,它不能執行一個包含一個帶變數符的批處理,如下
代碼
DECLARE @TableName VARCHAR(50),@Sql NVARCHAR(MAX),@OrderID INT;
SET @TableName = 'Orders';
SET @OrderID = 10251;
SET @sql = 'SELECT * FROM '+QUOTENAME(@TableName) + 'WHERE OrderID = @OrderID ORDER BY ORDERID DESC'
EXEC(@sql);
關鍵就在SET @sql這一句話中,如果我們運行這個批處理,編譯器就會產生一下錯誤
Msg 137, Level 15, State 2, Line 1
必須聲明標量變數 "@OrderID"。
使用EXEC時,如果您想訪問變數,必須把變數內容串聯到動態構建的代碼字元串中,如:
SET @sql = 'SELECT * FROM '+QUOTENAME(@TableName) + 'WHERE OrderID = '+CAST(@OrderID AS VARCHAR(10))+' ORDER BY ORDERID DESC'
串聯變數的內容也存在性能方面的弊端。SQL Server為每一個的查詢字元串創建新的執行計劃,即使查詢模式相同也是這樣。為演示這一點,先清空緩存中的執行計劃
DBCC FREEPROCCACHE (這個不是本文所涉及的內容,您可以查看MS的MSDN)
將代碼1運行3次,分別對@OrderID 賦予下面3個值,10251,10252,10253。然後使用下面的代碼查詢
SELECT cacheobjtype,objtype,usecounts,sql FROM sys.syscacheobjects WHERE sql NOT LIKE '%cach%' AND sql NOT LIKE '%sys.%'
點擊F5運行,我們可以看到,每執行一次都要產生一次的編譯,執行計劃沒有得到充分重用。
EXEC除了不支持動態批處理中的輸入參數外,他也不支持輸出參數。默認情況下,EXEC把查詢的輸出返回給調用者。例如下面代碼返回Orders表中所有的記錄數
DECLARE @sql NVARCHAR(MAX)
SET @sql = 'SELECT COUNT(ORDERID) FROM Orders';
EXEC(@sql);
然而,如果你要把輸出返回給調用批處理中的變數,事情就沒有那麼簡單了。為此,你必須使用INSERT EXEC語法把輸出插入到一個目標表中,然後從這表中獲取值後賦給該變數,就像這樣:
代碼
DECLARE @sql NVARCHAR(MAX),@RecordCount INT
SET @sql = 'SELECT COUNT(ORDERID) FROM Orders';
CREATE TABLE #T(TID INT);
INSERT INTO #T EXEC(@sql);
SET @RecordCount = (SELECT TID FROM #T)
SELECT @RecordCount
DROP TABLE #T2
2.sp_executesql的使用
sp_executesql命令在SQL Server中引入的比EXEC命令晚一些,它主要為重用執行計劃提供更好的支持。
為了和EXEC作一個鮮明的對比,我們看看如果用代碼1的代碼,把EXEC換成sp_executesql,看看是否得到我們所期望的結果
代碼
DECLARE @TableName VARCHAR(50),@sql NVARCHAR(MAX),@OrderID INT ,@sql2 NVARCHAR(MAX);
SET @TableName = 'Orders ';
SET @OrderID = 10251;
SET @sql = 'SELECT * FROM '+QUOTENAME(@TableName) + ' WHERE OrderID = '+CAST(@OrderID AS VARCHAR(50)) + ' ORDER BY ORDERID DESC'
EXEC sp_executesql @sql
注意最後一行;事實證明可以運行;
sp_executesql提供介面
sp_executesql命令比EXEC命令更靈活,因為它提供一個介面,該介面及支持輸入參數也支持輸出參數。這功能使你可以創建帶參數的查詢字元串,這樣就可以比EXEC更好的重用執行計劃,sp_executesql的構成與存儲過程非常相似,不同之處在於你是動態構建代碼。它的構成包括:代碼快,參數聲明部分,參數賦值部分。說了這么多,還是看看它的語法:
EXEC sp_executesql
@stmt= <statement>,--類似存儲過程主體
@params = <params>, --類似存儲過程參數部分,聲明參數類型
<params assignment> --類似存儲過程調用,為參數賦值,參數值要和參數順序要一一對應,也可以通過為參數指明參數值的方式為其賦值
@stmt參數是輸入的動態批處理,它可以引入輸入參數或輸出參數,和存儲過程的主體語句一樣,只不過它是動態的,而存儲過程是靜態的,不過你也可以在存儲過程中使用sp_executesql;
@params參數與定義輸入/輸出參數的存儲過程頭類似,實際上和存儲過程頭的語法完全一樣;
@<params assignment> 與調用存儲過程的EXEC部分類似。
其實@stmt,@params可以省略,那麼exec sp_executesql的語法就可以簡寫成如下格式:
EXEC sp_executesql
<statement>,
<params>,
<params assignment>
為了說明sp_executesql對執行計劃的管理優於EXEC,我將使用前面討論EXEC時用到的代碼。
代碼
DECLARE @TableName VARCHAR(50),@sql NVARCHAR(MAX),@OrderID INT;
SET @TableName = 'Orders ';
SET @OrderID = 10251;
SET @sql = 'SELECT * FROM '+@TableName + ' WHERE OrderID = @OID ORDER BY ORDERID DESC'
--注意當要對動態sql語句的表名實行參數化時,不可以如下表示:
--set @sql='select * from @TableName where OrderID=@OID ORDER BY Orderid desc',
--如果這樣會提示必須聲明標量變數@TableName,只可以如上面所寫的一樣,將表名@TableName作為變數名進行拼接
EXEC sp_executesql
@sql,
N'@OID int ',
@OID = @OrderID
下面我們看看exec sp_executesql的執行效率,在調用該代碼和檢查它生成的執行計劃前,先清空緩存中的執行計劃;
DBCC FREEPROCCACHE
將上面的動態代碼執行3次,每次執行都賦予@OrderID 不同的值,然後查詢sys.syscacheobjects表,並注意它的輸出,優化器只創建了一個備用計劃,而且該計劃被重用的3次
SELECT cacheobjtype,objtype,usecounts,sql FROM sys.syscacheobjects WHERE sql NOT LIKE '%cache%' AND sql NOT LIKE '%sys.%' AND sql NOT LIKE '%sp_executesql%'
點擊F5運行。
sq_executesql的另一個與其介面有關的強大功能是,你可以使用輸出參數為調用批處理中的變數返回值。利用該功能可以避免用臨時表返回數據,從而得到更高效的代碼和更少的重新編譯。定義和使用輸出參數的語法與存儲過程類似。也就是說,你需要在聲明參數時指定OUTPUT子句。例如,下面的靜態代碼簡單的演示了如何從動態批處理中利用輸出參數@p把值返回到外部批處理中的變數@i.
DECLARE @sql AS NVARCHAR(12),@i AS INT;
SET @sql = N' SET @p = 10';
EXEC sp_executesql
@sql,
N'@p AS INT OUTPUT',
@p = @i OUTPUT
SELECT @i --該代碼返回輸出10
以字母 N 為前綴標識 Unicode 字元串常量
總結以下幾點:
一.使用exce sp_executesql效率比exec要高,同一類型的語句,只需編譯一次即可,而exec執行幾次就需要編譯幾次。
二.構造動態sql的where子句,也就是條件子句時,exec無法使用變數來進行站位,需要將變數轉換成字元串,然後和動態sql進行拼接,這就可能引起Sql注入問題,如下:
SET @sql = 'SELECT * FROM '+QUOTENAME(@TableName) + ' WHERE OrderID = '+CAST(@OrderID AS VARCHAR(50)) + ' ORDER BY ORDERID DESC'
而若使用exec sp_executesql則可以使用變數來進行站位,以後再給這個參數傳值的放式構造動態sql,就避免的Sql注入的問題,如下:
SET @sql = 'SELECT * FROM '+@TableName + ' WHERE OrderID = @OID ORDER BY ORDERID DESC'
三.無論是Exec還是Exec
sp_executesql,如果想要將表名和列名進行動態參數化,不可以使用表名參數和列名參數來進行站位,而且表名參數和列名參數需要使用存儲過程的參數.對
於exec sp_executesql來說,不可以將表名參數和列名參數在指定為在exec
sp_executesql參數聲明部分聲明的參數,如:
代碼
create PROCEDURE GetData
@tbName nvarchar(10),
@colName nvarchar(10),
@Name nvarchar(10)
AS
BEGIN
declare @sql nvarchar(50);
set @sql='select '+ @colName+' from ' +@tbName+ ' where name=@whereName';
--注意此句不可以寫成如下:
-- set @sql='select @colName from @tbName where name=@whereName';
exec sp_executesql
@sql,
N'@whereName nvarchar(10)',
@Name
END
也就是說exec sp_executesql語句的參數聲明部分只能聲明動態sql的where子句的參數。