更新大表(很多列)。 C#.NET

我必须更新一个包含超过270个更新字段的大表。

我是.NET的新手,需要建议在这种情况下使用什么更好:SqlCommand,某种内存映射表或DataSet,或者它是否存在使用来自DB的元数据的某种自动生成的对象? 请帮忙。

原因:我有一个旧的大型Delphi7应用程序,其中一部分负责监听socket上的一些数据包,这些数据包被编组到大型结构中,最后一步存储在DB中。 现在我将这部分移植到新的C#服务中,至少实际上我必须保留相同的逻辑。 问题是结构是BIG(超过220个字段),存储它的表有近300个字段。 从我的220个字段的结构中扣除/计算其他~50个字段,所有都应该在DB中更新。 实际的Delphi代码是丑陋的ant,它像桌子一样在几年内增长,如下所示:

'UPDATE TABLE_NAME ' + ' MSG_TYPE = ' + IntToStr(integer(RecvSruct.MSG_TYPE)) + ' ' + ' ,SomeFLOATfield = ' + FloatToStr(RecvSruct.SomeFLOATfield) + ' ' + ... //and other over 270 fileds here 'WHERE ID = ' + IntToStr(obj.ID) 

没有任何动态SQL等。实际上我无法改变数据库结构..所以我只能在代码中播放,我不确定是否有必要翻译代码。 表用于某些报告和统计信息。 一些计算/扣除的字段必须处理源代码中的一些常量。

使用开发工具:MS SQL Server 2000,C#.net2.0,VS2008

最简单的解决方案适用于此,因为ole db的工作方式是使用字符串。 所以,为了传递270,500,1000个参数,我所做的只是传递一个字符串,一个包含270个参数的字符串可能远低于2kb …这在现代计算中…继续1 …不有性能损失。 这里有一个xml解决方案,但这只是苹果和橙子,你仍然传递字符串,但是需要额外的代码来处理xml。 所以…你的架构看起来像:

  1. 具有270个输入参数的SQL Server上的存储过程:

      Create Procedure sp_Example1 (@param1 [type], @param2 [type], @param3 [type], etc...) AS BEGIN [SQL statements] END 
  2. 具有270个参数的命令对象:

     SqlCommand cmd = new SqlCommand("sp_Example1", [sqlconnectionstring]); cmd.Parameters.Add(New SqlParameter("@param1", param1.value)); cmd.Parameters.Add(New SqlParameter("@param2", param2.value)); cmd.Parameters.Add(New SqlParameter("@param3", param3.value)); 

请记住,您仍在进行相当密集的操作,但您的基准应该是旧的应用程序。 如果它有点糟糕,我不担心它,因为框架需要更多的计算开销。

我不知道为什么它不会格式化代码……

好。 由于您可以添加新的存储过程,因此我建议打包所有值,并将其作为XML传递给存储过程。

你可以在这里找到一个例子: http : //granadacoder.wordpress.com/2009/01/27/bulk-insert-example-using-an-idatareader-to-strong-dataset-to-sql-server-xml/

好消息,我的例子比较旧,编码为Sql Server 2000(使用OPENXML)。

..

这比将300个参数发送到存储过程恕我直言更好。

另一个优点是,如果您有超过1行数据,您也可以将其下载。

……

它的“要点”:

首先,您可以在此处获取2000个“pubs”数据库:

http://www.microsoft.com/en-us/download/details.aspx?id=23654

现在添加此存储过程:

