使用 SQL Server 中的存储过程动态更新
Posted
技术标签:
【中文标题】使用 SQL Server 中的存储过程动态更新【英文标题】:Dynamically update with stored procedures in SQL Server 【发布时间】:2016-06-03 11:05:05 【问题描述】:我们有一张表如下:
CREATE TABLE dbo.Test (
StartDate DATETIME2 NOT NULL,
EndDate DATETIME2 NULL
Field1 NVARCHAR(50) NOT NULL,
Field2 NVARCHAR(50) NOT NULL,
etc.
)
如果我们插入一行,EndDate 总是 NULL。如果涉及更新,我们不会真正更新现有行,而是设置它的 EndDate 并插入一个新行。假设有一个用于插入和更新的存储过程。
DECLARE @newStartDate DATETIME2
SELECT @newStartDate = GETDATE()
UPDATE dbo.Test SET (EndDate = @newStartDate) WHERE StartDate = @someDate
INSERT INTO dbo.Test (StartDate, EndDate, Field1, Field2)
SELECT @newStartDate, NULL, Field1, Field2
FROM dbo.Test where StartDate = @someDate AND EndDate = @newStartDate
可能会发生我们需要设置其他开始/插入值而不是旧值的情况。可以按如下方式完成。
INSERT INTO dbo.Test (StartDate, EndDate, Field1, Field2)
SELECT @newStartDate, NULL, @parameter, Field2
FROM dbo.Test where StartDate = @someDate AND EndDate = @newStartDate
现在的主要问题是实际表包含超过 100 列,上述方法虽然可行,但很容易出错。
我正在寻找一种动态方法,其中插入语句动态获取参数值(如果存在)。
在 C# 中,我会做这样的事情。 existing
将是包含每一列的 Dictionary
,overrides
不是需要更改的默认值。
var existing= ..."SELECT TOP 1 * from dbo.Test"
foreach (var changeParameter in overrides)
existing[changeParameter.Key] = changeParameter.Value;
existing
中的所有值现在都存储为它们应该用于插入的值。因此我可以构建一个新的插入语句。
这种方法在数据库上是否可行?我不想加载所有数据行只是为了立即将其写回。我宁愿直接在数据库上做所有事情。
【问题讨论】:
“我正在寻找一种动态方法,其中插入语句动态获取参数值” - 为什么不只是动态构建插入 SQL? @stuartd 当然可以,但我该怎么做呢?我想在数据库上构建插入语句。如何决定是否可以动态地从旧行中获取参数“@field1”或 Field1? 我是否更正了您想要具有insert (cols) values (vals)
的 sql 脚本,其中 cols list 和 vals 列表将包含一些值数组,这些值是从别的地方传过来的?这是你的动态部分吗?一长串可能不同的列?
@IvanStarostin 是的,传递参数的列表可能很长,主要目标是从参数中选择值(如果提供),或者从上一行中选择。
@Dani 所以参数集具有恒定数量的元素,但其中一些是空值(并且此插入可能放置在存储过程中)或 元素数量 传递变量和insert
脚本是在客户端构建的临时查询?
【参考方案1】:
我正在寻找一种动态方法,其中插入语句 如果存在,则动态获取参数值。
这是我一直这样做的方式:
通过将默认值设为 NULL 来使参数可选。
如果没有提供参数值,则COALESCE
带有您要使用的字段的参数:
INSERT INTO dbo.Test (StartDate, EndDate, Field1, Field2)
SELECT @newStartDate, NULL, COALESCE(@parameter,Field1), Field2
FROM dbo.Test where StartDate = @someDate AND EndDate = @newStartDate
就这么简单。
编辑:
如果您使用表值参数并加入它,那么您的 COALESCE
看起来像这样,如果 TVP 中每个字段有 1 列:
COALESCE(tvp.Field1, Test.Field1)
或者,如果您将 TVP 作为 FieldName、FieldValue 对的垂直表传递,那么您将不得不使用 CASE 表达式或子查询,如下所示:
COALESCE((SELECT FieldValue FROM TVP WHERE FieldName='Field1'),Field1)
【讨论】:
谢谢,唯一的问题是我的表有超过 270 列,那将是一个相当多的参数列表... 在这种情况下,您可以将表值参数 JOIN 传递给它,并使用相同的技术。 “加入”是什么意思?我认为我没有正确理解它。你有例子吗? JOINing 表是一个基本的 SQL 操作,它过于宽泛,无法在 SO 答案中教授。如果你 googleSQL JOIN Tutorial
你会发现很多例子。
我知道 JOIN 是什么,但我不明白加入表值参数如何解决我的问题。【参考方案2】:
回答你的问题:
我正在寻找一种动态方法,其中插入语句 如果存在,则动态获取参数值。这种方法是 以某种方式可能在数据库上?
要在 SQL 服务器中获得 T-SQL 动态,您必须使用 sp_executesql,更多信息:
Execute Dynamic SQL commands in SQL Server
您必须将字段参数作为某种数组传递。有几种方法,这里是一种方法:
Passing array to a SQL Server Stored Procedure
您的解决方案将涉及使用CURSOR 来构建动态 Sql 查询,请参见示例:
SQL Server Cursor Example
【讨论】:
请看我上面的评论 您已经在 C# 示例中通过提及键/值对回答了这个问题。您必须传递给存储过程字段和值组合的内容。【参考方案3】:不确定 c# 如何解析查询参数,但您可以执行以下操作:
var sql = "
declare @param1 int = :param1,
@param2 int = :param2,
@param3 date = :param3,
@somedate datetime = :somedate,
@newstartdate datetime = :newstartdate,
...
insert into dbo.Test (...full list of columns)
select isnull(@param1, field1), isnull(@param2, field2), ...and so on
from dbo.Test
where StartDate = @someDate AND EndDate = @newStartDate
"
然后将值或NULL分配给所有参数。 ISNULL
将执行此操作,如果 new 为 NULL,您将拥有 新值 或 旧值。但是需要一些技巧才能完全放置 null。
或
您必须在运行时在客户端构建整个查询
或
按照@TabAlleman 的建议传递带有参数名称和值的 TVP,并检查此表是否存在与表中特定列同名的参数。然后继续ISNULL
或其他东西来选择是保留现有值还是从 TVP 中获取传递。
【讨论】:
以上是关于使用 SQL Server 中的存储过程动态更新的主要内容,如果未能解决你的问题,请参考以下文章