2016-10-14 133 views
0

我正在使用Merge語句更新並從我的表格中的web api(以數據表格的形式)插入批量記錄。插入和更新工作正常,但當我發送一個損壞的值.Catch塊不處理該值。這裏是我的代碼:捕獲塊沒有處理該錯誤

//Creating type to fetch the data table from web api(have to go with this approach only) 

    CREATE TYPE [dbo].[CustomerType] AS TABLE(
      [Id] [int] NULL, 
      [Name] [nvarchar](100) NULL, 
      [Country] [nvarchar](50) NULL, 
      [Date] [datetime] NULL 
     ) 

    // Stored proc for update and insert using merge 

    CREATE PROCEDURE Update_Customers 
      @tblCustomers CustomerType READONLY 
    AS 
    BEGIN 

    BEGIN TRY 

      MERGE INTO Customers c1 
      USING @tblCustomers c2 
      ON c1.CustomerId=c2.Id 
      WHEN MATCHED THEN 
      UPDATE SET c1.Name = c2.Name 
       ,c1.Country = c2.Country, 
       c1.date =c2.date 
      WHEN NOT MATCHED THEN 
      INSERT VALUES(c2.Id, c2.Name,c2.date, c2.Country); 

    END TRY 


    BEGIN CATCH 

    //My table for logging the error 
    INSERT INTO ERROR_LOG(ERROR_LINE,ERROR_MESSAGE,PROC_NAME) 
    VALUES (ERROR_LINE(),ERROR_MESSAGE(),ERROR_PROCEDURE) 

    END CATCH 

    END 

    Thanks in advance 
+0

是什麼導致了一個損壞的值? SQL不像您給出的信用那樣嚴格。如果它的髒數據,沒有發生。爲什麼首先出現錯誤?您的BEGIN TRAN在哪裏?爲什麼你不能限制合併中的值?你將SQL視爲一行行語言......它是關係型的。 –

+0

@ clifton_h-我將50條記錄傳遞給數據表,並在一條記錄中傳遞帶有額外空間的日期(2016-10-15)。我的存儲過程未在ERROR_LOG表中記錄該錯誤(在catch塊中使用)。你能幫助其他方法嗎?我需要將錯誤插入該表中。 – paemmi

+0

爲什麼你需要這個錯誤日誌?轉換插入了錯誤數據的數據會不會更簡單?爲什麼你需要「捕捉」這個插入錯誤?修復源代碼,或使表格健壯。請記住,這是一個關係表。在將其插入表格之前驗證您的數據。 –

回答

0

問題是SQL不會按照您認爲的方式處理錯誤。

  1. SQL Server保持ACID狀態。

原子:全部或全部。所有的工作都被分解爲事務性陳述,這些陳述必須成功或者整個修改/創建被還原,或者回滾到以前的工作狀態。通過使用BEGIN TRANSACTION/COMMIT TRANSACTION或使用ROLLBACK TRANSACTION,您可以明確說明某件作品的交易。 Read More: Transaction StatementsROLLBACK TRANSACTION

一致性:每個事務都必須使SQL Server處於有效狀態。例如,雖然DROP TABLE MyTable;可能是有效的事務,但如果MyTable具有依賴關係(即Foreign Key Constraints),則SQL Server將處於不一致狀態,並且會將事務回滾到上一個一致狀態。

隔離:每筆交易發生在自己的時間和空間。我們說序列號具體。隔離功能允許在服務器上同時執行多個甚至相似的語句,並且每個語句都獨佔一個。術語阻止指當語句正在等待事務提交併且死鎖是兩個事務正在無限期地彼此等待時。

耐用性:與存在於內存中的軟件程序不同,該程序可能因突然斷電而丟失,SQL Server的事務對於磁盤一旦提交就永久存在。這提供了一個聲明的最終結果。這也是LOG文件到位的地方,因爲它記錄了數據庫上執行的事務。 READ MORE: ACID PROPERTIES

自從您的BEGIN TRY/CATCH塊查找這些問題後,我提到了所有這些。

  1. 對待你的表作爲數據集

召回SQL是基於關係集合理論。當它可以對組/數據/對象集執行操作時,SQL是最好的。它與繼承完全不兼容,並且使用草寫邏輯是可能的,但效果不佳。 因此,在您的ETL過程中,將數據視爲整個集合。

相反,通過處理你的數據作爲一個整體設置,您可以將用戶/網絡接口的次要拼寫錯誤/錯誤,並隔離您希望使用謂詞子句來編目數據類型的實際誤差(WHERE/HAVING/ON

的你可以做什麼的一個例子是類似以下內容:

CREATE TABLE #ETLTable(ID  INT   NOT NULL 
        , Name  VARCHAR(50) NOT NULL 
        , Comment VARCHAR(50) NULL 
        , Country VARCHAR(50) NOT NULL 
        , Dated  VARCHAR(50)); --implicitly declared as allowing NULL values unless the type denies it 

CREATE TABLE #MyTable (ID  INT   NOT NULL 
        , Name  VARCHAR(50) NOT NULL 
        , Comment VARCHAR(50) NULL 
        , Country VARCHAR(50) NOT NULL 
        , Dated  DATE); 

CREATE TABLE #ERROR_TABLE (ObjectID  INT NULL 
         , encrypted INT 
         , text   VARCHAR(MAX) 
         , start_time DATETIME2 
         , Table_ID  INT 
         , Table_Name VARCHAR(50) 
         , Table_Country VARCHAR(50) 
         , Table_Dated VARCHAR(20)); 

