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 프로필러에서 다음 쿼리는 getti입니다. NG는에게 붙잡혀 : 당신이 볼 수있는 자, 매개 변수 @v가 VARCHAR로 정의

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 

(3) 열 V dbo.test 원래의 테이블에있는 동안하는 것은 VARCHAR (50)로 정의된다.

값 708에 3 자리가 있으므로 VARCHAR (3)이 전달됩니다. 상수 스트링 5 문자를 전달하면, 매개 변수 크기는 VARCHAR (5)로 전달됩니다. 동작으로 설계된는 여기에 설명 : SqlParameter.Size Property 다음과 같이 ". 명시 적으로 설정하지 않으면, 크기가 지정된 매개 변수 값의 실제 크기로부터 추정된다"

나는 실제로 그것을 막을 방법을 찾고 있습니다. 이 때문에 매개 변수로 전달 된 가변 길이 데이터 형식의 모든 조합이 생성 된 단일 실행 계획을 강제합니다. 이로 인해 Sql Server에서 수천 개의 유사한 실행 계획이 생성되어 CPU 시간을 소비하고 많은 양의 RAM이 캐싱되지 않도록합니다. -익숙한.

이 응용 프로그램의 핵심 코드를 완전히 다시 디자인하지 않으면 어떻게 영향을받을 수 있습니까? 여기서 내가하고 싶은 것은 CommandBuilder를 사용하는 원본 테이블의 TYPES뿐만 아니라 SIZE도 얻는 것이지만이 정보를 얻는 것은 불가능 해 보입니다.

+0

: 여기

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

내가 프로파일에 표시되는 내용의 스크린 샷입니다 : 참조에 대한보다 완벽한 코드 예제 포함 열 크기를 제공하지만 내 테스트에서는 이것이 도움이되지 않습니다. 이러한 'MaxLength' 값을 업데이트 명령의 매개 변수로 매핑 할 때도 쿼리 계획은 여전히'varchar' 열의 길이가 유추되었음을 보여줍니다. – wablab

+0

네, 정확하게 저의 발견입니다. 한편으로이 접근법을 사용했습니다. 그러나 데이터 어댑터의 UPDATE 메소드는 미리 준비된 모든 것을 "파괴"합니다 ... – Magier

답변

1

답변을 찾은 것 같아서 여기에 게시합니다. 형식이 지정된 코드를위한 추가 공간이 도움이 될 것입니다. 대답은 FillSchema과 DataAdapter의 RowUpdating 이벤트를 처리하는 것으로 나타납니다. 위의 설명에서 간략하게 설명했듯이 da.FillSchema(ds, SchemaType.Source);을 사용하여 열 크기를 가져옵니다. 그런 다음 DataAdapter의 RowUpdating 이벤트에 대한 처리기를 추가하고 여기에서 update 명령의 매개 변수에 열 크기를 설정합니다. 이런 식으로 뭔가 :

편집 : SqlDataAdapter를의`FillSchema` 방법을 사용 enter image description here

+0

나는이 목요일을 시도하고 피드백을 줄 것입니다. 고맙습니다. – Magier

+0

나는 당신의 제안을 시도했다. 그것은 기본적으로 정확히 동일하게 사용합니다. \t par.Size = ds.Tables [0] .Columns [par.SourceColumn] .MaxLength; main 메소드에서. 이것은, 물론 당신의 솔루션은 모든 매개 변수 크기를 성공적으로 조정합니다. 그러나 유감스럽게도 dataadapter.Update 메서드는 SQL Server에서 실행되는 마지막 sp_executesql 명령의 유연한 집중 형 데이터 형식 크기로 인해 바람직하지 않은 결과로 끝나는 모든 것을 변경합니다. 불행히도 문제를 해결하지 못합니다. – Magier

+0

이상한. 그것은 내 마지막에 작동하는 것 같습니다. 위의 대답에서 코드를 업데이트하여 현재 수행중인 작업에 대한보다 완벽한 참조를 제공합니다. 어쩌면 우리가하는 일을 다른 방식으로 파악할 수도 있고, 아니면 다른 누군가가 결과를 다시 시도 할 수도 있습니다. – wablab