C# 如何从原始表定义继承参数大小?
使用SqlDataAdapter和SqlCommandBuilder,为sql server创建的更新语句效率低下,如下所述 以下是复制的示例代码: Sql Server:C# 如何从原始表定义继承参数大小?,c#,sql-execution-plan,sqlcommand,sqldataadapter,sqlcommandbuilder,C#,Sql Execution Plan,Sqlcommand,Sqldataadapter,Sqlcommandbuilder,使用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] P
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被定义为VARCHAR3,而在原始表dbo.test中,列v被定义为VARCHAR50
传递VARCHAR3是因为值708有3位数字。如果要传递5个字符长的常量字符串,则参数大小将作为VARCHAR5传入。
此处通过设计对其行为进行了解释:如下所示:
如果未显式设置,则根据的实际大小推断大小
指定的参数值
我实际上在寻找一种方法来防止这种情况。正因为如此,作为参数传递的每一个可变长度数据类型组合都会强制执行生成的单个执行计划,这导致Sql Server中成千上万个类似的执行计划被编译,消耗CPU时间,并阻止大量RAM被缓存,而决不重复使用
如果不完全重新设计此应用程序的核心代码,如何影响此行为?
我想在这里做的不仅是使用CommandBuilder从原始表中获取类型,而且还要获取大小,但是似乎不可能获取这些信息 我想我找到了答案,所以我会把它贴在这里,为格式化代码留出额外的空间会有所帮助。答案似乎是FillSchema和处理DataAdapter的行更新事件的组合。因此,正如我们在上面的评论中简要讨论的,使用da.FillSchemads、SchemaType.Source;以获取列大小。然后,为DataAdapter的RowUpdate事件添加一个处理程序,并在那里设置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中看到的屏幕截图:
使用SqlDataAdapter的FillSchema方法将为您提供列大小,但在我的测试中,这没有帮助。即使将那些MaxLength值映射回update命令的参数,查询计划仍然显示varchar列的长度是推断的。是的,这也是我的发现,同时我也采用了这种方法,但是dataadapter的更新方法会破坏预先准备好的一切……我将在本周四尝试并给您反馈。谢谢。我试过你的建议了。它基本上和我已经尝试过的PAR.Simult= DS。表[0 ]一样。列[PAR.SoCuCeCultUn]。MaxLength;在主要方法中。这样,您的解决方案也可以成功地调整所有参数大小。但不幸的是,dataadapter.Update方法仍然会改变所有这些,它最终会导致在SQL Server中执行的最终sp_executesql命令中出现不希望出现的灵活、依赖于内容的数据类型大小的结果。所以,不幸的是,这并不能解决问题。它似乎对我这方面起了作用。我已经更新了上面答案中的代码,以提供更完整的参考资料。也许你可以发现我们正在做的不同的事情,或者其他人可以尝试重新计算结果。是的,你和我之间的主要区别是我忘记了下面一行:da.rowupdatening+=new-sqlrowupdateingeventhandleronrowupdate;我没有注意到,因为thiseventhandler中的断点仍然停止/工作,但最终类型大小会根据上面的行而有所不同。没有这一行,它是varcharlen的值,有了它,它就是VARCHARMAXLENOFCLOMN。奇怪,我不明白为什么,但毕竟你的答案是正确的!