7

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

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

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

'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

4

5 回答 5

2

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

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

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

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

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

于 2013-02-27T22:56:50.470 回答
1

好的。由于您可以添加一个新的存储过程,我建议将所有值打包,并将其作为 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

现在添加这个存储过程:

/* 美国药典 */

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
'
<TitlesDS>
    <Titles>
        <title_id>PN3333</title_id>
        <title>Peanut Cooking</title>
        <type>trad_cook</type>
        <pub_id>0877</pub_id>
        <price>3.33</price>
        <advance>4444.00</advance>
        <royalty>1</royalty>
        <ytd_sales>33</ytd_sales>
        <notes>Peanut Cooking Notes</notes>
        <pubdate></pubdate>
    </Titles>

    <Titles>
        <title_id>SSMS4444</title_id>
        <title>Sql Server Management Studio</title>
        <type>programming</type>
        <pub_id>0877</pub_id>
        <price>13.33</price>
        <advance>5444.00</advance>
        <royalty>2</royalty>
        <ytd_sales>33</ytd_sales>
        <notes>Sql Server Management Studio Notes</notes>
        <pubdate></pubdate>
    </Titles>   

</TitlesDS>
'
, 0
于 2013-02-27T22:31:30.507 回答
0
  1. 您可以将表拆分为新表,然后创建与旧表同名的视图,用于连接、切换、强制转换等,以将新表转换为报表的旧结构。

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

  3. 使用当前的数据库结构,您使用开箱即用的 ORM 可能会很累,因为您有大量的列要映射。您可以将 POCO 类创建为类型安全模型,然后使用数据表示法或自定义属性来简化映射,然后根据属性动态创建 SQL 命令。

于 2013-02-27T22:55:42.053 回答
0

恐怕没有什么特别的兔子可以从.net 帽子中拔出来。

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

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

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

没有办法从这里说它应该如何完成,但一个可能有里程的想法是将当前表隐藏在除了一小组函数之外的所有内容中。

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

仅代码(无 sql)将在应用程序和表之间插入 ORM 样式访问。这是一个应该基于您的技能组合和应用程序组合的决定,而不是其他任何事情。

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

于 2013-02-27T23:02:10.957 回答
0

使用Simple.data可以简化您的代码和逻辑(尽管它需要 .NET 4.0)

于 2013-02-27T22:40:52.373 回答