我们要求只使用存储过程来更新/插入 SQL 表中的数据。
我可以创建将更新 150 列的存储过程,但这将要求我需要从我拥有的对象中获取所有 150 列值并将所有 150 列显式传递给存储过程。
谁能建议我不必明确传递所有 150 个值的方法?
我们要求只使用存储过程来更新/插入 SQL 表中的数据。
我可以创建将更新 150 列的存储过程,但这将要求我需要从我拥有的对象中获取所有 150 列值并将所有 150 列显式传递给存储过程。
谁能建议我不必明确传递所有 150 个值的方法?
这里的其他答案显示了实现目标的替代方法。但是,出于几个原因,我会提醒您不要使用它们。
大多数都很脆。这意味着您有潜在的问题不会显示编译时间,并且在许多情况下,除非每个边缘情况都经过正确测试,否则不会出现。
大多数会产生相同或更多的代码。在更新/插入主表之前,您不必通过解析字符串或将 xml 文件转换为临时表来保存任何代码。
非标准方式意味着该项目的下一个程序员将需要注意这个过程与其他过程的操作方式不同的事实,除了保存几行 C# 代码之外没有其他原因。
重点是,继续以通常的方式创建您的存储过程并以通常的方式调用它。大多数问题将出现在编译时或有限的测试用例中。此外,通过保持与其他代码相同的格式,您增加了未来更改成功的可能性,而无需大量额外工作。
你通过xml。
如果您为行/列提供值,则更新它。如果没有为您提供任何值,请使用原始值。这可以通过 CASE 语句来完成。
这是一个罗斯文的例子。
编辑:我在这里编写了一个#temp 表。您可以尝试使用 @variable 表。我有时都做过。它“取决于”。您可以直接从 xml 转到真实表(没有中间 #temp 或 @variable 表),一些原始的 MS 示例就是这样做的。您想尝试 MERGE(如果您有 2008 年及以上版本)或将 INSERT/UPDATE 语句包装在 TRAN 中。(我通常做的是先切碎 xml,然后开始 TRAN、插入/更新(或 MERGE),然后是 COMMIT。
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
很大程度上取决于基础表的要求,如果没有任何列可以为 Nullable,那么您可以像这样创建和 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 参数,您只需为正在更改的列的参数提供值。
不确定这是否是您想要的,但您可以做的一件事是将一个参数中的所有值传递给存储的过程(通过 VARCHAR(MAX) 或 TEXT 类型),并与一个字符连接,例如“|” (管道)然后在存储过程中将其拆分以更新每一列。这种方法有许多缺点(对于初学者来说,VARCHAR 的大小有限和参数的正确顺序),但如果没有进一步的细节,它可能是一个好的开始。
感谢您的回复,我使用的是实体框架 4,所以我使用了 Mapped the Stored proc。使用实体框架,然后创建方法,如 `public virtual ObjectResult> UpdateCaseFormID(Nullable id, string formId, Nullable usersId, string formDataSource, ....) {
ObjectParameter idParameter;
if (id.HasValue)
{
idParameter = new ObjectParameter("Id", id);
}`
我使用 Object 将所有值显式传递给上述方法
你可以看看Dapper。
它是一个“微 ORM”——它可以为您处理从对象属性复制到 DbCommands 参数的大量样板代码,而不会增加一些更大的 ORM 的膨胀。
将表值作为参数传递并使用MERGE
语法。这会产生一个强类型、基于集合的解决方案。
SQL 2008 为我们提供了 TVP。通过这种方式,您可以像要更新的表一样格式化参数。然后,使用MERGE
您只需将表中的所有列设置为 TVP 中的相应列。
TVP 是来自应用程序的强类型,不需要任何字符串解析或动态 SQL。
在这种情况下,我宁愿使用数组(或复合变量)来传递值。但您需要注意此数组类型应保存所有 150 列数据类型的值。意味着您应该在这 150 个列数据类型中拥有主数据类型数组。
出色地 。在这种情况下,您可以使用数组或 varchar2 ..