2

我想将大量数据从一个表复制到另一个表。我在存储过程中使用游标来做同样的事情。但它只适用于记录较少的表。如果表包含更多记录,它会长时间执行并且挂了。请给一些建议,我怎样才能更快地复制数据,我的SP如下:

--exec uds_shop

--select * from CMA_UDS.dbo.Dim_Shop
--select * from UDS.dbo.Dim_Shop
--delete from CMA_UDS.dbo.Dim_Shop

alter procedure uds_shop
as
begin
declare @dwkeyshop int
declare @shopdb int
declare @shopid int
declare @shopname nvarchar(60)
declare @shoptrade int
declare @dwkeytradecat int
declare @recordowner nvarchar(20)
declare @LogMessage varchar(600)

Exec CreateLog 'Starting Process', 1 

DECLARE cur_shop CURSOR FOR
    select 
    DW_Key_Shop,Shop_ID,Shop_Name,Trade_Sub_Category_Code,DW_Key_Source_DB,DW_Key_Trade_Category,Record_Owner
    from 
    UDS.dbo.Dim_Shop 

    OPEN  cur_shop
    FETCH NEXT FROM cur_shop INTO @dwkeyshop,@shopid,@shopname,@shoptrade, @shopdb ,@dwkeytradecat,@recordowner
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
    Set @LogMessage = ''
    Set @LogMessage = 'Records insertion/updation start date and time : ''' + Convert(varchar(19), GetDate()) + ''''            

    if (isnull(@dwkeyshop, '') <> '')
        begin 
        if not exists (select crmshop.DW_Key_Shop from CMA_UDS.dbo.Dim_Shop as crmshop where (convert(varchar,crmshop.DW_Key_Shop)+CONVERT(varchar,crmshop.DW_Key_Source_DB)) = convert(varchar,(CONVERT(varchar, @dwkeyshop) + CONVERT(varchar, @shopdb))) )
        begin
        Set @LogMessage = Ltrim(Rtrim(@LogMessage)) + ' ' + 'Record for shop table is inserting...'         
                insert into 
                 CMA_UDS.dbo.Dim_Shop
                (DW_Key_Shop,DW_Key_Source_DB,DW_Key_Trade_Category,Record_Owner,Shop_ID,Shop_Name,Trade_Sub_Category_Code)
                values
                (@dwkeyshop,@shopdb,@dwkeytradecat,@recordowner,@shopid,@shopname,@shoptrade)
        Set @LogMessage = Ltrim(Rtrim(@LogMessage)) + ' ' + 'Record successfully inserted in shop table for shop Id : ' + Convert(varchar, @shopid) 

            end
            else

            begin 
             Set @LogMessage = Ltrim(Rtrim(@LogMessage)) + ' ' + 'Record for Shop table is updating...' 
             update 
             CMA_UDS.dbo.Dim_Shop
             set DW_Key_Trade_Category=@dwkeytradecat,
             Record_Owner=@recordowner,
             Shop_ID=@shopid,Shop_Name=@shopname,Trade_Sub_Category_Code=@shoptrade

             where       
                DW_Key_Shop=@dwkeyshop and DW_Key_Source_DB=@shopdb     
             Set @LogMessage = Ltrim(Rtrim(@LogMessage)) + ' ' + 'Record successfully updated for shop Id : ' + Convert(varchar, @shopid)
            end
        end
        Exec CreateLog @LogMessage, 0
        FETCH NEXT FROM cur_shop INTO @dwkeyshop,@shopid,@shopname,@shoptrade, @shopdb ,@dwkeytradecat,@recordowner
    end
    CLOSE cur_shop 
    DEALLOCATE cur_shop
End
4

4 回答 4

5

假设targetTabledestinationTable具有相同的架构...

INSERT INTO targetTable t
SELECT * FROM destinationTable d
WHERE someCriteria

除非没有其他方法(很少见),否则避免使用游标。

您可以使用该WHERE子句过滤掉任何重复记录。

如果您有标识列,请使用不包含标识列的显式列列表。

您还可以尝试禁用约束并删除索引,前提是您在之后替换它们(并确保检查约束)。

如果您使用的是 SQL Server 2008(更高版本),则可以使用该MERGE语句。

于 2012-09-09T06:44:45.007 回答
1

根据我的个人经验,当您将大量数据从一个表复制到另一个表(具有类似约束)时,请删除要复制数据的表上的约束。复制完成后,再次恢复所有约束。

在我的情况下,我可以将复制时间从 7 小时减少到 30 分钟(1 亿条记录,有 6 个约束)

于 2012-09-09T06:44:12.947 回答
0

众所周知,光标速度很慢,对于非常大的数据集,内存可能会开始成为问题。

看起来您确实在每次迭代中都做了很多记录,所以您可能会被光标卡住,但我会寻找一种方法将工作分解为多个调用,以便您可以保持较小的占用空间。

如果你有一个自动编号列,我会在过程中添加一个'@startIdx bigint',并重新定义你的游标语句以获取'TOP 1000''WHERE [autonumberFeild] <= @startIdx Order by [autonumberFeild]'。然后创建一个新的存储过程,例如:

DECLARE @startIdx bigint = 0
WHILE select COUNT(*) FROM <sourceTable> > @startIdx
BEGIN
    EXEC <your stored procedure> @startIdx
END
SET @startIdx = @startIdx + 1000

此外,请确保您的数据库文件设置为自动增长,并且它会以较大的增量进行,因此您不会将所有时间都花在增长数据文件上。

于 2012-09-09T07:00:31.750 回答
0
INSERT INTO targetTable 
SELECT * FROM destinationTable 
WHERE someCriteria (based on Criteria you can copy/move the records)
于 2013-09-11T12:28:28.793 回答