0

使用SqlDataAdapter和SqlCommandBuilder爲sql server創建的更新語句效率低下,如下所述。如何從原始表定義繼承參數大小?

這裏是再現示例代碼: SQL服務器:

Create database TestDB; 
GO 
USE [TestDB] 
CREATE TABLE [dbo].[test](
    [i] [int] NOT NULL, 
    [v] [varchar](50) NULL, 
    [c] [char](10) NULL, 
CONSTRAINT [pk1] PRIMARY KEY CLUSTERED ([i] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 
insert into dbo.Test (i,v,c) values (10,'A','B'); 
GO 

C#控制檯應用程序演示代碼:

using System; 
using System.Data.SqlClient; 
using System.Data; 

namespace CmdBuildTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      SqlConnection cn = new SqlConnection(); 
      cn.ConnectionString = @"Server=localhost\sql2016;Database=testDB;Trusted_Connection=True;";    
      SqlDataAdapter da = new SqlDataAdapter("Select * From dbo.test", cn); 

      //da.FillSchema(ds, SchemaType.Mapped);   
      SqlCommandBuilder cb = new SqlCommandBuilder(da); 
      cb.ConflictOption = ConflictOption.OverwriteChanges; 
      //cb.RefreshSchema(); 
      DataSet ds = new DataSet(); 
      da.Fill(ds); 
      ds.Tables[0].Rows[0]["v"] = DateTime.UtcNow.Millisecond.ToString(); 
      SqlCommand u = cb.GetUpdateCommand(true); 
      Console.WriteLine("Update Command: " + u.CommandText); 

      foreach (SqlParameter par in u.Parameters) 
      { 
       Console.WriteLine(" Name=" + par.ParameterName + "|Type=" + par.SqlDbType.ToString() + " |Size=" + par.Size.ToString()); 
      }   
      da.UpdateCommand = u; //I am not sure if this is required, but I am careful.    
      //Execute Changes/Update Statement : 
      da.Update(ds); 
      Console.ReadLine(); 
      //Sample result in Profiler: 
      /* 
       exec sp_executesql N'UPDATE [dbo].[test] SET [v] = @v WHERE (([i] = @Original_i))',N'@v varchar(3),@Original_i int',@v = '603',@Original_i = 1 
      */ 
     } 

    } 
} 

Console.WriteLine命令顯示創建下列SQL UPDATE語句:

UPDATE [dbo].[test] SET [i] = @i, [v] = @v, [c] = @c WHERE (([i] = @Original_i)) 

在Sql Profiler中,以下查詢是getti毫無遺漏NG:

exec sp_executesql N'UPDATE [dbo].[test] SET [v] = @v 
WHERE (([i] = @Original_i))',N'@v varchar(3),@Original_i int',@v='708',@Original_i=1 

現在你可以看到,參數@v被定義爲VARCHAR(3),而在原始表dbo.test列V被定義爲VARCHAR(50)。

由於值708有3個數字,所以傳遞VARCHAR(3)。如果要傳遞一個長度爲5個字符的常量字符串,則參數大小將作爲VARCHAR(5)傳遞。 的行爲是設計說明:這裏:SqlParameter.Size Property如下:「如果沒有明確設定,大小從 指定參數值的實際大小推斷」

我正在尋找一種方法來防止這種情況。正因爲如此,所有通過參數傳遞的變長數據類型的組合都會強制生成一個執行計劃,這導致Sql Server中數千個類似的執行計劃正在被編譯,這些執行計劃需要CPU時間並阻止大量RAM被緩存, -用過的。

如何在不完全重新設計此應用程序的核心代碼的情況下影響此行爲? 我想在這裏做的是不僅從原始表使用CommandBuilder,而且SIZE獲得TYPES,但似乎不可能獲得此信息。

+0

使用的SqlDataAdapter的'FillSchema'方法將爲您提供色譜柱的大小,但在我的測試中這並沒有幫助。即使將這些「MaxLength」值映射回更新命令的參數,查詢計劃仍然會顯示「varchar」列的長度被推斷。 – wablab

+0

是的,這也正是我的發現,同時我也採取了這種方法,但dataadapter的UPDATE方法「銷燬」所有事先準備好的東西...... – Magier

回答

1

我想我找到了答案,所以我會在這裏發佈(格式化代碼的額外空間將有所幫助)。答案似乎是FillSchema和處理DataAdapter的RowUpdating事件的組合。因此,正如我們在上面的評論中簡要討論的那樣,使用da.FillSchema(ds, SchemaType.Source);來獲取列大小。然後,爲DataAdapter的RowUpdating事件添加一個處理程序,並在那裏爲update命令的參數設置列大小。事情是這樣的:

編輯:包括參考一個更完整的代碼示例:

public static void Main(string[] args) 
{ 
    var cn = new SqlConnection("Data Source=.; Initial Catalog=TestDB; Integrated Security=SSPI"); 
    var da = new SqlDataAdapter("SELECT * FROM dbo.test", cn); 
    var cb = new SqlCommandBuilder(da); 
    cb.ConflictOption = System.Data.ConflictOption.OverwriteChanges; 

    var ds = new DataSet(); 
    da.Fill(ds); 
    da.FillSchema(ds, SchemaType.Source); 

    ds.Tables[0].Rows[0]["v"] = DateTime.UtcNow.Millisecond.ToString(); 
    da.RowUpdating += new SqlRowUpdatingEventHandler(da_RowUpdating); 
    da.Update(ds); 
} 

static void da_RowUpdating(object sender, SqlRowUpdatingEventArgs e) 
{ 
    foreach (var p in e.Command.Parameters.Cast<SqlParameter>()) 
    { 
     p.Size = e.Row.Table.Columns[p.SourceColumn].MaxLength; 
    } 
} 

下面是我在profiler中看到的截圖: enter image description here

+0

我會嘗試這個ob週四,並給你反饋。謝謝。 – Magier

+0

我試過你的建議。它基本上完全一樣,我也已經嘗試使用\t par.Size = ds.Tables [0] .Columns [par.SourceColumn] .MaxLength;在主要方法。這一點以及您的解決方案成功調整了所有參數大小。但不幸的是,dataadapter.Update方法仍然改變了所有這一切,它終結於SQL Server中執行的最終sp_executesql命令中靈活的,依賴於數據的數據類型大小的不希望的結果。所以,不幸的是,它不能解決問題。 – Magier

+0

奇怪。它似乎在爲我的目的而努力。我在上面的答案中更新了代碼,以提供我正在做的更完整的參考。也許你可以發現我們做得不同的事情,或者別人可以嘗試重現結果。 – wablab