處理常見數據庫編程任務和問題 前一節在高層次上對基於 CLR 的編程與 T
SQL
中間層和擴展存儲過程 (XP) 進行了比較
在這一節中
我們將考慮數據庫應用程序開發人員經常遇到的一些編程任務和模型
並且討論如何使用 CLR(以及在一些情況下如何不使用)進行處理
使用 Framework 庫進行數據驗證
SQL Server
中的 CLR 集成允許用戶利用
NET Framework 類庫提供的豐富功能來解決其數據庫編程問題
常規表達式的使用可以很好地說明 CLR 集成如何增強了驗證和過濾功能
在處理數據庫中存儲的文本數據方面
常規表達式提供的模式匹配功能比通過 T
SQL 查詢語言中的 LIKE 運算符可用的模式匹配功能多
考慮以下 C# 代碼
它只是 System
Text
RegularExpressions 命名空間中的 RegEx 類的一個簡單包裝
using System;
using System
Data
Sql;
using System
Data
SqlTypes;
using System
Text
RegularExpressions;
public partial class StringFunctions
{
[SqlFunction(IsDeterministic = true
IsPrecise = true)]
public static bool RegExMatch(string pattern
string matchString)
{
Regex r
= new Regex(pattern
TrimEnd(null));
return r
Match(matchString
TrimEnd(null))
Success;
}
[SqlFunction(IsDeterministic = true
IsPrecise = true)]
public static SqlString ExtractAreaCode(string matchString)
{
Regex r
= new Regex(
\\((?<ac>[
][
][
])\\)
);
Match m = r
Match(matchString);
if (m
Success)
return m
Value
Substring(
);
else return SqlString
Null;
}
};
假設 StringFunctions
RegExMatch 和 StringFunctions
ExtractAreaCode 方法已經被注冊為帶有 RETURNS NULL ON NULL INPUT 選項的數據庫中的用戶定義函數(這允許該函數在任何輸入都為 NULL 時返回 NULL
這樣在該函數內就沒有特殊的 NULL 處理代碼)
現在
可以在使用上述代碼的表的列中定義約束
以驗證電子郵件地址和電話號碼
如下所示
create table Contacts
(
FirstName nvarchar(
)
LastName nvarchar(
)
EmailAddress nvarchar(
) CHECK
(dbo
RegExMatch(
[a
zA
Z
_\
]+@([a
zA
Z
_\
]+\
)+(com|org|edu)
EmailAddress) =
)
USPhoneNo nvarchar(
) CHECK
(dbo
RegExMatch(
\([
][
][
]\) [
][
][
]\
[
][
][
][
]
UsPhoneNo)=
)
AreaCode AS dbo
ExtractAreaCode(UsPhoneNo) PERSISTED
)
另外
請注意 AreaCode 列是使用 dbo
ExtractAreaCode 函數從 USPhoneNo 列中取出地區代碼而得到的列
然後
可以對 AreaCode 列建立索引
這樣便於在表格中根據特定地區代碼查找聯系人的查詢
更一般地講
此示例演示了如何利用
NET Framework 庫來增強帶有有用函數的 T
SQL 內置函數庫
這些有用函數很難用 T
SQL 表達
產生結果集
需要從運行在服務器內的數據庫對象(如存儲過程或視圖)中產生結果集可能是最常見的數據庫編程任務之一
如果可以使用單個查詢(SELECT 語句)來構建結果集
則這只需使用視圖或在線表值函數即可實現
然而
如果需要多個語句(過程邏輯)來構建結果集
則有兩個選擇
存儲過程和表值函數
雖然 SQL Server
有表值函數
但是它們只能用 T
SQL 進行編寫
在 SQL Server
中
通過 CLR 集成
還可以使用托管語言來編寫這樣的函數
在這一節中
我們將討論如何決定使用存儲過程還是使用表值函數
以及使用 T
SQL 還是使用 CLR
從 T
SQL 過程可以將相關的結果作為表值函數的返回值返回
或者通過存儲過程內曾經隱式存在的
調用者管道
返回
從存儲過程的任何位置(不管執行的嵌套程度如何)執行 SELECT 語句都會把結果返回給調用者
更嚴格地講
實際上 SELECT 語句並沒有進行變量賦值
而且
FETCH
READTEXT
PRINT 和 RAISERROR 語句也隱式地將結果返回給調用者
請注意
調用者
一直沒有正確地定義
它實際上取決於存儲過程的調用上下文
如果從任何客戶端數據訪問 API(如 ODBC
OLEDB 和 SQLClient)中調用存儲過程
則調用者是實際的 API
並且它提供的任何一種抽象都可以表示結果(如 hstmt
IRowset 或 SqlDataReaderand)
這意味著
通常
從存儲過程中產生的結果將始終返回到調用 API 中
而跳過堆棧中所有的 T
SQL 框架
如以下示例中所示
create proc proc
as
select col
from dbo
table
;
create proc proc
as
exec proc
;
在執行過程 proc
時
proc
產生的結果將轉到 proc
的調用者
proc
中只有一種方法可以捕獲產生的結果
即通過使用 INSERT/EXEC 將其存儲到永久表
臨時表或表變量中
從而將結果流式處理到磁盤
create proc proc
as
declare @t table(col
int);
insert @t (col
) exec proc
;
do something with results
在使用 INSERT/EXEC的情況下
調用者
是 INSERT 語句的目標表/視圖
SQL Server
CLR 存儲過程引入了新的
調用者
類型
當通過托管框架中的 in
proc 提供程序執行查詢時
就可以通過 SqlDataReader 對象使結果可用
並且可以在存儲過程中使用結果
SqlCommand cmd=SqlContext
GetCommand();
cmd
CommandText=
select col
from dbo
table
;
SqlDataReader sdr=cmd
ExecuteReader();
while (sdr
Read())
{
// do something with current row
}
下面的問題是托管存儲過程如何將結果返回給它的調用者而不是通過 SqlDataReader 來使用它
這可以通過稱為 SqlPipe 的新類來實現
通過 SqlContext 類的靜態方法可以使此類的實例對托管存儲過程可用
SqlPipe 有幾種方法可以將結果返回給存儲過程的調用者
這兩個類都是在 Sqlaccess
dll 中定義的
SqlPipe
在 SqlPipe 類中可以使用的方法中
最容易理解的就是 Execute 方法
它將命令對象作為參數接受
這個方法主要執行命令
並且沒有使執行的結果可用於托管框架
而是將結果發送給存儲過程的調用者
發送結果的這種形式在語義上與將語句嵌入 T
SQL 存儲過程內是一樣的
在本文前面描述的性能方面
SqlPipe
Execute 與 T
SQL 是等價的
create proc proc
as
select col
from dbo
table
;
The equivalent in C# would be:
public static void proc
()
{
System
Data
SqlServer
SqlCommand cmd=SqlContext
GetCommand();
cmd
CommandText=
select col
from dbo
table
;
SqlContext
GetPipe()
Execute(cmd);
}
對於返回的數據是由執行的查詢直接產生的情況
SqlPipe
Execute 可以很好地工作
然而
在某些情況下可能希望
)從數據庫中獲得結果
進行操作或者轉換
然後發送它們
或者
)將結果發送回原地而不是本地 SQL Server 實例
SqlPipe 提供了一組可以協同工作以使應用程序可以將任何結果返回給調用者的方法
SendResultsStart
SendResultsRow 和 SendResultsEnd
在很大程度上
這些 API 類似於對擴展存儲過程的開發人員可用的 srv_describe 和 srv_sendrow API
SendResultsStart 將 SqlDataRecord 作為參數接受
並且指示返回的新結果集的開頭
該 API 從記錄對象讀取元數據信息
並且將其發送給調用者
該方法有重載
以允許發送元數據以及記錄中的實際值
隨後可以返回行
方法是對要發送的每行調用一次 SendResultsRowows
在發送完全部所需的行之後
需要調用 SendResultsEnd 來指示結果集的結尾
例如
下面的 C# 代碼片段表示一個存儲過程
它讀取 XML 文檔(來自 MSDN 的 Really Simple Syndication [RSS] 供給)
使用 System
Xml 類進行解析
並且以相關的形式返回信息
請注意
這些代碼應該創建為 EXTERNAL_ACCESS(或 UNSAFE)程序集
因為訪問 Internet 所需的代碼訪問安全 (CAS) 權限只有在這些權限集中才是可用的
// Retrieve the RSS feed
XPathDocument doc = new XPathDocument(
);
XPathNavigator nav = doc
CreateNavigator();
XPathNodeIterator i = nav
Select(
//item
);
// create metadata for four columns
// three of them are string types and one of the is a datetime
SqlMetaData[] rss_results = new SqlMetaData[
];
rss_results[
] = new SqlMetaData(
Title
SqlDbType
NVarChar
);
rss_results[
] = new SqlMetaData(
Publication Date
SqlDbType
DateTime);
rss_results[
] = new SqlMetaData(
Description
SqlDbType
NVarChar
);
rss_results[
] = new SqlMetaData(
Link
SqlDbType
NVarChar
);
// construct the record which holds metadata and data buffers
SqlDataRecord record = new SqlDataRecord(rss_results);
// cache a SqlPipe instance to avoid repeat
From:http://tw.wingwit.com/Article/program/SQLServer/201311/22049.html