0

当我从不同的数据源(Json文件,其他数据库和REST API)导入它们时,我需要对各种数据进行重复数据删除,首先将它们加载到一个表中,该表定义它们的类型并将数据存储为Json,稍后当我运行批处理时,我可以查找类型并将数据插入到合适的表中。导入行的数量是不同的(每种类型都有不同的表格/表格),但总是超过100万个(如果我使用VARCHAR(MAX)将它们以Json格式放置在单个表格中,那么这些数据总共会达到10G以上)。在SQL Server中执行批量插入忽略并返回id的最佳方法是什么?

正如我所说,我需要处理重复,所以我尝试定义唯一索引的目标表,并启用Ignore Duplicate Keys,这“仅仅”当我插入现有数据提出了警告。问题是,这只适用于少数情况。大多数情况下,我需要使用5+ varchar(255)字段,并且由于限制(900字节,src),我无法将它们添加到唯一索引。

我在挣扎的另一件事是在批量插入期间,我需要插入关系数据,这意味着一个表将有外键到另一个表。所以首先我需要处理依赖关系,并且在获得插入的Ids后,使用那些我可以插入数据的Ids。就像一个产品有一个制造商,所以我首先插入当前批次中的所有制造商名称,然后使用这些Ids我可以插入产品。

我想需要在查询返回的ID,做重复数据删除的结果来实现:

  • 将同时运行,通过8-16螺纹
  • 应该返回插入标识
  • 应该只插入数据如果它之前没有被另一个线程插入(或根本没有插入)

首先,我试图通过使存储过程如下所示来处理这个问题:

  1. 尽量选择数据,如果找到,返回的Id
  2. 如果没有找到,重新开始交易
  3. 检查,如果它已经得到了由另一个线程插入。
  4. 如果不是,插入并返回新的ID。

代码示例这个.:

CREATE PROCEDURE [dbo].usp_insert_pdproductdetails 
    @GDDataSourceVersionId INT, 
    @ManufacturerNameId BIGINT, 
    @ManufacturerReference NVARCHAR(255), 
    @PropertiesJson NVARCHAR(MAX), 
    @OriginalContentPage NVARCHAR(MAX), 
    @NewId BIGINT OUT 
AS 
BEGIN 
    SET NOCOUNT ON; 

    SELECT @NewId = [Id] FROM PDProductDetails 
    WHERE GDDataSourceVersionId = @GDDataSourceVersionId AND 
      ManufacturerId = @ManufacturerNameId AND 
      ManufacturerReference = @ManufacturerReference; 

    IF @NewId IS NULL 
    BEGIN 
     SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 
     BEGIN TRANSACTION 
      SELECT @NewId = [Id] FROM PDProductDetails 
      WHERE GDDataSourceVersionId = @GDDataSourceVersionId AND 
        ManufacturerId = @ManufacturerNameId AND 
        ManufacturerReference = @ManufacturerReference; 
      IF @NewId IS NULL 
      BEGIN 
       INSERT INTO PDProductDetails (GDDataSourceVersionId, ManufacturerId, ManufacturerReference, PropertiesJson, OriginalContentPage) 
       VALUES(@GDDataSourceVersionId, @ManufacturerNameId, @ManufacturerReference, @PropertiesJson, @OriginalContentPage); 
       SELECT @NewId = SCOPE_IDENTITY(); 
      END 
     COMMIT TRANSACTION 
    END 

    SELECT @NewId; 
END 
GO 

的多个线程会调用这个并插入产品的详细信息。但是,使用这个我真的很快僵死了。我改了不同的方法,使用合并:

CREATE PROCEDURE [dbo].usp_insert_pdproductdetails 
    @GDDataSourceVersionId INT, 
    @ManufacturerNameId BIGINT, 
    @ManufacturerReference NVARCHAR(255), 
    @PropertiesJson NVARCHAR(MAX), 
    @OriginalContentPage NVARCHAR(MAX), 
    @NewId BIGINT OUT 
