SQL游标性能/备选方案?

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

我目前有两个表,表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

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,但正如我在文章中提到的,我使用时态表来维护表版本控制,我不想通过使用触发器对此破例。