/ * USP * /

 DROP PROCEDURE dbo.uspTitleUpsert GO CREATE PROCEDURE dbo.uspTitleUpsert ( @xml_doc TEXT , @numberRowsAffected int output --return ) AS SET NOCOUNT ON DECLARE @hdoc INT -- handle to XML doc DECLARE @errorTracker int -- used to "remember" the @@ERROR DECLARE @updateRowCount int DECLARE @insertRowCount int --Create an internal representation of the XML document. EXEC sp_xml_preparedocument @hdoc OUTPUT, @XML_Doc -- build a table (variable table) to store the xml-based result set DECLARE @titleupdate TABLE ( identityid int IDENTITY (1,1) , title_id varchar(6) , title varchar(80) , type varchar(32) , pub_id varchar(32) , price money , advance money , royalty varchar(32) , ytd_sales varchar(32) , notes TEXT , pubdate datetime ) --the next call will take the info IN the @hdoc(with is the holder for @xml_doc), and put it IN a variableTable INSERT @titleupdate ( title_id , title , type , pub_id , price , advance , royalty , ytd_sales , notes , pubdate ) SELECT title_id , title , type , pub_id , price , advance , royalty , ytd_sales , notes , getdate() /*dbo.udf_convert_xml_date_to_datetime (pubdate)*/ FROM -- use the correct XPath .. the second arg ("2" here) distinquishes -- between textnode or an attribute, most times with --.NET typed datasets, its a "2" --This xpath MUST match the syntax of the DataSet OPENXML (@hdoc, '/TitlesDS/Titles', 2) WITH ( title_id varchar(6) , title varchar(80) , type varchar(32) , pub_id varchar(32) , price money , advance money , royalty varchar(32) , ytd_sales varchar(32) , notes TEXT , pubdate varchar(32) ) EXEC sp_xml_removedocument @hdoc select * from @titleupdate SET NOCOUNT OFF Update dbo.titles set title = vart.title , type = vart.type , pub_id = vart.pub_id , price = vart.price , advance = vart.advance , royalty = vart.royalty , ytd_sales = vart.ytd_sales , notes = vart.notes , pubdate = vart.pubdate FROM @titleupdate vart , dbo.titles realTable WHERE (rtrim(upper(realTable.title_id))) = ltrim(rtrim(upper(vart.title_id))) and exists ( select null from dbo.titles innerRealTable where (rtrim(upper(innerRealTable.title_id))) = ltrim(rtrim(upper(vart.title_id))) ) Select @updateRowCount = @@ROWCOUNT INSERT INTO dbo.titles ( title_id , title , type , pub_id , price , advance , royalty , ytd_sales , notes , pubdate ) Select title_id , title , type , pub_id , price , advance , royalty , ytd_sales , notes , pubdate FROM @titleupdate tu WHERE not exists ( select null from dbo.titles innerRealTable where (rtrim(upper(innerRealTable.title_id))) = ltrim(rtrim(upper(tu.title_id))) ) Select @insertRowCount = @@ROWCOUNT print '/@insertRowCount/' select @insertRowCount print '' print '/@updateRowCount/' select @updateRowCount print '' select @numberRowsAffected = @insertRowCount + @updateRowCount --select * from titles SET NOCOUNT OFF GO --GRANT EXECUTE on dbo.uspTitleUpsert TO pubsuser GO 

/ *示例用法* /

 EXEC dbo.uspTitleUpsert '   PN3333 Peanut Cooking trad_cook 0877 3.33 4444.00 1 33 Peanut Cooking Notes    SSMS4444 Sql Server Management Studio programming 0877 13.33 5444.00 2 33 Sql Server Management Studio Notes    ' , 0 

使用Simple.data可以简化代码和逻辑(虽然它需要.NET 4.0)

  1. 您可以将表拆分为新表,然后创建与连接,交换,铸件等旧表相同名称的视图,以将新表转换为报表的旧结构。

  2. 如果您使用命令(如您发布的Delphi代码中),请使用参数来防止SQL注入。

  3. 使用当前的DB结构,您可以使用开箱即用的ORM,因为您需要映射大量列。 您可以将POCO类创建为类型安全模型,然后使用数据符号或自定义属性使映射更简单,然后从属性中动态创建SQL命令。

我害怕没有特别的兔子从这个帽子里拔出.net帽子。

除了“知道”的复杂性之外,只有一些完全独立的字段已经改变并且只为它们构建了一个更新语句,你就被塞满了。

即使知道这一点也会更好地存储,因为blob并不能真正帮助你。 在任何情况下都可能不是真的。

参数化查询或存储过程在代码中看起来有点整洁,但无论如何都可以在delphi中完成。

没有办法从这里说它应该怎么做,但一个可能有里程的想法是隐藏当前表除了一小部分function之外的所有东西。

例如,如果要重命名它,然后使用当前名称创建视图。 读取的东西和(可能是写入它的一些代码)都没有注意到。 如果您只能通过视图和一些存储过程访问原始表,那么您可以开始破解结构。

代码(没有sql)只是在应用程序和表之间插入ORM样式访问。 这是一个应该基于您的技能组合和应用程序组合的决定。

除非您能够并且准备将所有应用程序与此表的特定实现分离,否则您只是在抛光粪便。 没有必要花费宝贵的资源。