問題是SQL不會按照您認爲的方式處理錯誤。
- SQL Server保持ACID狀態。
原子:全部或全部。所有的工作都被分解爲事務性陳述,這些陳述必須成功或者整個修改/創建被還原,或者回滾到以前的工作狀態。通過使用BEGIN TRANSACTION
/COMMIT TRANSACTION
或使用ROLLBACK TRANSACTION
,您可以明確說明某件作品的交易。 Read More: Transaction Statements和ROLLBACK TRANSACTION
一致性:每個事務都必須使SQL Server處於有效狀態。例如,雖然DROP TABLE MyTable;
可能是有效的事務,但如果MyTable具有依賴關係(即Foreign Key Constraints
),則SQL Server將處於不一致狀態,並且會將事務回滾到上一個一致狀態。
隔離:每筆交易發生在自己的時間和空間。我們說序列號具體。隔離功能允許在服務器上同時執行多個甚至相似的語句,並且每個語句都獨佔一個。術語阻止指當語句正在等待事務提交併且死鎖是兩個事務正在無限期地彼此等待時。
耐用性:與存在於內存中的軟件程序不同,該程序可能因突然斷電而丟失,SQL Server的事務對於磁盤一旦提交就永久存在。這提供了一個聲明的最終結果。這也是LOG文件到位的地方,因爲它記錄了數據庫上執行的事務。 READ MORE: ACID PROPERTIES
自從您的BEGIN TRY/CATCH
塊查找這些問題後,我提到了所有這些。
- 對待你的表作爲數據集
召回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
中的有用信息。您實際上可以利用這些信息進行實時推薦。 這是您的錯誤追蹤的目標。
- 提醒一下,瞭解您的
RAISERROR
或TRY
/CATCH
只適用於整個交易......一個工作單元。如果您需要優雅地允許Merge語句失敗,那就好了。但要明白,適當的ETL 將保證這種昂貴的陳述永不失敗。
因此,無論如何,請在您寫入最終表格之前執行所有ETL過程。這是臨時表的要點...所以你可以在你的批量表中過濾非法或拼寫錯誤。
少做事就是懶惰和不專業。學習正確的習慣,他們會幫助你免受未來的災難。
是什麼導致了一個損壞的值? SQL不像您給出的信用那樣嚴格。如果它的髒數據,沒有發生。爲什麼首先出現錯誤?您的BEGIN TRAN在哪裏?爲什麼你不能限制合併中的值?你將SQL視爲一行行語言......它是關係型的。 –
@ clifton_h-我將50條記錄傳遞給數據表,並在一條記錄中傳遞帶有額外空間的日期(2016-10-15)。我的存儲過程未在ERROR_LOG表中記錄該錯誤(在catch塊中使用)。你能幫助其他方法嗎?我需要將錯誤插入該表中。 – paemmi
爲什麼你需要這個錯誤日誌?轉換插入了錯誤數據的數據會不會更簡單?爲什麼你需要「捕捉」這個插入錯誤?修復源代碼,或使表格健壯。請記住,這是一個關係表。在將其插入表格之前驗證您的數據。 –