SQL游标性能/备选方案?
我目前有两个表,表1和表2结构如下。如您所见,表1包含多行FK列,FK列将外键设置为表2 ID列,该列每个ID只有一行,表1中的最新值按表1中的ID列排序SQL游标性能/备选方案?,sql,sql-server,cursor,Sql,Sql Server,Cursor,我目前有两个表,表1和表2结构如下。如您所见,表1包含多行FK列,FK列将外键设置为表2 ID列,该列每个ID只有一行,表1中的最新值按表1中的ID列排序 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 Tabl
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和时态表,其中对表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中的每一行,以便临时表拾取更新并跟踪更改?哦,这就是您想要的吗
insert into table2(ID, END_DTTM)
select fk, max(END_DTTM)
from table1 t1
group by fk;
我不确定您是如何运行更新SQL的,但我将描述用于跟踪Oracle中的更改的过程 我在要审核的表上设置了一个触发器。我创建了另一个具有相同列的表,一组以OLD_uu为前缀,另一组以NEW_uu为前缀。在Oracle触发器中,可以引用新行和旧行。我使用新旧值、DML操作类型和时间戳在审计表中运行insert。此外,我将添加数据库用户,如果可能的话,还将添加请求更改的应用程序用户 在当前的RAC集群和我们古老的9iAIX服务器中,我从未注意到任何性能下降 此外,如果事务回滚,则不会插入审核记录,因为触发器位于事务内部
不要让别人告诉你不要使用SQL触发器。虽然您不想对触发器做“疯狂”的事情(比如运行查询或调用Web服务),但它是触发器的完美应用程序(我通常使用它们向一行添加最后更新的日期。我不相信应用程序层会提供准确的信息)。请解释您尝试执行的逻辑。不要让陌生人试图弄明白光标代码。戈登,我已经在我的帖子中解释了逻辑。代码是为了显示我正在做的事情,如果没有用的话可以忽略。如果我不清楚,请这样说,我可以尝试重新措辞,没有任何例子或代码!显示所需的结果。表2的值具有所需的结果,即表1组中fk的最后一行不是真的,因为这只会将最新的/fk记录插入表中,我想要的是插入第一条记录并更新后续记录,以便临时表记录更改跟踪。此外,max可能不是解决方案,因为表中还有其他需要插入或更新的字段。为什么不能用触发器将“版本”插入表2中?每五分钟运行一次更新查询似乎更容易。谢谢Doug,这没关系,但我使用的是MS SQL server,但正如我在文章中提到的,我使用时态表来维护表版本控制,我不想通过使用触发器对此破例。