AS 
BEGIN 
    SET NOCOUNT ON; 

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 
    BEGIN TRANSACTION; 
     MERGE 
     INTO [dbo].[PDProductDetails] T 
     USING (SELECT @GDDataSourceVersionId, @ManufacturerNameId, @ManufacturerReference, @PropertiesJson, @OriginalContentPage) 
      AS Source (GDDataSourceVersionId, ManufacturerNameId, ManufacturerReference, PropertiesJson, OriginalContentPage) 
     ON T.GDDataSourceVersionId = Source.GDDataSourceVersionId AND 
      T.ManufacturerId = Source.ManufacturerNameId AND 
      T.ManufacturerReference = Source.ManufacturerReference 
     WHEN NOT MATCHED THEN 
      INSERT (GDDataSourceVersionId, ManufacturerId, ManufacturerReference, PropertiesJson, OriginalContentPage) 
      VALUES(Source.GDDataSourceVersionId, Source.ManufacturerNameId, 
       Source.ManufacturerReference, Source.PropertiesJson, Source.OriginalContentPage); 
    COMMIT TRANSACTION; 

    SELECT @NewId = [Id] FROM PDProductDetails (NOLOCK) 
    WHERE GDDataSourceVersionId = @GDDataSourceVersionId AND 
      ManufacturerId = @ManufacturerNameId AND 
      ManufacturerReference = @ManufacturerReference; 

    SELECT @NewId; 
END 
GO 

这总是合并行,后来选择。它仍然陷入僵局,并不像其他人那么快,但仍然如此。

如何才能实现插入忽略并返回插入的id功能,这不会在并发环境中发生死锁?

+0

请无效询问多个不同的问题。这是目前太宽泛的海事组织。海事组织你应该分开你有两个问题。 –

+0

@TT。我认为这是一个单一的问题。问我怎样才能以并发方式插入忽略,同时还返回Ids。 – appl3r

+0

嗯......在你的第一个问题之后,你去一个与_挣扎的另一件事。这是两个不同的问题。 –

回答

0

在@ ta.speot.is提到你可以做OUTPUT合并,我搜索了如何将它分配给一个变量和一个answer mentioned it

我用这个存储过程。:

CREATE PROCEDURE [dbo].usp_insert_pdproductdetails 
    @GDDataSourceVersionId INT, 
    @ManufacturerNameId BIGINT, 
    @ManufacturerReference NVARCHAR(255), 
    @PropertiesJson NVARCHAR(MAX), 
    @OriginalContentPage NVARCHAR(MAX), 
    @NewId BIGINT OUT 
AS 
BEGIN 
    SET NOCOUNT ON; 

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 
    BEGIN TRANSACTION; 
     MERGE 
     INTO [dbo].[PDProductDetails] T 
     USING (SELECT @GDDataSourceVersionId, @ManufacturerNameId, @ManufacturerReference, @PropertiesJson, @OriginalContentPage) 
      AS Source (GDDataSourceVersionId, ManufacturerNameId, ManufacturerReference, PropertiesJson, OriginalContentPage) 
     ON T.GDDataSourceVersionId = Source.GDDataSourceVersionId AND 
      T.ManufacturerId = Source.ManufacturerNameId AND 
      T.ManufacturerReference = Source.ManufacturerReference 
     WHEN MATCHED THEN 
      UPDATE SET @NewId = T.Id 
     WHEN NOT MATCHED THEN 
      INSERT (GDDataSourceVersionId, ManufacturerId, ManufacturerReference, PropertiesJson, OriginalContentPage) 
      VALUES(Source.GDDataSourceVersionId, Source.ManufacturerNameId, 
       Source.ManufacturerReference, Source.PropertiesJson, Source.OriginalContentPage); 

    SET @NewId = ISNULL(@NewId, SCOPE_IDENTITY()); 

    COMMIT TRANSACTION; 

    SELECT @NewId; 
END 
GO 

编辑:作为@ ta.speot.is提到的,它会更好,使用表值参数,使一批的要求,使用相同的方法(MERGE将使用表输入源) 。

相关问题