Sql-Server

用於查詢的 CLR SQL 與 T-SQL

  • July 6, 2016

我知道 CLR 儲存過程在過程程式碼中更有效,而 T-SQL 更適合查詢 - 但是我很難決定使用什麼來滿足我的特定需求,我想知道是否還有其他事情我在做決定時應該考慮。

我計劃創建的程式碼包括查詢一組數據、實現一些算法(隨著時間的推移會變得更加複雜)以及將數據插入數據庫。此儲存過程將由調度程序部署 - 很可能是 azure webJobs,因為據我所知 Azure SQL 不支持 db 作業。

為什麼我想到使用 C#

假設我可以從我的伺服器載入 DAL 庫,並利用 OOP——我想我可以為這個問題實現一個對編碼器更友好的解決方案——至於性能,我認為它會更好,或者至少不明顯。

為什麼我想到使用 T-SQL

我實際上並不認為它是一個更好的解決方案,但我可能忽略了一些對 T-SQL 來說可能是一個很好的案例,因為它沒有任何意義對我來說毫無意義。

最重要的是

有沒有一種方法可以創建 T-SQL 儲存過程,以復雜對像作為參數呼叫 C# 儲存過程 - 執行我的算法並返回要插入的數據,然後從 T-SQL 插入它?似乎是最優雅,性能最好的(?)解決方案(假設它是可能的,並且從 T-SQL 呼叫 CLR 沒有缺點)。

首先,要明確一點:SQLCLR / .NET / C# / VB.NET 無法查詢數據庫。只有 T-SQL 可以查詢 SQL Server。因此,為了讓 SQLCLR 程式碼以任何方式獲取數據或與 SQL Server 互動,它必須SqlConnection像任何其他 .NET 應用程序一樣建立一個 .NET 應用程序,並送出 T-SQL,或執行一個儲存過程。

是的,您可以從 T-SQL 呼叫 SQLCLR(不管是 C# 還是 VB.NET)儲存過程、標量函式或表值函式 (TVF) 等。事實上,這是呼叫 SQLCLR 對象的唯一方法。表示在程序集中定義的 SQLCLR 對象的類需要定義指向這些類的 T-SQL 包裝器對象,以便可以呼叫它們。

可以將復雜對象發送到 SQLCLR 對象,但遺憾的是不能通過表值參數 (TVP)。但是,您可以在 T-SQL 中創建一個本地臨時表,填充它,然後在 SQLCLR 對像中從中進行選擇,或者您可以將復雜數據打包成 XML 並將其作為參數傳遞給 SQLCLR 對象,該對象將通過XmlReader.

為了有效地取回數據,您可以:

  • INSERT從 SQLCLR 儲存過程發出語句
  • 創建一個接受 TVP 並從 SQLCLR 對象呼叫的 T-SQL 儲存過程
  • 從在 INSERT 語句中使用的 SQLCLR 對象返回結果集,與INSERT INTO ... EXECSQLCLR 儲存過程或INSERT INTO ... SELECTSQLCLR TVF 一樣。

就載入 DAL 庫而言,這可能有效,但也可能會出現並發症。這完全取決於您的庫在做什麼以及正在引用哪些框架庫。SQL Server 的 CLR 主機僅支持一小部分 .NET Framework 庫,因此如果您的庫引用了其他未“批准”的庫,那麼您將需要手動載入這些庫以及它們的任何依賴庫(如果有的話)。這裡的複雜因素是載入不受支持的 .NET Framework 庫需要將它們設置為UNSAFE,這反過來又需要將數據庫設置為TRUSTWORTHY ON. 另一個複雜因素是,載入 .NET Framework 庫可能一開始就無法載入,因為 SQL Server 內部的 CLR 主機只允許純 MSIL 程序集,因此您無法載入混合模式程序集。一些 .NET Framework 庫目前是混合模式,不會載入。其他的目前是“純”的,並且會載入,但不能保證在未來的 .NET Framework 更新中不會將它們更改為混合模式程序集。如果/當這種情況發生(並且已經發生,例如System.ServiceModel),那麼您將需要重新編碼您的程序集。所以最好堅持使用受支持的框架庫,因為這些庫可以保證在未來的升級中繼續工作。

請記住,應用程序域是每個數據庫、每個所有者(程序集的)。這意味著,就任何特定的 SQLCLR 對象而言,所有會話/SPID 將呼叫相同的程式碼並共享相同的記憶體。這就是為什麼需要將方法聲明為static. 這就是為什麼嘗試使用非readonly靜態類變數需要將程序集標記為UNSAFE:因為此類變數的值可以被另一個會話/SPID 覆蓋(例如:SQLCLR 程序集在多個查詢同時執行時拋出錯誤) .

關於問題中的這一陳述:

至於性能,我認為相比之下會更好或至少不明顯。

我不會在這裡假設任何事情。有很多因素會影響任一方向的性能:

  • 某些數據類型在 SQL Server 記憶體和 .NET AppDomain 記憶體之間來回傳輸比其他數據類型更快
  • 如何在 C# 中編寫算法(我最近為已經收到另一個使用 LINQ 建議的人編寫了一個簡單的函式——LINQ 版本花了 375 - 400 毫秒來做與我的版本相同的事情,但有更多的行,只需要2 - 9 毫秒完成,41 倍 - 200 倍的差異!)
  • 您擁有多少數據(有時 T-SQL 使用較小的值會更快——通常是字元串——而 SQLCLR 使用更長的值可能會更快)
  • 您是否遵循最佳實踐?如果您正在編寫確定性 SQLCLR 標量函式並且不將其標記為IsDeterministic=true,那麼您將無法從它能夠參與並行計劃中獲益(這是 T-SQL UDF 無法做到的)。
  • 等等

因此:不要假設;測試!

如果您想了解有關使用 SQLCLR 的更多資訊,您可能想查看我在 SQL Server Central 上就該主題撰寫的系列文章:通往 SQLCLR 的階梯(需要免費註冊才能訪問其內容)。有幾篇文章和更多的文章,還有大量的例子來說明各種特性、安全性等。


關於 Azure SQL 數據庫:請注意,雖然SAFE在 2014 年底添加了對 SQLCLR(僅限程序集)的支持,但最近(據說是暫時的)刪除了它。客戶於 2016 年 4 月 8 日收到電子郵件,稱該功能於 2016 年 4 月 15 日被刪除。我能找到的最正式的通知是reddit 上的這個文章

顧客:

Azure SQL 中對 CLR 程序集的支持是否會很快結束?

我問這個是因為我今天早上(據說)收到了一封來自 Microsoft 的電子郵件,指出 4 月 15 日之後將不再支持此功能(!!!)。

Jan,Azure SQL DB 團隊的 PM,致力於性能和彈性池:

您收到的電子郵件確實是真實的。如郵件中所述,我們確定 SQL 數據庫中使用者定義的 SQL CLR 存在安全風險。迄今為止,還沒有已知的漏洞利用。為了幫助保護客戶的安全,我們已主動暫停在 Azure SQL 數據庫中使用使用者定義的 SQL CLR。這意味著使用者定義的 SQL CLR 將不再起作用。我們將在六個月內為您提供恢復 SQL CLR 的時間表。

和 Azure SQL 數據庫安全團隊的 PM:

此問題僅適用於 Azure SQL 數據庫服務,不適用於本地 SQL Server 或 IaaS。

引用自:https://dba.stackexchange.com/questions/142868