OLEDB 源任务是否将所有行传递给 OLE DB 命令任务,并且 OLE DB 命令任务一次处理一条记录?

Does the OLEDB source task pass all the rows to the OLE DB Command task, and the OLE DB Command task processes one record at a time?

我有一个包含 2 个组件的数据流任务:

  1. 作为 SELECT 查询的 OLEDB 数据源任务:
SELECT ACCOUNTID
FROM JOBS
WHERE STATUS=3
  1. OLE DB 命令任务:
DELETE FROM ACCOUNT
WHERE ACCOUNTID=?

逻辑是状态 ID 为 3 的任何作业都必须导致从帐户 table 中删除帐户 ID。

我知道当步骤 1 returns 多条记录时,步骤 2 执行速度较慢,因为它是对每条记录进行的操作。就好像我在一个单独的 table 中暂存了步骤 1 中的数据,然后在一个执行 sql 任务中根据暂存的 table 触发删除,那么它会更快。

由于行数 returns 总是很小(低于 20),我使用 OLE DB 命令任务方法。我的问题是 -

  1. OLEDB 源任务是否将每一行传递给 OLE DB 命令任务,从而导致 OLEDB 命令任务一次处理一条记录?

或者 OLEDB 源任务是否将所有行传递给 OLE DB 命令任务,并且 OLE DB 命令任务一次处理一条记录?

  1. 一旦行被 OLEDB 源任务提取,那么在传递到 OLE DB 命令任务之前它们保存在哪里?

  2. 在 OLE DB 命令任务完成之前,OLEDB 源任务生成的行是否已锁定?

OLE DB 源任务是一个 non-blocking 对象,不会部分(semi-blocking 组件)或全部(阻塞组件)保留数据。因此,它们在执行时间方面更有效率。

如果您使用 SQL 服务器,则在页面级别引入数据库快照。可能是在 SQL 查询开始插入之前,它曾经拍摄整个源查询的快照,然后开始插入到目标数据库中。 源服务器上的每个 update/insert 事务都可能承担页面复制操作的开销以维护快照,尤其是在 SSMS 2017 中。

坏习惯:在源中添加 NOLOCK。

您可以查看此博客: https://blog.sqlauthority.com/2015/07/03/sql-server-difference-between-read-committed-snapshot-and-snapshot-isolation-level/

C# 控制台应用程序(用 Core 3.1 编写,但应适用于 Framework 4.8+)以异步方式 运行 您的请求。这不能放入 SSIS 中的脚本任务中。这与您原来的 SSIS 包没有太大区别(即加载要删除的帐户然后处理删除)。但是,主要区别在于所有删除同时发生,而不是一次一个。

如果合适,您也可以考虑使用“select 不同的帐户 ID”。无需处理相同的 accountID 两次。

    public static async Task Main()
    {
        List<int> accts = await getAccts(3);
        await Task.WhenAll(accts.Select(async a => await deleteAccts(a)));
    }

    public static async Task deleteAccts(int acct)
    {
        string sql = "DELETE FROM ACCOUNT WHERE ACCOUNTID=@acct";

        using (var conn = new SqlConnection("Insert your connection string"))
        {
            using (var cmd = new SqlCommand(sql, conn))
            {
                cmd.Parameters.Add("@acct", SqlDbType.Int).Value = acct;
                conn.Open();
                await cmd.ExecuteNonQueryAsync();
            }
        }
    }

    public static async Task<List<int>> getAccts(int Status)
    {
        List<int> accts = new List<int>();
        
        string sql = "SELECT ACCOUNTID FROM JOBS WHERE STATUS=@Status";

        using (var conn = new SqlConnection("Insert your connection string"))
        {
            using (var cmd = new SqlCommand(sql,conn))
            {
                cmd.Parameters.Add("@Status", SqlDbType.Int).Value = Status;
                conn.Open();
                using (var rdr = await cmd.ExecuteReaderAsync())
                {
                    while (await rdr.ReadAsync())
                    {
                        accts.Add(rdr.GetInt32(0));
                    }
                }
            }
        }

        return accts;
    }

我会留下 C# 答案以供参考,但似乎不需要暂存或使用任何代码。您可以在单个 SQL 语句中执行此操作。

Delete from ACCOUNT
  join jobs on jobs.AccountID = Account.AccountID and jobs.status=3

唯一的限制是这些表在同一台服务器上。

第一个问题

Does the OLEDB source task pass each row into the OLE DB Command task, thereby resulting in the OLEDB Command task processing one record at a time? Or Does the OLEDB source task pass all the rows to the OLE DB Command task, and the OLE DB Command task processes one record at a time?

OLE DB 源从 SQL 数据库中提取数据并将它们存储在内存缓冲区中。有几个因素会影响数据加载到 SSIS 的方式:

  1. 提取的数据行数
  2. 每行存储的数据量
  3. DefaultBufferMaxRowsDefaultBufferSize数据流任务properties values

另一方面,OLE DB 命令将数据 row-by-row 处理为 mentioned in the official documentation:

The OLE DB Command transformation runs an SQL statement for each row in a data flow.

第二题

Until the completion of the OLE DB Command task, are the rows produced by the OLEDB Source task locked?

如前所述,数据在内存缓冲区内传输。一旦 OLE DB 命令处理的行数和大小满足数据流任务的缓冲区配置,它们就会被发送到下一个组件。当然,这比从 OLE DB 源中提取数据要慢,因为数据已处理 row-by-row.

此外,在处理数据时,OLE DB 命令没有从 OLE DB 源接收任何新数据。它不像 排序转换 。它只接收一个缓冲区,处理它,并通过输出发送它,而不是将所有数据存储在内存中并进行处理。

以下屏幕截图显示了 OLE DB 组件如何在结束处理当前缓冲区之前不接收新的数据缓冲区 (我在一个巨大的 table 上使用更新语句而没有本例中的索引):


有用的资源