CREATE TABLE #ACTIONS ([Action] VARCHAR(50) 
          , [inserted_ID]  int 
          , inserted_Name  VARCHAR(50) 
          , inserted_Country VARCHAR(50) 
          , inserted_Date  Date 
          , deleted_ID   int 
          , deleted_Name  VARCHAR(50) 
          , deleted_Country  VARCHAR(50) 
          , deleted_Date  Date) 

INSERT INTO #MyTable (ID, Name, Country, Dated) 
VALUES (1, 'Mary', 'USA', '12/23/12') 
    , (2, 'Julio', 'Mexico', '12/25/12') 
    , (3, 'Marx', 'USA', '11/11/12') 
    , (4, 'Ann', 'USA', '11/27/12'); 

INSERT INTO #ETLTable(ID, Name, Country, Comment, Dated) 
VALUES (1,'Mary', 'USA', 'Valid Date', '12/23/12') 
    , (2,'Julio', 'Mexico', 'Invalid Date', '12-25,12') 
    , (3,'Marx', 'USA', 'Valid but incorrect Date', '12-11/25') --this actually means YY-MM-DD 
    , (4,'Ann','USA', 'Not Matching Date', '12-23-12') 
    , (5, 'Hillary', 'USA', 'New Entry', '11/24/12'); 

/*SQL Server is fairly flexible to datatypes entries, so be explicit. 
Note the date highlighted. This will fail your code since it is of datatype DATETIME. CAST is implicit anyways, and should not be depended on in important queries. Theoretically, you could catch this with your TRY/CATCH block, but the entire Merge statement would be rolled back...an expensive and probably unacceptable cost. 
    You should proof your INSERTIONS of errors before you start expensive transactions like the MERGE statement. In this example, I knew what dates were being entered and that only the Japanese version might be entered. Dates are very finicky (DATE has no format) and best handled outside the merge statement altogether. */ 
;WITH CTE AS (
SELECT ID, Name, Country, Comment, ISNULL(TRY_CAST(Dated AS Date), CAST(CONVERT(datetime, Dated, 11) AS DATE)) AS Dated --TRY_CAST returns a NULL if it cannot succeed and will not fail your query. 
       FROM #ETLTable 
       WHERE ISDATE(Dated) = 1 
      ) 

MERGE INTO #MyTable TGT 
    USING CTE SRC ON TGT.ID = SRC.ID 
        AND TGT.Name = SRC.Name 
WHEN MATCHED AND SRC.Dated > TGT.Dated 
    THEN UPDATE SET TGT.Dated = SRC.Dated 
        , TGT.Comment = SRC.Comment 
WHEN NOT MATCHED BY TARGET 
    THEN INSERT(ID, Name, Country, Comment, Dated) VALUES (SRC.ID, SRC.Name, SRC.Country, SRC.Comment, SRC.DATED) 
    OUTPUT $action AS [Action] 
     , inserted.ID 
     , inserted.Name 
     , inserted.Country 
     , inserted.Dated 
     , deleted.ID 
     , deleted.Name 
     , deleted.Country 
     , deleted.Dated 
    INTO #Actions; 

/* Note, you would have to run this query separately, as it only records active transactions. */ 
--CREATE PROC MyProject 
--AS BEGIN 
--WAITFOR DELAY '00:00:10' 
--Print 'ME' 
--END 
;WITH CTE AS (

      SELECT t.objectid, encrypted, text, start_time 
      FROM sys.dm_exec_requests AS r 
      CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) AS t 
       INNER JOIN (SELECT object_id FROM sys.procedures 
          WHERE object_id = OBJECT_ID('MyProject')) B ON t.objectid = B.object_id) 

INSERT INTO #ERROR_TABLE (ObjectID, encrypted, text, start_time, Table_ID, Table_Name, Table_Country, Table_Dated) 
SELECT CTE.objectid, CTE.encrypted, CTE.text, CTE.start_time, B.Table_ID, B.Table_Name, B.Table_Country, B.Table_Dated 
FROM CTE 
RIGHT OUTER JOIN (SELECT ID AS Table_ID 
         , Name AS Table_Name 
         , Country AS Table_Country 
         , Dated AS Table_Dated 
       FROM #ETLTable 
       WHERE ISDATE(Dated) = 0) B ON 1 = 1 

SELECT * FROM #ERROR_TABLE 
SELECT * FROM #Actions 
SELECT * FROM #MyTable 

注意,腳本不破的SQL Server任何東西,其實分開壞輸入前Merge聲明。

  • 請注意#ERROR_TABLE中的有用信息。您實際上可以利用這些信息進行實時推薦。 這是您的錯誤追蹤的目標。
  • 提醒一下,瞭解您的RAISERRORTRY/CATCH只適用於整個交易......一個工作單元。如果您需要優雅地允許Merge語句失敗,那就好了。但要明白,適當的ETL 將保證這種昂貴的陳述永不失敗

因此,無論如何,請在您寫入最終表格之前執行所有ETL過程。這是臨時表的要點...所以你可以在你的批量表中過濾非法或拼寫錯誤。

少做事就是懶惰和不專業。學習正確的習慣,他們會幫助你免受未來的災難。

+0

個人而言,我喜歡將數據加載到臨時表中,然後提升可以正確轉換的行。您提到了批量插入,因此必須是在插入實時數據之前清理並準備好的數據。這就是爲什麼你應該使用臨時表(我希望你一直在討論這個問題),這樣你就可以將ETL過程分解成子階段,以保證在最終數據庫表中獲得完美的結果。沒有什麼比在事先應該處理大塊數據時必須更改實時數據更糟糕。 –

相關問題