我写这篇回答感觉有点傻,因为我认为你应该能够从其他帖子中组合出完整的答案 - 但是这不是我所想到的任何一个问题的确切重复。
Stackoverflow上已经有涉及此问题的问题和答案 - 但是在我的搜索中,我只找到了一些不是线程安全的答案,大多数都在使用merge
。
有不同的问题和答案可以参考,例如我的回答在c#中向数据库添加多个参数化变量,您可以看到如何使用c#表值参数进行操作,以及Aaron Bertrand的答案在SQL Server中插入if条件,您可以看到如何创建一个安全的upsert - 但是我没有找到任何涵盖这个问题的完整答案 - 所以这里你可以找到:
首先,您需要在数据库中创建一个用户定义的表类型:
CERATE TYPE MyTableType AS TABLE
(
Column1 int NOT NULL,
Column2 int NOT NULL,
Column3 int NOT NULL,
PRIMARY KEY (Column1, Column2, Column3)
)
然后,您创建存储过程:
CREATE stp_UpsertMyTable
(
@MyTableType dbo.MyTableType readonly
)
AS
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE t
SET t.column4 = tvp.column4,
t.column5 = tvp.column5
FROM dbo.MyTable AS t
INNER JOIN @MyTableType AS tvp
ON t.Column1 = tvp.Column1
AND t.Column2 = tvp.Column2
AND t.Column3 = tvp.Column3;
INSERT dbo.MyTable(<ColumnsList>)
SELECT <ColumnsList>
FROM @MyTableType AS tvp
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.MyTable t
WHERE t.Column1 = tvp.Column1
AND t.Column2 = tvp.Column2
AND t.Column3 = tvp.Column3
);
COMMIT TRANSACTION;
GO
然后,C# 部分很简单:
DataTable dt = new DataTable();
dt.Columns.Add("Column1", typeof(int));
dt.Columns.Add("Column2", typeof(int));
dt.Columns.Add("Column3", typeof(int));
dt.Columns.Add("Column4", typeof(string));
dt.Columns.Add("Column5", typeof(string));
using (var con = new SqlConnection("ConnectionString"))
{
using(var cmd = new SqlCommand("stp_UpsertMyTable", con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@MyTable", SqlDbType.Structured).Value = dt;
con.Open();
cmd.ExecuteNonQuery();
}
}
现在您可以使用表值参数进行完整且安全的upsert操作,只需要在c#和sql server之间进行一次往返即可。