使用 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 Profiler 中,以下查询越来越多:
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 属性如下:
“如果没有明确设置,大小是从指定参数值的实际大小推断出来的。”
我实际上正在寻找一种方法来防止这种情况。因此,作为参数传递的可变长度数据类型的每个组合都会强制生成一个执行计划,这会导致 Sql Server 中成千上万个类似的执行计划被编译消耗 CPU 时间并阻塞大量 RAM 被缓存,并且永远不会重新-用过的。
如果不完全重新设计此应用程序的核心代码,如何影响这种行为?我在这里想做的不仅是使用 CommandBuilder 从原始表中获取 TYPES,而且还获取 SIZE,但似乎无法获取此信息。