ad hoc 一般都說是即席查詢,當到底什么是即席查詢,也沒有說清楚,讓人一頭霧水,在wikipedia上的解釋如下:
ad hoc 允許終端用戶自己去建立特定的、自定義的查詢請求。通常是通過一個用戶友好的圖形界面來進行數據查詢而無需用戶對 SQL 或者數據庫架構有深入的了解。
在stackOverflow上的解釋如下:
Ad hocis latin for "for this purpose". You might call it an "on the fly" query, or a "just so" query. It's the kind of SQL query you just loosely type out where you need it
var newSqlQuery = "SELECT * FROM table WHERE id = " + myId;
...which is an entirely different query each time that line of code is executed, depending on the value of
myId
. The opposite of an ad hoc query is a predefined query such as a Stored Procedure, where you have created a single query for the entire generalized purpose of selecting from that table (say), and pass the ID as a variable.
讓代碼解釋下什么是Ad hoc,就一目了然了。在SSMS中如下的查詢語句即為Ad Hoc查詢:
use AdventureWorks2008R2 go SELECT soh .SalesOrderNumber , sod.ProductID FROM Sales.SalesOrderHeader AS soh INNER JOIN Sales.SalesOrderDetail AS sod ON soh.SalesOrderID = sod.SalesOrderID WHERE soh.SalesOrderNumber = 'SO43662' SELECT soh .SalesOrderNumber , sod.ProductID FROM Sales.SalesOrderHeader AS soh INNER JOIN Sales.SalesOrderDetail AS sod ON soh.SalesOrderID = sod.SalesOrderID WHERE soh.SalesOrderNumber = 'SO58928'
這種 hard-code 查詢通常是臨時的,有特殊目的的,與之對應的是參數化的查詢,看如下的TSQL代碼:
declare @orderNumber nvarchar(50) SELECT soh .SalesOrderNumber , sod.ProductID FROM Sales.SalesOrderHeader AS soh INNER JOIN Sales.SalesOrderDetail AS sod ON soh.SalesOrderID = sod.SalesOrderID WHERE soh.SalesOrderNumber = @orderNumber
這種查詢為參數化查詢,生成的執行計划可以重用,而ad hoc 生成的執行計划不能重用,每次都需要compile一次,消耗相當多的CPU資源,當遇到內存壓力時,這些執行一次的執行計划
首先被清除掉,為了避免這種情況產生的代價,在數據庫級別有個選項parameterization,可以讓系統自動把Ad hoc 查詢 轉化成參數化查詢而重用執行計划。
在C#代碼中的Ad hoc 查詢SQL又是如何編寫的呢?
cmd.CommandType = CommandType.Text; cmd.CommandText = @"SELECT soh.SalesOrderNumber, sod.ProductID FROM Sales.SalesOrderHeader AS soh INNER JOIN Sales.SalesOrderDetail AS sod ON soh.SalesOrderID = sod.SalesOrderID WHERE soh.SalesOrderNumber = '" + txtSalesOrderNo.Text + "'"; dtrSalesOrders = cmd.ExecuteReader();
這種通過動態拼接的方式組成的SQL語句即為Ad hoc 查詢,與之對應的參數化查詢為:
cmd.CommandType = CommandType.Text; cmd.CommandText = @"SELECT soh.SalesOrderNumber, sod.ProductID FROM Sales.SalesOrderHeader AS soh INNER JOIN Sales.SalesOrderDetail AS sod ON soh.SalesOrderID = sod.SalesOrderID WHERE soh.SalesOrderNumber = @SalesOrderNo"; cmd.Parameters.Add("@SalesOrderNo", SqlDbType.NVarChar, 50); cmd.Parameters["@SalesOrderNo"].Value = txtSalesOrderNo.Text; dtrSalesOrders = cmd.ExecuteReader();
當然,存儲過程是100%的參數化查詢,不管該存儲過程帶不帶參數,通常來說,生產環境中不應該有大量的Ad hoc 查詢,會導致Cpu利用率過高,系統性能下降,通常的解決辦法就是
在程序中改寫代碼,轉化成存儲過程的寫法,如果是沒辦法改寫客戶端的代碼,有以下幾種思路來緩解Cpu的壓力,但是不能100%奏效!
1:強制參數化
ALTER DATABASE AdventureWorks SET PARAMETERIZATION FORCED
2:修改實例配置
EXEC sp_configure 'show advanced options',1 RECONFIGURE EXEC sp_configure 'optimize for ad hoc workloads',1 RECONFIGURE