我目前有两个表Table1和Table 2结构如下。正如您所看到的,表1包含列FK的多行,而FK列使用表2 ID列的外键,每列只有一行,表1中按表ID排序的表1中的最新值< / p>
Table 1
ID FK END_DTTM
1 1 01/01/2000
2 1 01/01/2005
3 1 01/01/2012
4 1 01/01/2100
5 2 01/01/1999
6 2 01/01/2100
7 3 01/01/2100
Table 2
ID END_DTTM
1 01/01/2100
2 01/01/2100
3 01/01/2100
业务要求是跟踪表2中的每个更新,以便可以检索时间点数据。为了实现这一点,我使用的是SQL 2016和Temporal表,其中表2的每次更新都会自动在历史记录表中创建一个版本。
要实现插入更新过程,我目前正在使用非常慢的游标,并且在30分钟内处理大约71000行,并且该表有大约6000万行!游标查询如下:
BEGIN
BEGIN TRY
BEGIN TRANSACTION;
Declare @ID as int;
Declare @FK as int;
Declare @END_DTTM as datetime2;
DECLARE @SelectCursor as CURSOR;
SET @SelectCursor = CURSOR LOCAL STATIC READ_ONLY FORWARD_ONLY FOR
SELECT [ID],[FK],[END_DTTM] from TABLE1 order by FK,ID;
OPEN @SelectCursor ;
FETCH NEXT FROM @SelectCursor INTO @ID,@FK,@END_DTTM;
WHILE @@FETCH_STATUS = 0
BEGIN
UPDATE TABLE2
set
END_DTTM = @END_DTTM
where ID = @FK
IF @@ROWCOUNT = 0
BEGIN
INSERT Table2
(
ID,END_DTTM
)
VALUES (
@FK,@END_DTTM
)
END
FETCH NEXT FROM @SelectCursor INTO @ID,@FK,@END_DTTM;
END
CLOSE @SelectCursor;
DEALLOCATE @SelectCursor;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
DECLARE @ErrorNumber INT = ERROR_NUMBER();
DECLARE @ErrorLine INT = ERROR_LINE();
DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();
DECLARE @ErrorSeverity INT = ERROR_SEVERITY();
DECLARE @ErrorState INT = ERROR_STATE();
PRINT 'Actual error number: ' + CAST(@ErrorNumber AS VARCHAR(10));
PRINT 'Actual line number: ' + CAST(@ErrorLine AS VARCHAR(10));
PRINT 'Actual message: ' + CAST(@ErrorMessage AS VARCHAR(4000));
PRINT 'Actual severity: ' + CAST(@ErrorSeverity AS VARCHAR(10));
PRINT 'Actual state: ' + CAST(@ErrorState AS VARCHAR(10));
Insert into ERROR_LOG
(
SOURCE_PRIMARY_KEY
,ERROR_CODE
,ERROR_COLUMN
,ERROR_DESCRIPTION
)
VALUES
(
null,
@ErrorNumber,
@ErrorState,
@ErrorMessage,
'Error!'
);
Throw;
-- RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH
END;
我尝试使用cte,但我没有看到任何性能提升,实际上它比游标本身慢一点。
有没有更好的方法来实现上述使用基于集合的操作仍然处理表1和更新2中的每一行,以便临时表获取更新并跟踪更改?
答案 0 :(得分:1)
我不确定您是如何运行更新SQL的,但我将描述用于跟踪Oracle中的更改的过程。
我在要审核的表上设置了一个触发器。我创建了另一个具有相同列的表,一个以OLD_为前缀,另一个以前缀为NEW_。在Oracle触发器中,您可以引用新行和旧行。我使用旧值和新值,DML操作类型和TIMESTAMP在审计表中运行插入。此外,我将添加数据库用户,如果可能,还会添加请求更改的应用程序用户。
在当前的RAC集群和我们古老的9i AIX服务器中,我永远不会注意到任何性能下降。
此外,如果事务被回滚,它将不会插入审计记录,因为触发器在事务中。
不要让人们告诉你不要使用SQL触发器。虽然你不想用触发器做“疯狂”的事情(比如运行查询或调用Web服务),但它是触发器的完美应用程序(我通常使用它们将最后更新的日期添加到行中。我不喜欢t信任应用层以获得准确的信息。)
答案 1 :(得分:0)
哦,这就是你想要的吗?
insert into table2(ID, END_DTTM)
select fk, max(END_DTTM)
from table1 t1
group by fk;