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將使用表輸入源) 。

相關問題