【问题标题】:Insert Into Select Statement Needing Transactions插入需要事务的 Select 语句
【发布时间】:2019-08-28 03:06:06
【问题描述】:

我有一个存储过程,它收集来自各地的 ETL 数据,最后尝试将它们全部连接到一个大表中。大约 100 列宽,3500 万行。

当我从临时表中提取并将它们全部连接在一起时,插入查询可能需要数小时、页面到磁盘等。它对于我的环境来说太大了。

Insert Into tbl_huge
    Select Distinct a, b, c, d, e, f, g, h, i, j, k
    from (Mess of Subqueries & #tmp_tbls) 

批处理此插入以一次提交 100k 行或其他内容的最佳方法是什么?数据中没有好的自然键可以将其分解成半均匀的,而且我担心无论如何这都是真的。

我已经看到使用目标表中不存在的地方的不同示例,但这似乎是错误的方法,不会继续扩展和增长。

那么这里最好的方法是什么?对结果进行排序并在 while 循环中多次重新执行 Select & insert 查询,保持计数器知道我需要执行 rows > x?

有没有更好的方法,要么允许我准确地选择要插入的子集,要么将选择预处理到内存中(+页面文件由于大小)并将其读回以插入块中?

【问题讨论】:

  • 我首先要看的是您是否可以重新组织您的(Mess of Subqueries & #tmp_tbls),从而消除对SELECT DISTINCT 的需求。如果有什么导致你消耗资源,那就是它。
  • 我的强烈建议是don’t use a stored procedure,而是使用您选择的应用程序语言在数据库外组装数据更改,然后一次发送 10K 行

标签: sql sql-server


【解决方案1】:

ETL 是“提取、转换和加载”。您只需一步完成所有这些操作,而这需要 3 个步骤。

您确实需要在转换数据之前“着陆”(提取)数据,然后将其加载到单个巨大的表中。

为您的(Mess of Subqueries & #tmp_tbls) 创建固定表(不是#temp)以将数据提取到其中。
将原始数据加载到这些表中(如果您的所有数据都在一台服务器或同一个数据库中,则可以跳过此步骤)

然后将数据转换为插入所需的“形状”。

完成后,加载它。

如果其中任何步骤花费的时间太长,您可以查看对单个步骤进行分块

这需要更多的工作,但更强大。

编辑:通常以这种方式加载数据时,它基于日期(即将所有昨天的数据移动到仓库中),如果这是这里问题的一部分,您可以更频繁地运行加载。每小时甚至每 5 分钟一次

【讨论】:

  • 即使我获取了所有数据,我也不确定在从一堆连接中完成插入时如何分块?我会遇到基本相同的问题,不是吗?
  • 这是一个有效的观点,但我们不知道是否有必要。可能是将数据加载到临时表中需要花费所有时间并且从临时表中插入会非常快? (尽管具有 35M 行的宽表可能会出现问题)但是通过将其拆分为多个语句和步骤,您可以隔离问题所在并调整查询的这些部分。
【解决方案2】:

您可以按照以下步骤进行批量插入

  1. 创建一个带有 RowNumber 标识列的临时表以获得以下结果集。
CREATE TABLE #tempTableToHoldResultSet(RowNumber Int IDENTITY(1,1), OtherColumns...)
  1. 将结果集插入到第 5 步创建的临时表中。 1
INSERT INTO #tempTableToHoldResultSet(othercolumns...)
SELECT DISTINCT a, b, c, d ... FROM (Mess of Subqueries & #tmp_tbls) 
  1. 现在,使用 While 循环执行批量插入。您可以使用 Stackoverflow link 使用 TRY CATCH TRANSACTION 管理
DECLARE @minRowNumber INT = 1
DECLARE @batchsize INT = 10000 
DECLARE @maxRowNumber INT
SET @maxRowNumber = @minRowNumber + @batchSize 

WHILE EXISTS (SELECT * FROM #tempTableToHoldResultSet WHERE RowNumber >=  
  @minRowNumber AND RowNumber < @maxRowNumber)
BEGIN 
SET XACT_ABORT, NOCOUNT ON

DECLARE @starttrancount int

BEGIN TRY
    SELECT @starttrancount = @@TRANCOUNT

    IF @starttrancount = 0
        BEGIN TRANSACTION

INSERT INTO tbl_huge
SELECT a,b,c,d... FROM #tempTableToHoldResultSet     
WHERE RowNumber >= @minRowNumber AND RowNumber < @maxRowNumber;
SET @minRowNumber = @maxRowNumber
SET @maxRowNumber = @minRowNumber + @batchSize

IF @starttrancount = 0 
        COMMIT TRANSACTION
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 AND @starttrancount = 0 
        ROLLBACK TRANSACTION;
    THROW;
    --before SQL Server 2012 use 
    --RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
END
GO
  1. 数据加载完成后,可以DROP临时表。
-- Drop the temporary table, after data load is completed
DROP TABLE #tempTableToHoldResultSet 
GO

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-17
    • 1970-01-01
    • 1970-01-01
    • 2016-09-01
    • 1970-01-01
    • 2021-01-11
    • 1970-01-01
    相关资源
    最近更新 更多