T-SQL: надлежащий способ ЗАКРЫТЬСЯ/ОСВОБОДИТЬ курсор в триггере обновления

Предположение, что мы говорим о лексикографическом порядке по переставляемым значениям, существует два общих подхода, которые можно использовать:

  1. преобразовывают одну перестановку элементов к следующей перестановке (как отправленный ShreevatsaR), или
  2. непосредственно вычисляют n th перестановка, при подсчете n от 0 вверх.

Для тех (как я;-), кто не говорит C++ как собственные компоненты, приблизьтесь 1, может быть реализован от следующего псевдокода, приняв основанную на нуле индексацию массива с индексным нулем на "левом" (заменяющий некоторой другой структурой, такой как список, "оставлен как осуществление";-):

1. scan the array from right-to-left (indices descending from N-1 to 0)
1.1. if the current element is less than its right-hand neighbor,
     call the current element the pivot,
     and stop scanning
1.2. if the left end is reached without finding a pivot,
     reverse the array and return
     (the permutation was the lexicographically last, so its time to start over)
2. scan the array from right-to-left again,
   to find the rightmost element larger than the pivot
   (call that one the successor)
3. swap the pivot and the successor
4. reverse the portion of the array to the right of where the pivot was found
5. return

Вот пример, запускающийся с текущей перестановки CADB:

1. scanning from the right finds A as the pivot in position 1
2. scanning again finds B as the successor in position 3
3. swapping pivot and successor gives CBDA
4. reversing everything following position 1 (i.e. positions 2..3) gives CBAD
5. CBAD is the next permutation after CADB

Для второго подхода (прямое вычисление n th перестановка), помните, что существует N! перестановки N элементы. Поэтому, если Вы переставляете N элементы, первое (N-1)!, перестановки должны начаться с самого маленького элемента, следующее (N-1)!, перестановки должны начаться со второго самого маленького и так далее. Это приводит к следующему рекурсивному подходу (снова в псевдокоде, нумеруя перестановки и положения от 0):

To find permutation x of array A, where A has N elements:
0. if A has one element, return it
1. set p to ( x / (N-1)! ) mod N
2. the desired permutation will be A[p] followed by
   permutation ( x mod (N-1)! )
   of the elements remaining in A after position p is removed

Так, например, 13-я перестановка ABCD найдена следующим образом:

perm 13 of ABCD: {p = (13 / 3!) mod 4 = (13 / 6) mod 4 = 2; ABCD[2] = C}
C followed by perm 1 of ABD {because 13 mod 3! = 13 mod 6 = 1}
  perm 1 of ABD: {p = (1 / 2!) mod 3 = (1 / 2) mod 2 = 0; ABD[0] = A}
  A followed by perm 1 of BD {because 1 mod 2! = 1 mod 2 = 1}
    perm 1 of BD: {p = (1 / 1!) mod 2 = (1 / 1) mod 2 = 1; BD[1] = D}
    D followed by perm 0 of B {because 1 mod 1! = 1 mod 1 = 0}
      B (because there's only one element)
    DB
  ADB
CADB

Кстати, "удаление" элементов может быть представлено параллельным массивом булевских переменных, который указывает, какие элементы все еще доступны, таким образом, не необходимо создать новый массив на каждом рекурсивном вызове.

Так, для итерации через перестановки ABCD, просто количество от 0 до 23 (4!-1), и непосредственно вычисляют соответствующую перестановку.

17
задан ROMANIA_engineer 20 June 2017 в 19:40
поделиться

3 ответа

Да, используйте TRY / CATCH, но после этого убедитесь, что вы освободили и т. Д. К сожалению, в SQL Server нет finally.

Однако я предлагаю обернуть это в другой try / catch

CREATE TRIGGER trigger1 ON [dbo].[table1] AFTER UPDATE
AS 
BEGIN                           
    --declare some vars
    DECLARE @Col1 SMALLINT, @Col1 TINYINT 

    BEGIN TRY
        --declare cursor            
        DECLARE Cursor1 CURSOR FOR 
        SELECT Col1, Col2 FROM INSERTED                     

        --do the job
        OPEN Cursor1
        FETCH NEXT FROM Cursor1 INTO @Col1, @Col2

        WHILE @@FETCH_STATUS = 0
        BEGIN
            IF ...something...
                    EXEC myProc1 @param1 = @Col1, @Param2 = @Col2
            ELSE
            IF ...something else...
                    EXEC myProc2 @param1 = @Col1, @Param2 = @Col2

            FETCH NEXT FROM Cursor1 INTO @Col1, @Col2                               
        END
    END TRY
    BEGIN CATCH
        --do what you have to
    END CATCH

    BEGIN TRY
        --clean it up               
        CLOSE Cursor1
        DEALLOCATE Cursor1                                  
    END TRY
    BEGIN CATCH
        --do nothing
    END CATCH
END

Другой вопрос, является ли курсор в триггере хорошей идеей ...

15
ответ дан 30 November 2019 в 10:24
поделиться

Что вам следует делать, так это никогда не использовать курсор в триггере. Вместо этого напишите правильный код на основе набора. Если бы кто-то импортировал данные в вашу таблицу из 100 000 новых записей, вы бы заблокировали таблицу на несколько часов и остановили бы вашу базу данных. Использование курсора в триггере - очень плохая практика.

1
ответ дан 30 November 2019 в 10:24
поделиться

Вы можете использовать функцию Cursor_Status ().

if CURSOR_STATUS('global','cursor_name') >= 0 
begin
 close cursor_name
  deallocate cursor_name 
end

Ссылка : : http://msdn.microsoft.com/en-us/library/ms177609.aspx

40
ответ дан 30 November 2019 в 10:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: