SQL游标性能/替代?

时间:2017-02-22 02:23:57

标签: sql sql-server cursor

我目前有两个表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中的每一行,以便临时表获取更新并跟踪更改?

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;