Как сказал @ paul-wehland, это потому, что ваш Try-Catch не использует вложенный курсор. Таким образом, ваша следующая итерация будет инициализировать курсор по имени, которое уже существует. Я предоставил пример кода, который будет запускать ваш базовый сценарий с предполагаемым условием сбоя на итерации 11 каждого курсора.
В этом примере я закомментировал ту часть кода, которая решает проблему. Где вы решите разместить этот блок, полностью зависит от вас, но имеет смысл проверить это либо до объявления вложенного курсора, либо внутри блока Catch.
declare
@id tinyint,
@parent_id tinyint,
@name varchar(255),
@parent_name varchar(255);
declare
@table
table
(
id tinyint not null primary key,
[name] varchar(255) not null
);
declare
@target
table
(
parent_id tinyint not null,
child_id tinyint not null,
parent_name varchar(10) not null,
child_name varchar(10) not null,
primary key(parent_id, child_id)
);
with cteNumber
as (
select top 11
[id] = row_number() over (order by [object_id])
from
sys.objects
)
insert into
@table
select
id,
[name] = replicate('a', id)
from
cteNumber;
declare
db_cursor1
cursor
local keyset read_only forward_only
for
select
0,
id,
'Initial',
[name]
from
@table;
open
db_cursor1;
fetch
next
from
db_cursor1
into
@id,
@parent_id,
@name,
@parent_name;
while @@FETCH_STATUS = 0
begin
begin transaction;
begin try
insert into @target
(parent_id, child_id, parent_name, [child_name])
values
(@parent_id, @id, @parent_name, @name);
--- inner cursor
/*
if CURSOR_STATUS('local', 'db_cursor2') = 1
begin
close
db_cursor2;
deallocate
db_cursor2;
end;
-- */
declare
db_cursor2
cursor
local keyset read_only forward_only
for
select
id,
[name]
from
@table;
open
db_cursor2;
fetch
next
from
db_cursor2
into
@id,
@name;
while @@FETCH_STATUS = 0
begin
insert into @target
(parent_id, child_id, parent_name, [child_name])
values
(@parent_id, @id, @parent_name, @name);
fetch
next
from
db_cursor2
into
@id,
@name;
end;
close
db_cursor2;
deallocate
db_cursor2;
commit transaction
end try
begin catch
print ERROR_MESSAGE();
rollback transaction;
end catch;
fetch
next
from
db_cursor1
into
@id,
@parent_id,
@name,
@parent_name;
end;
close
db_cursor1;
deallocate
db_cursor1;
select
[Last @id] = @id,
[Last @name] = @name,
[Last @parent_id] = @parent_id,
[Last @parent_name] = @parent_name;
select
*
from
@table;
select
*
from
@target;
EDITED
Вы также можете использовать создание переменной курсора и назначить ей объявление вложенного курсора, что устранит проблему обращения с дублирующимися именами. Смотрите ниже:
declare
@id tinyint,
@parent_id tinyint,
@name varchar(255),
@parent_name varchar(255);
declare
@table
table
(
id tinyint not null primary key,
[name] varchar(255) not null
);
declare
@target
table
(
parent_id tinyint not null,
child_id tinyint not null,
parent_name varchar(10) not null,
child_name varchar(10) not null,
primary key(parent_id, child_id)
);
with cteNumber
as (
select top 11
[id] = row_number() over (order by [object_id])
from
sys.objects
)
insert into
@table
select
id,
[name] = replicate('a', id)
from
cteNumber;
declare
@db_cursor2 cursor;
declare
db_cursor1
cursor
local keyset read_only forward_only
for
select
0,
id,
'Initial',
[name]
from
@table;
open
db_cursor1;
fetch
next
from
db_cursor1
into
@id,
@parent_id,
@name,
@parent_name;
while @@FETCH_STATUS = 0
begin
begin transaction;
begin try
insert into @target
(parent_id, child_id, parent_name, [child_name])
values
(@parent_id, @id, @parent_name, @name);
--- inner cursor
set @db_cursor2 = cursor
local keyset read_only forward_only
for
select
id,
[name]
from
@table;
open
@db_cursor2;
fetch
next
from
@db_cursor2
into
@id,
@name;
while @@FETCH_STATUS = 0
begin
insert into @target
(parent_id, child_id, parent_name, [child_name])
values
(@parent_id, @id, @parent_name, @name);
fetch
next
from
@db_cursor2
into
@id,
@name;
end;
close
@db_cursor2;
deallocate
@db_cursor2;
commit transaction
end try
begin catch
print ERROR_MESSAGE();
rollback transaction;
end catch;
fetch
next
from
db_cursor1
into
@id,
@parent_id,
@name,
@parent_name;
end;
close
db_cursor1;
deallocate
db_cursor1;
select
[Last @id] = @id,
[Last @name] = @name,
[Last @parent_id] = @parent_id,
[Last @parent_name] = @parent_name;
select
*
from
@table;
select
*
from
@target;