使用存储过程在单个表中插入150列

本文关键字:插入 150列 单个表 存储过程 | 更新日期: 2023-09-27 18:13:24

我们要求只使用存储过程来更新/插入SQL表中的数据。

我可以创建更新150列的存储过程,但这需要我从对象中获取所有150列的值,并显式地将所有150列传递给存储过程。

谁能建议一种方法,我不需要传递所有150值显式

使用存储过程在单个表中插入150列

这里的其他答案显示了实现目标的其他方法。然而,我要提醒你不要使用它们,原因如下:

  1. 大多数都是脆性的。这意味着您有潜在的问题,这些问题不会在编译时显示出来,并且在许多情况下,除非对每个边缘情况都进行了适当的测试,否则不会显示出来。

  2. 大多数会导致相同或更多的代码。在更新/插入到主表之前,必须解析字符串或将xml文件转换为临时表,这样不会节省任何代码。

  3. 非标准方式意味着这个项目的下一个程序员将需要注意这样一个事实,即这个过程的操作与其他过程不同,而不是为了节省几行c#代码。

关键是,继续以通常的方式创建存储过程并以通常的方式调用它。大多数问题将在编译时或有限的测试用例中出现。此外,通过与其他代码保持相同的格式,您可以增加未来更改成功的可能性,而无需进行大量额外的工作。

您传递xml.

如果为行/列提供值,则更新它。如果没有提供值,则使用原始值。这可以通过CASE语句来完成。

这是一个北风的例子。

编辑:我在这里编写了一个#temp表。您可以尝试使用@variable表。我有时两者都做过。这"要看情况"。您可以直接从xml到真正的表(没有中间的#temp或@variable表),一些原始的MS示例就是这样做的。您需要尝试MERGE(如果您有2008及以上版本)或将INSERT/UPDATE语句包装在TRAN中。(我通常做的是首先分解xml,然后开始TRAN,插入/更新(或合并),然后提交。

Use Northwind
GO

IF OBJECT_ID('tempdb..#OrdersHolder') IS NOT NULL
begin
        drop table #OrdersHolder
end

CREATE TABLE #OrdersHolder
(
IdentityKey int not null identity (1001, 1), 
[OrderID] int, 
[ShippedDate] datetime,
[Freight] money
)

-- Declare XML variable
DECLARE @data XML;
-- Element-centered XML
SET @data = N'
<root>
    <Order>
        <OrderID>10248</OrderID>
        <ShippedDate>01/01/2001</ShippedDate>
        <Freight>33.33</Freight>
    </Order>
    <Order>
        <OrderID>10249</OrderID>
        <ShippedDate>02/02/2002</ShippedDate>
        <Freight>44.44</Freight>
    </Order>
    <Order>
        <OrderID>10250</OrderID>
        <Freight>55.55</Freight>
    </Order>
    <Order>
        <OrderID>10251</OrderID>
        <ShippedDate>09/09/2999</ShippedDate>
    </Order>
    <Order>
        <OrderID>-99999</OrderID>
        <ShippedDate>12/31/2222</ShippedDate>
        <Freight>333.00</Freight>
    </Order>
</root>
';

INSERT INTO #OrdersHolder ( [OrderID] ,  [ShippedDate] , [Freight] )
SELECT T.myEntity.value('(OrderID)[1]', 'INT') AS OrderID,
       T.myEntity.value('(ShippedDate)[1]', 'datetime') AS ShippedDate, 
       T.myEntity.value('(Freight)[1]', 'money') AS Freight
FROM @data.nodes('root/Order') AS T(myEntity)

;
select * from #OrdersHolder
/* Here is the magic */
Update dbo.[Orders] 
Set 
[ShippedDate] = CASE
                    WHEN holder.[ShippedDate] IS NOT NULL then holder.ShippedDate
                    ELSE realTable.ShippedDate
                END
,
[Freight] = CASE
                    WHEN holder.[Freight] IS NOT NULL then holder.Freight
                    ELSE realTable.Freight
                END
FROM    
    #OrdersHolder holder , dbo.[Orders] realTable
Where
    holder.OrderID = realTable.OrderID

