Обновление большой таблицы (много столбцов). C#.NET

Мне нужно обновить большую таблицу с более чем 270 полями обновления.

Я относительно новичок в .NET и должен посоветовать, что лучше использовать в этом случае: SqlCommand, какая-то таблица или набор данных, сопоставленные с памятью, или, может быть, существуют какие-то автоматически сгенерированные объекты, использующие метаданные из БД? Пожалуйста помочь.

причина: У меня есть старое приложение big Delphi7, часть которого отвечает за прослушивание на сокете некоторых пакетов, которые маршалируются в большие структуры и, в конечном итоге шаг, хранящийся в БД. Теперь я портирую эту часть на новый сервис C#, и, по крайней мере, я должен сохранить ту же логику. Проблема в том, что структура большая (более 220 полей), а таблицы, в которых она хранится, имеют около 300 полей. Из моей структуры 220 полей вычитаются / вычисляются другие ~50 полей, и все они должны обновляться в БД. Фактический код 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-инструменты: В MS SQL сервер 2000, на C# .net2 версии.0, VS2008

5 ответов


здесь применяется самое простое решение, потому что 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));
    

помните, что вы все еще делаете довольно интенсивную операцию, но вашим эталоном должно быть старое приложение. Если это немного хуже, я бы не беспокоился об этом, так как структура требует больше вычислительных накладных расходов.

Я понятия не имею, почему это не будет форматировать код...


Ok. Поскольку вы можете добавить новую хранимую процедуру, я бы предложил упаковать все значения и отправить их в виде 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 "пабов" здесь:

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

используя простой.данные может упростить ваш код и логику (хотя для этого требуется .NET 4.0)


  1. вы можете разделить таблицы на новые таблицы, а затем создать представления с тем же именем, что и старые таблицы, которые объединяются, переключаются, отливки и т. д., чтобы преобразовать новые таблицы в старые структуры для отчетов.

  2. Если вы используете команды (например, в опубликованном коде Delphi), используйте параметры для предотвращения SQL-инъекции.

  3. С текущей структурой БД у вас есть использование из коробки ORM может быть утомительным, так как вы есть множество столбцов для сопоставления. Вы можете создать классы POCO как типобезопасную модель, затем использовать обозначения данных или пользовательские атрибуты, чтобы упростить сопоставление, а затем создавать команды SQL на лету из атрибутов.


нет специального кролика, чтобы вытащить из шляпы .net для этого, я боюсь.

за исключением сложностей вокруг "знания", только некоторые полностью независимые поля изменились и построили оператор обновления только для них, вы набиты.

даже зная, что это было бы лучше хранить как blob, на самом деле не поможет вам. Скорее всего, это не так.

параметризованный запрос или хранимая процедура будут выглядеть немного аккуратнее в коде но это можно было сделать и в Дельфах.

нет способа сказать отсюда, как это должно быть сделано, но одна идея, которая может иметь пробег, скрывает текущую таблицу от всего, кроме небольшого набора функций.

например, если вы были, чтобы переименовать его, а затем создать представление с текущим именем. Ни одно из чтения материала и (возможно, хороший бит кода, который пишет на него) не заметит. Если вы можете добраться до точки, где таблицу можно подняться только по мнению и некоторые хранимые процедуры, затем вы можете начать взламывать структуру.

код(без sql) будет заключаться только в вставке доступа в стиле ORM между приложением (приложениями) и таблицей. Это решение, которое должно быть основано на вашем наборе навыков и сочетании приложений больше, чем что-либо еще.

Если вы не можете и не готовы отделить все приложения от конкретного имплемента, который является этой таблицей, вы просто полируете фекалии. Нет смысла тратить ценные ресурсы для этого.