7

我有几个大表(188m 和 144m 行)我需要从视图中填充,但每个视图都包含几亿行(将伪维度建模数据组合成一个平面形式)。每个表上的键是超过 50 个复合字节的列。如果数据在表中,我总是可以考虑使用 sp_rename 来创建另一个新表,但这不是一个真正的选择。

如果我执行单个 INSERT 操作,该过程会占用大量事务日志空间,通常会将其归档并引发 DBA 的一堆麻烦。(是的,这可能是 DBA 应该处理/设计/架构师的工作)

我可以使用 SSIS 并通过批量提交将数据流式传输到目标表中(但这确实需要通过网络传输数据,因为我们不允许在服务器上运行 SSIS 包)。

除了使用某种键将行分成不同的批次并执行循环将过程划分为多个 INSERT 操作之外,还有什么其他的方法吗?

4

6 回答 6

7

视图是否有任何类型的唯一标识符/候选键?如果是这样,您可以使用以下方法将这些行选择到工作表中:

SELECT key_columns INTO dbo.temp FROM dbo.HugeView;

(如果有意义,可以将此表放入不同的数据库,可能使用 SIMPLE 恢复模式,以防止日志活动干扰您的主数据库。无论如何,这应该生成更少的日志,并且您可以释放空间恢复之前的其他数据库,以防问题是您周围的磁盘空间不足。)

然后你可以做这样的事情,一次插入 10,000 行,并在两者之间备份日志:

SET NOCOUNT ON;

DECLARE
    @batchsize INT,
    @ctr INT,
    @rc INT;

SELECT
    @batchsize = 10000,
    @ctr = 0;

WHILE 1 = 1
BEGIN
    WITH x AS
    (
        SELECT key_column, rn = ROW_NUMBER() OVER (ORDER BY key_column)
        FROM dbo.temp
    )
    INSERT dbo.PrimaryTable(a, b, c, etc.)
        SELECT v.a, v.b, v.c, etc.
        FROM x
        INNER JOIN dbo.HugeView AS v
        ON v.key_column = x.key_column
        WHERE x.rn > @batchsize * @ctr
        AND x.rn <= @batchsize * (@ctr + 1);

    IF @@ROWCOUNT = 0
        BREAK;

    BACKUP LOG PrimaryDB TO DISK = 'C:\db.bak' WITH INIT;

    SET @ctr = @ctr + 1;
END

这完全不在我的脑海中,所以不要剪切/粘贴/运行,但我认为总体思路就在那里。有关更多详细信息(以及为什么我在循环内备份日志/检查点),请参阅sqlperformance.com上的这篇文章:

请注意,如果您正在执行常规数据库和日志备份,您可能需要完整备份以重新启动您的日志链。

于 2009-10-21T17:35:15.700 回答
5

您可以对数据进行分区并将数据插入游标循环中。这与 SSIS 批量插入几乎相同。但是在您的服务器上运行。

create cursor ....
select YEAR(DateCol), MONTH(DateCol) from whatever

while ....
    insert into yourtable(...)
    select * from whatever 
    where YEAR(DateCol) = year and MONTH(DateCol) = month
end
于 2009-10-21T17:23:43.640 回答
4

我知道这是一个旧线程,但我制作了 Arthur 光标解决方案的通用版本:

--Split a batch up into chunks using a cursor.
--This method can be used for most any large table with some modifications
--It could also be refined further with an @Day variable (for example)

DECLARE @Year INT
DECLARE @Month INT

DECLARE BatchingCursor CURSOR FOR
SELECT DISTINCT YEAR(<SomeDateField>),MONTH(<SomeDateField>)
FROM <Sometable>;


OPEN BatchingCursor;
FETCH NEXT FROM BatchingCursor INTO @Year, @Month;
WHILE @@FETCH_STATUS = 0
BEGIN

--All logic goes in here
--Any select statements from <Sometable> need to be suffixed with:
--WHERE Year(<SomeDateField>)=@Year AND Month(<SomeDateField>)=@Month   


  FETCH NEXT FROM BatchingCursor INTO @Year, @Month;
END;
CLOSE BatchingCursor;
DEALLOCATE BatchingCursor;
GO

这解决了我们的大表负载问题。

于 2013-12-09T16:57:41.387 回答
2

没有精灵尘埃,你知道的。

在不了解正在传输的实际模式的细节的情况下,通用解决方案将与您描述的完全一样:将处理划分为多个插入并跟踪键。这是一种伪代码 T-SQL:

create table currentKeys (table sysname not null primary key, key sql_variant not null);
go

declare @keysInserted table (key sql_variant);
declare @key sql_variant;
begin transaction
do while (1=1)
begin
    select @key = key from currentKeys where table = '<target>';
    insert into <target> (...)
    output inserted.key into @keysInserted (key)
    select top (<batchsize>) ... from <source>
    where key > @key
    order by key;

    if (0 = @@rowcount)
       break; 

    update currentKeys 
    set key = (select max(key) from @keysInserted)
    where table = '<target>';
    commit;
    delete from @keysInserted;
    set @key = null;
    begin transaction;
end
commit

如果您想允许并行批处理并对键进行分区,它会变得更加复杂。

于 2009-10-21T17:30:06.527 回答
1

您可以使用 BCP 命令加载数据并使用 Batch Size 参数

http://msdn.microsoft.com/en-us/library/ms162802.aspx

两步过程

  • 从视图到文本文件的 BCP OUT 数据
  • BCP IN 数据从文本文件到具有批量大小参数的表中
于 2009-10-21T17:29:21.957 回答
1

这看起来像是一份好的 ol' BCP工作。

于 2009-10-21T17:29:34.543 回答