/* Note, the OrderID will be incorrect because OrderID is IDENTITY , so I used -99999 to force a non match */
INSERT INTO dbo.[Orders] ( [ShippedDate] , [Freight] )
Select
[ShippedDate] = CASE
                    WHEN holder.[ShippedDate] IS NOT NULL then holder.ShippedDate
                    ELSE null
                END
,
[Freight] = CASE
                    WHEN holder.[Freight] IS NOT NULL then holder.Freight
                    ELSE null
                END
FROM    
    #OrdersHolder holder
Where
not exists ( select null from dbo.Orders innerRealTable where holder.OrderID = innerRealTable.OrderID )


/* Now show the real table data, 2 rows should have both [ShippedDate] and  [Freight] updated, 1 row just Freight and one row just the ShippedDate */
Select 
UpdateStatusFYI = 
CASE
    when realTable.OrderID = 10248 then 'Should be ShippedDate and Freight'
    when realTable.OrderID = 10249 then 'Should be ShippedDate and Freight'
    when realTable.OrderID = 10250 then 'Should be just Freight'
    when realTable.OrderID = 10251 then 'Should be ShippedDate'
    else 'unknown'
END
,
realTable.[OrderID], realTable.[ShippedDate] , realTable.[Freight]  from    #OrdersHolder holder join dbo.[Orders] realTable
    on holder.OrderID = realTable.OrderID 
/* Take a best shot at showing newly created data */
Select 
realTable.[OrderID], realTable.[ShippedDate] , realTable.[Freight]  from #OrdersHolder holder join dbo.[Orders] realTable
    on holder.ShippedDate = realTable.ShippedDate and holder.Freight = realTable.Freight 
Where
not exists ( select null from dbo.Orders innerRealTable where holder.OrderID = innerRealTable.OrderID )


IF OBJECT_ID('tempdb..#OrdersHolder') IS NOT NULL
begin
        drop table #OrdersHolder
end

不确定这是否是您想要的,但是您可以做的一件事是将一个参数中的所有值传递给存储过程(通过VARCHAR(MAX)或TEXT类型),与字符连接,说'|'(管道),然后在存储过程中将其拆分以更新每个列。这种方法有许多缺点(对于初学者来说,VARCHAR的大小有限,参数的顺序也不合适),但是如果没有进一步的细节,它可能是一个好的开始。

这在很大程度上取决于底层表的需求,如果没有列是空的,那么您可以创建一个像这样的SP:

    CREATE PROCEDURE usp_SomeUpdateProc (@PrimaryKeyID INT,
     @Column1 VARCHAR = NULL,
     @Column2 INT = NULL,
     ...)
    AS 
    UPDATE dbo.SomeTable
    SET Column1 = ISNULL(@Column1, Column1),
        Column2 = ISNULL(@Column2, Column2),
        ....
    WHERE PrimaryKeyID = @PrimaryKeyId

对于这种类型的存储过程,只需要@PrimaryKeyId参数,您只需要为正在更改的列的参数提供值。

传递一个表值作为参数,并使用MERGE语法。这就产生了一个强类型的、基于集合的解决方案。

SQL 2008给了我们tvp。通过这种方式,您可以像正在更新的表一样格式化参数。然后,使用MERGE,您只需将表中的所有列设置为TVP中的相应列。

TVP在应用程序中是强类型的,不需要任何字符串解析或动态SQL。

你可以看看Dapper。

它是一个'micro-ORM' -它可以处理大量的样板代码,从一个对象属性复制到一个DbCommands参数,而不会增加一些更大的orm的膨胀

感谢您的响应,我使用实体框架4,所以我使用映射存储程序与实体框架,然后创建的方法`public virtual ObjectResult> UpdateCaseFormID(可空id, string formId,可空usersId, string formDataSource, ....){

        ObjectParameter idParameter;
        if (id.HasValue)
        {
            idParameter = new ObjectParameter("Id", id);
        }`

和i使用Object将所有值显式地传递给上面的方法

在这种情况下,我宁愿使用数组(或复合变量)来传递值。但是需要注意的是,该数组类型应该保存所有150列数据类型的值。意味着在这150列数据类型中应该有一个主数据类型的数组。

。对于这种情况,您可以使用array或varchar2 .