【问题标题】:Loop over and run SQL query several thousand times循环运行 SQL 查询数千次
【发布时间】:2018-03-16 19:41:55
【问题描述】:

我已经阅读了几十篇帖子,其中很多都可以追溯到几年前,但无法想出一种现代、安全和可靠的方法来将数千条记录中的特殊值作为单个查询进行更新。

我遍历表中的所有记录,根据一些特殊逻辑确定一个 DateTime 值,然后运行这个简单的查询来更新该值……超过 3500 次。那是很多次的线路。

UPDATE ScheduleTickets
    SET ScheduledStartUTC = @ScheduledStartUTC
    WHERE ScheduleId = @ScheduleId AND PatchSessionId = @PatchSessionId

我已经看到 cmets 通过保存和使用 DataTable 来不浪费内存。我见过使用 StringBuilder 动态创建更新查询的解决方案,但感觉不安全/肮脏。当然,整个过程不到一分钟,但一定有更好的方法。

所以,在弄清楚 DateTime 值之后,我调用...

UpdateScheduleTicketStart(ScheduleId, PatchSessionId, scheduledDateTime);

看起来像这样...

private static void UpdateScheduleTicketStart(long scheduleId, long patchSessionId, DateTime scheduledStartUTC)
{
    using (SqlConnection c = ConnectVRS())
    {
        SqlCommand cmd = new SqlCommand(@"
            UPDATE ScheduleTickets
                SET ScheduledStartUTC = @ScheduledStartUTC
                WHERE ScheduleId = @ScheduleId AND PatchSessionId = @PatchSessionId
            ", c);
        cmd.Parameters.Add("@ScheduleId", SqlDbType.BigInt).Value = scheduleId;
        cmd.Parameters.Add("@PatchSessionId", SqlDbType.BigInt).Value = patchSessionId;
        cmd.Parameters.Add("@ScheduledStartUTC", SqlDbType.VarChar).Value = scheduledStartUTC;
        cmd.ExecuteNonQuery();
    }
}

如何在一次调用中将所有值传递给 SQL Server,或者如何创建单个 SQL 查询以一举完成更新?

【问题讨论】:

  • 循环遍历什么表?
  • 可能是表值参数?也许重新思考如何确定要使用的正确值?没有一些细节很难说。
  • 我正在阅读这篇文章,因为您不想有一个循环(某处)并为每个插入调用您的方法?您的回答将与 Stackoverlflow 问题有关:stackoverflow.com/questions/20635796/bulk-update-in-c-sharp
  • 折衷方案是执行上述代码的操作,只需在单个事务中进行,而不是每次都建立新连接。
  • @MobyDisk 这就是我在问题中提出的问题。我想在单个事务中执行 3500 多条记录的更新。每个都有一个不同的 DateTime 值,但我还需要传递两个值来确定要更新表中的哪一行。这两个值构成一个唯一键。

标签: c# sql .net sql-server sql-server-2016


【解决方案1】:

很多人建议使用 TableValueParameter,我同意这将是一个好方法。以下是您如何做到这一点的示例:

首先在 SQL Server 中创建 TVP 和存储过程

CREATE TYPE [dbo].[SchdeuleTicketsType] As Table
(
    ScheduledStartUTC DATETIME NOT NULL
  , ScheduleId        INT      NOT NULL
  , PatchSessionId    INT      NOT NULL
)


CREATE PROCEDURE [dbo].[usp_UpdateTickets]
(
   @ScheduleUpdates As [dbo].[SchdeuleTicketsType] Readonly
)
   AS
   Begin
        UPDATE t1
        SET t1.ScheduledStartUTC = t2.ScheduledStartUTC
        FROM ScheduleTickets AS t1
            INNER JOIN @ScheduleUpdates AS t2
        ON t1.ScheduleId = t2.ScheduleId AND
           t1.PatchSessionId  = t2.PatchSessionId 
   End
)

下一步修改您的代码以填充表并将其作为参数传递给存储过程:

    private void Populate()
    {
        DataTable dataTable = new DataTable("SchdeuleTicketUpdates");

        //we create column names as per the type in DB 
        dataTable.Columns.Add("ScheduledStartUTC", typeof(DateTime));
        dataTable.Columns.Add("ScheduleId", typeof(Int32));
        dataTable.Columns.Add("PatchSessionId", typeof(Int32));

        //write you loop to populate here

        //call the stored proc
        using (var conn = new SqlConnection(connString))
        {
            var command = new SqlCommand("[usp_UpdateTickets]");
            command.CommandType = CommandType.StoredProcedure;

            var parameter = new SqlParameter();
            //The parameter for the SP must be of SqlDbType.Structured 
            parameter.ParameterName = "@ScheduleUpdates";
            parameter.SqlDbType = System.Data.SqlDbType.Structured;
            parameter.Value = dataTable;
            command.Parameters.Add(parameter);
            command.ExecuteNonQuery();

        }
    }

【讨论】:

  • 我同意 TVP 是一个好方法,是我多年前所做的事情的唯一方法。只需要说服数据库人员允许它。我看到每个人都建议使用 DataTable,想知道为什么我在其他线程中看到这么多 cmets 来避免这种情况。
  • 使用 DataTable 有不同的方法。大多数人可能会不喜欢 DataAdapter 方法,但不是 TVP 方法。
【解决方案2】:

如果值在另一个表中,请使用join

UPDATE st
    SET ScheduledStartUTC = ot.ScheduledStartUTC
    FROM ScheduleTickets st JOIN
         OtherTable ot
         ON st.ScheduleId = ot.ScheduleId AND st.PatchSessionId = ot.PatchSessionId;

你不指定特殊的逻辑,但你可以用 SQL 来表达它。

【讨论】:

  • 这是另一个人写的一个很大的while循环。它有效,所以此时不​​想破坏它。以后会考虑的。
  • 好吧,我会给 Gordon 一个 +1,因为您的问题是 如何在一次调用中将所有值传递给 SQL Server,或者如何创建单个 SQL 查询来执行一下子就更新了? 这个查询就是这样做的。
  • 我不会。他说 C# 应用程序中有特殊的逻辑来计算日期。如果该逻辑不移植到 SQL Server 怎么办?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-18
  • 1970-01-01
  • 1970-01-01
  • 2015-10-17
  • 1970-01-01
  • 2017-12-10
相关资源
最近更新 更多