问题是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
为什么你需要这个错误日志?转换插入了错误数据的数据会不会更简单?为什么你需要“捕捉”这个插入错误?修复源代码,或使表格健壮。请记住,这是一个关系表。在将其插入表格之前验证您的数据。 –