如何在Microsoft SQL Server中将数据从一个表复制到另一个表

时间:2015-04-16 07:57:11

标签: c# sql-server database sql-server-2012

我正在开展一项非常复杂的工作,我确实需要你的建议。 我需要将数据从一个表复制到另一个表,我知道对于这种方法,有很多解决方案,如

如果两者都是相同的架构

INSERT INTO newTable
SELECT * FROM oldTable

如果两者都是不同的架构

INSERT INTO newTable (col1, col2, col3)
SELECT column1, column2, column3
FROM oldTable

也可以使用SQL Cursor,并且确实存在更多变体。 但让我总结一下这个问题:

问题

我有一个包含大约的CSV文件。 150万条记录。每条记录目前有6个正在导入的字段。

现在,要将CSV文件中的数据插入SQL服务器,我将C#与实体框架结合使用。

为了提高性能,我会将所有这些记录插入临时表中。 这是临时表的模式:

CREATE TABLE [dbo].[TEMP_GENERIC_ARTICLE](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [GlnCode] [nvarchar](100) NULL,
    [Description] [nvarchar](max) NULL,
    [VendorId] [nvarchar](100) NULL,
    [VendorName] [nvarchar](100) NULL,
    [ItemNumber] [nvarchar](100) NULL,
    [ItemUOM] [nvarchar](max) NULL,
    [DateCreatedInternal] [datetime] NOT NULL,
    [DateUpdatedInternal] [datetime] NOT NULL,
 CONSTRAINT [PK_dbo.TEMP_GENERIC_ARTICLE] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)

然后我有一个另一个应用程序将使用的表,称为T_GENERIC_ARTICLE,其架构是:

CREATE TABLE [dbo].[T_GENERIC_ARTICLE](
    [GlnCode] [nvarchar](100) NOT NULL,
    [Description] [nvarchar](max) NULL,
    [VendorId] [nvarchar](100) NOT NULL,
    [VendorName] [nvarchar](100) NULL,
    [ItemNumber] [nvarchar](100) NOT NULL,
    [ItemUOM] [nvarchar](128) NOT NULL,
 CONSTRAINT [PK_dbo.T_GENERIC_ARTICLE] PRIMARY KEY CLUSTERED 
(
    [GlnCode] ASC,
    [VendorId] ASC,
    [ItemNumber] ASC,
    [ItemUOM] ASC
)

因此,真实表格不再具有“ID”字段,并且主键上有4列数据库。

现在,我想做什么:

只要数据存储在Temp表中,或者每1000条记录存储一次,我就需要运行一个SQL存储过程,它将数据从temp表复制到{ {1}}表。

复制后,我需要检查该主键是否已存在的记录。如果是这种情况,那么我想更新记录,否则我想插入一条新记录。

完成复制后,我想删除destination表中的所有记录。

问题

如果能够以尽可能快的速度将记录从temp转移到temp表,那么这么大的数据集(150万条记录)的最佳工作方法是什么?

我从未使用过如此庞大的数据集,所以我真的需要一些建议。

亲切的问候

1 个答案:

答案 0 :(得分:1)

使用MERGE将数据从登台表复制到主表,处理1.5米行应该没有问题:

MERGE [dbo].[T_GENERIC_ARTICLE] AS t
USING [TEMP_GENERIC_ARTICLE] AS s
    ON s.GlnCode = t.GlnCode
    AND s.VendorId = t.VendorId
    AND s.ItemNumber = t.ItemNumber
    AND s.ItemUOM = t.ItemUOM
WHEN MATCHED THEN UPDATE    
    SET Description = s.Description,
        VendorName = s.VendorName
WHEN NOT MATCHED THEN
    INSERT (GlnCode, Description, VendorId, VendorName, ItemNumber, ItemUOM)
    VALUES (s.GlnCode, s.Description, s.VendorId, s.VendorName, s.ItemNumber, s.ItemUOM);

SSIS适用于插入,但它不能很好地处理upsert,因为OLEDB命令任务操作RBAR,因此插入登台表并使用MERGE比尝试upsert更有效直接到主表。


修改

回答评论中的问题:

  

在插入每条记录之前进行检查吗?还是仅在第一次进行检查?我的意思是,如果merge命令首先插入一条记录,对于下面的记录,它会检查它是否再次存在于目标表中(因为从上一个合并语句中添加了一条新记录)?

它首先进行检查,但是如果源表中有两个匹配的行,则MERGE将失败并显示错误消息

  

MERGE语句尝试多次更新或删除同一行。当目标行与多个源行匹配时会发生这种情况。 MERGE语句不能多次更新/删除目标表的同一行。优化ON子句以确保目标行最多匹配一个源行,或使用GROUP BY子句对源行进行分组。

如果临时表中有重复的行,则需要在执行合并之前对它们进行重复处理,因此,如果您只想要临时表中的最新记录,那么可以使用NOT EXISTS exlcude记录相同键的新记录:

WITH LatestTempGenericArticle AS
(
    SELECT  GlnCode, Description, VendorId, VendorName, ItemNumber, ItemUOM
    FROM    dbo.[TEMP_GENERIC_ARTICLE] AS t
    WHERE   NOT EXISTS
            (   SELECT  1
                FROM    dbo.[TEMP_GENERIC_ARTICLE] AS t2
                WHERE   t2.GlnCode = t.GlnCode
                AND     t2.VendorId = t.VendorId
                AND     t2.ItemNumber = t.ItemNumber
                AND     t2.ItemUOM = t.ItemUOM
                AND     t2.DateCreatedInternal > t.DateCreatedInternal
            )
)
MERGE [dbo].[T_GENERIC_ARTICLE] AS t
USING LatestTempGenericArticle AS s
    ON s.GlnCode = t.GlnCode
    AND s.VendorId = t.VendorId
    AND s.ItemNumber = t.ItemNumber
    AND s.ItemUOM = t.ItemUOM
WHEN MATCHED THEN UPDATE    
    SET Description = s.Description,
        VendorName = s.VendorName
WHEN NOT MATCHED THEN
    INSERT (GlnCode, Description, VendorId, VendorName, ItemNumber, ItemUOM)
    VALUES (s.GlnCode, s.Description, s.VendorId, s.VendorName, s.ItemNumber, s.ItemUOM);