0

SqlDataAdapterとSqlCommandBuilderを使用すると、SQL Server用に作成された更新ステートメントは、以下で説明するように非効率的です。ここで元のテーブル定義からパラメータサイズを継承する方法は?

は、再生のためのサンプルコードは、次のとおりです。 SQL Serverの:

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プロファイラでは、次のクエリはgetti cought ngの:オリジナルテーブルの列VはVARCHAR(50)のように定義されてdbo.testながら

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は、(3)VARCHARとして定義されます。

値708が3桁であるため、VARCHAR(3)が渡されます。定数文字列を5文字で渡すと、パラメータ・サイズはVARCHAR(5)として渡されます。 動作は、ここで説明し、設計によってれる:「明示的に設定されていない場合は、サイズが 指定されたパラメータ値の実際のサイズから推測されます。」

SqlParameter.Size Propertyを次のように

私は実際にそれを防ぐ方法を探しています。このため、パラメータとして渡される可変長データ型のすべての組み合わせが生成された単一の実行計画を強制するため、Sql Server内で数千の類似の実行計画が生成され、CPU時間が消費され、大量のRAMがキャッシュされる-中古。

このアプリケーションのコアコードを完全に再設計しなければ、この動作にどのような影響がありますか? 私がここでやりたいのは、元のテーブルからCommandBuilderを使用してTYPEを取得するだけでなく、SIZEを取得することですが、この情報を取得することは不可能です。

+0

列のサイズを提供しますが、私のテストではこれは役に立ちません。それらの「MaxLength」値を更新コマンドのパラメータにマッピングしても、クエリプランは依然として、「varchar」列の長さが推測されることを示している。 – wablab

+0

はい、まさに私の発見でもありますが、私はこのアプローチをしばらく行っていましたが、データアダプターのUPDATEメソッドは事前に準備されたすべてを "破壊"します... – Magier

答えて

1

私は答えを見つけたので、ここで投稿します(フォーマットされたコードの余分なスペースが役に立ちます)。答えは、FillSchemaとDataAdapterのRowUpdatingイベントを処理しているようです。したがって、上記のコメントで簡単に説明したように、da.FillSchema(ds, SchemaType.Source);を使用して列のサイズを取得します。次に、DataAdapterのRowUpdatingイベントのハンドラを追加し、そこでupdateコマンドのパラメータに列サイズを設定します。

EDIT:参照のためのより完全なコード例を含める:このような何か

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; 
    } 
} 

は、ここで私は、プロファイラで見るもののスクリーンショットです:SqlDataAdapterオブジェクトの `FillSchema`方法を使用して enter image description here

+0

私はこの木曜日の木曜日を試し、フィードバックをします。ありがとうございました。 – Magier

+0

私はあなたの提案を試みました。それは基本的に私がすでに使ってみたのとまったく同じです。\t par.Size = ds.Tables [0] .Columns [par.SourceColumn] .MaxLength;主な方法で。これは、あなたのソリューションはすべてのパラメータのサイズをうまく調整します。しかし、残念ながら、dataadapter.Updateメソッドは、SQL Serverで実行される最後のsp_executesqlコマンドで、柔軟性のある、集中型のデータ型サイズの望ましくない結果で終わるということをすべて変更します。だから残念ながら、それは問題を解決しません。 – Magier

+0

奇妙な。それは私の最後に働いているように見えます。私は私がやっていることのより完全な参照を提供するために私の答えのコードを更新しました。たぶんあなたは私たちがやっていることを発見することができます。あるいは、他の人が結果を再現しようとするかもしれません。 – wablab

関連する問題