我必须更新一个包含超过270个更新字段的大表。
我是.NET的新手,需要建议在这种情况下使用什么更好:SqlCommand,某种内存映射表或DataSet,或者它可能使用来自DB的元数据存在某种自动生成的对象?请帮忙。
原因: 我有一个旧的大型Delphi7应用程序,其中一部分负责监听socket上的一些数据包,这些数据包被编组到大型结构中,最后一步存储在DB中。现在我将这部分移植到新的C#服务中,至少实际上我必须保留相同的逻辑。 问题是结构是BIG(超过220个字段),存储它的表有近300个字段。从我的220个字段的结构中扣除/计算其他~50个字段,所有都应该在DB中更新。 实际的Delphi代码是丑陋的,它像桌子本身一样长了几年,就像这样:
'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等。 实际上我无法更改数据库结构..所以我只能在代码中播放,我不确定是否有必要翻译代码。表用于某些报告和统计信息。一些计算/扣除的字段必须处理源代码中的一些常量。
使用了dev-tools: MS SQL Server 2000,C#.net2.0,VS2008
答案 0 :(得分:2)
最简单的解决方案适用于此,因为ole db的工作方式是使用字符串。所以,为了传递270,500,1000个参数,我所做的只是传递一个字符串,一个包含270个参数的字符串可能远低于2kb ......这在现代计算中...继承1 ...不有性能损失。这里有一个xml解决方案,但这只是苹果和橙子,你仍然传递字符串,但是需要额外的代码来处理xml。所以......你的架构应该是这样的:
具有270个输入参数的SQL服务器上的存储过程:
Create Procedure sp_Example1
(@param1 [type], @param2 [type], @param3 [type], etc...)
AS
BEGIN
[SQL statements]
END
具有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));
请记住,您仍在进行相当密集的操作,但您的基准测试应该是旧的应用程序。如果它有点糟糕,我不会担心它,因为框架需要更多的计算开销。
我不知道为什么它不会格式化代码......
答案 1 :(得分: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
现在添加此存储过程:
/ * 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
'
<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
答案 2 :(得分:0)
使用Simple.data可以简化您的代码和逻辑(尽管它需要.NET 4.0)
答案 3 :(得分:0)
您可以将表拆分为新表,然后创建与连接,交换,转换等旧表相同名称的视图,以将新表转换为报表的旧结构。 / p>
如果你使用命令(比如你发布的Delphi代码),请使用参数来防止SQL注入。
使用现有的DB结构,您可以使用开箱即用的ORM,因为您需要映射大量的列。您可以将POCO类创建为类型安全模型,然后使用数据符号或自定义属性使映射更简单,然后从属性中动态创建SQL命令。
答案 4 :(得分:0)
没有特别的兔子可以从这个.net帽子里拿出来。我害怕。
缺乏周围的复杂性&#34;知道&#34;
,只有一些完全独立的字段已经改变,只为它们构建一个更新语句。即使知道这样可以更好地存储,因为blob并不能真正帮助你。在任何情况下都可能不是真的。
参数化查询或存储过程在代码中看起来有点整洁,但无论如何都可以在delphi中完成。
没有办法从这里说它应该怎么做,但是一个可能有里程数的想法是将当前表隐藏在除一小部分函数之外的所有内容中。
例如,如果要重命名它,然后使用当前名称创建视图。读取的东西和(可能是写入它的一些代码)都没有注意到。 如果你只能通过视图和一些存储过程访问原始表,那么你可以开始破解结构。
代码(无sql)只是在应用程序和表之间插入ORM样式访问。 这个决定应该基于你的技能和应用程序的组合而不是其他任何东西。
除非您能够并且准备将所有应用程序与此表的特定实现分离,否则您只是在抛光粪便。没有必要花费宝贵的资源。