【问题标题】:Build Where Clause Dynamically in Ado.net C#在 Ado.net C# 中动态构建 Where 子句
【发布时间】:2018-10-28 23:19:14
【问题描述】:

我将在给定时间接收大约 1000 条记录,我必须确定它们是现有记录还是新记录。

如果它们存在,我必须更新记录,如果是新记录,则只需插入它们。我不知道它们中的任何一个是否会存在,或者它们是否都会存在。

我认为最好对数据库进行一次查询并尝试查找它们中的任何一个是否存在于数据库中并将它们存储在内存中并检查内存中的集合并进行检查。

最初有人告诉我,我 1 个字段就足以确定唯一性。所以我以为我可以针对数据库中的 1 个字段执行 1 个大 in 子句,但现在我发现情况并非如此,我需要使用 3 个字段来确定记录是否存在或现在。

这基本上是一个and子句

select * from where columnA = "1" and ColumnB = "2" and ColumnC = "3"

如何在 C# ado.net 中正确解决这个问题?

我猜我需要有一些超级 where 子句?

select * from where (columnA = "1" and ColumnB = "2" and ColumnC = "3") or  (columnA = "4" and ColumnB = "5" and ColumnC = "6") or [....998 more conditional clauses)

如果可能的话,我愿意接受更好的想法。我仍然认为一次性完成比执行 1000 个单独的查询要好。

【问题讨论】:

  • 您使用的是什么 rdbms?例如,SQL Server 支持表值参数,因此您应该能够使用单个存储过程完成所有操作。 (Read here 关于如何以线程安全的方式进行操作)
  • 将使用 sql serer 2017 但在本地必须使用 2014,因为没有 Windows 10。
  • 那么我建议使用表值参数而不是动态创建 where 子句。大量关于 stackoverflow 和一般网络的示例。
  • 所以这个表值参数 SP 将返回给我的 C# 重复的结果?
  • 是的,很容易。您也可以在存储过程中执行 upsert,而不必先返回 c#。

标签: c# sql-server ado.net


【解决方案1】:

我只能帮助您为您的请求编写查询

        var recordCount = 1000;
        var query = "SELECT * FROM TableName WHERE";
        for (var i = 1; i < recordCount - 2; i += 3)
        {
            query += " (columnA = " + i + " and ColumnB = " + (i + 1) + " and ColumnC = " + (i + 2) + ") or ";
        }

【讨论】:

  • 是的,我想(但多么希望)它看起来不会像那样。尽管我一直认为这可能会像查询一样慢,因为它可能必须查看数据库中每一行的所有 1000 个“或”语句。
【解决方案2】:

我觉得写这个答案有点傻,因为我认为你应该能够将其他帖子的完整答案放在一起 - 但这并不是我想到的任何一个问题的完全重复。

Stackoverflow 中已经有问题和答案处理这个问题 - 但是在我的搜索中,我只找到了不是线程安全的答案,其中大多数都使用merge

我可以向您推荐不同的问题和答案,例如 我对Adding multiple parameterized variables to a database in c# 的回答,您可以在其中看到如何在c# 上使用表值参数,以及Aaron Bertrand 对Using a if condition in an insert SQL Server 的回答,您可以在其中看到如何创建安全的upsert - 但是我没有找到任何涵盖的答案这完全 - 所以你去:

首先你需要在你的数据库中创建一个用户定义的表类型:

CERATE TYPE MyTableType AS TABLE
(
     Column1 int NOT NULL,
     Column2 int NOT NULL,
     Column3 int NOT NULL,
     -- rest of the columns in your table goes here
     PRIMARY KEY (Column1, Column2, Column3)
)

然后,创建存储过程:

CREATE stp_UpsertMyTable
(
    @MyTableType dbo.MyTableType readonly -- table valued parameters must be readonly
)
AS

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION;

UPDATE t 
SET t.column4 = tvp.column4,
    t.column5 = tvp.column5 -- and so on for all columns that are not part of the key
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;

 -- Note: <ColumnsList> should be replaced with the actual columns in the table
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));

// Fill your data table here

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 之间只有一次往返。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-08
    • 2016-12-13
    • 1970-01-01
    • 2023-03-29
    相关资源
    最近更新 更多