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

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

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

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

有帮助吗?

解决方案

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

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

其他提示

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

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

这一切都是我的头脑,所以不要剪切/粘贴/运行,但我认为总的想法就在那里。

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

我知道这是一个旧线程,但我制作了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

这解决了我们大型桌子的负载问题。

没有精灵尘埃,你知道。

在不知道有关正在传输的实际模式的细节的情况下,通用解决方案就像您描述的那样:将处理划分为多个插入并跟踪密钥。这是一种伪代码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

如果你想允许并行批处理并对密钥进行分区,那将会变得更加复杂。

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

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

两步过程

  • BCP OUT 数据从视图到文本文件
  • 使用批量大小参数将文本文件中的 BCP IN 数据放入表中

这看起来像是一个很好的工作' BCP

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top