Курсор T-Sql не продолжает выборку

Я знаю, что курсоры не одобряются, и я стараюсь по возможности избегать их использования, но могут быть некоторые законные причины для их использования. У меня есть один, и я пытаюсь использовать пару курсоров: один для основной таблицы и один для дополнительной таблицы. Курсор основной таблицы выполняет итерацию по основной таблице во внешнем цикле. курсор вторичной таблицы проходит по вторичной таблице во внутреннем цикле. Проблема в том, что курсор первичной таблицы, хотя, очевидно, продолжает и сохраняет значение столбца первичного ключа [Fname] в локальную переменную @Fname, но не получает строку для соответствующего столбца внешнего ключа во вторичной таблице. Для вторичной таблицы он всегда возвращает строки, значение столбца внешнего ключа которых совпадает со значением столбца первичного ключа Первый ряд первичной таблицы.

Ниже приводится очень упрощенный пример того, что я хочу сделать в реальной хранимой процедуре. Имена - это основная таблица

SET NOCOUNT ON
DECLARE 
    @Fname varchar(50) -- to hold the fname column value from outer cursor loop
    ,@FK_Fname varchar(50) -- to hold the fname column value from inner cursor loop
    ,@score int
;

--prepare primary table to be iterated in the  outer loop
DECLARE @Names AS Table (Fname varchar(50))
INSERT @Names
    SELECT 'Jim' UNION
    SELECT 'Bob' UNION
    SELECT 'Sam' UNION
    SELECT 'Jo' 


--prepare secondary/detail table to be iterated in the inner loop
DECLARE @Scores AS Table (Fname varchar(50), Score int)
INSERT @Scores
    SELECT 'Jo',1 UNION
    SELECT 'Jo',5 UNION
    SELECT 'Jim',4 UNION
    SELECT 'Bob',10 UNION
    SELECT 'Bob',15 

--cursor to iterate on the primary table in the outer loop
DECLARE curNames CURSOR
FOR SELECT Fname FROM @Names


OPEN curNames
FETCH NEXT FROM curNames INTO @Fname

--cursor to iterate on the secondary table in the inner loop
DECLARE curScores CURSOR
FOR 
    SELECT FName,Score 
    FROM @Scores 
    WHERE Fname = @Fname 
 --*** NOTE: Using the primary table's column value @Fname from the outer loop

WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT 'Outer loop @Fname = ' + @Fname

    OPEN curScores
    FETCH NEXT FROM curScores INTO @FK_Fname, @Score

    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT ' FK_Fname=' + @FK_Fname + '. Score=' + STR(@Score)
        FETCH NEXT FROM curScores INTO @FK_Fname, @Score
    END
    CLOSE curScores
    FETCH NEXT FROM curNames INTO @Fname
END

DEALLOCATE curScores

CLOSE curNames
DEALLOCATE curNames

Вот что я получаю за результат. Обратите внимание, что для внешнего цикла он ДЕЙСТВИТЕЛЬНО показывает актуальное Fname, но когда это Fname используется как @Fname для выборки соответствующей строки из вторичной таблицы для последующих итераций, он по-прежнему получает строки, соответствующие первой строка (Боб) основной таблицы.

Outer loop @Fname = Bob
    FK_Fname=Bob. Score=10
    FK_Fname=Bob. Score=15
Outer loop @Fname = Jim
    FK_Fname=Bob. Score=10
    FK_Fname=Bob. Score=15
Outer loop @Fname = Jo
    FK_Fname=Bob. Score=10
    FK_Fname=Bob. Score=15
Outer loop @Fname = Sam
    FK_Fname=Bob. Score=10
    FK_Fname=Bob. Score=15

Пожалуйста, дайте мне знать, что я делаю не так. Заранее спасибо!

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
5 458
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я бы попробовал разместить

DECLARE curScores CURSOR
FOR 
    SELECT FName,Score 
    FROM @Scores 
    WHERE Fname = @Fname 

внутри первого while, потому что вы объявляете курсор только для первого значения имени

Ответ принят как подходящий

Значение @fName оценивается в: DECLARE curScores CURSOR, а не в основном цикле. Вы должны объявить, а затем освободить второй курсор в основном цикле.

Благодаря нескольким подсказкам я смог найти решение.

Мне пришлось ОБЪЯВИТЬ и ОТКЛЮЧИТЬ вторичный курсор в первом цикле. Сначала я ненавидел это делать, так как думал, что выделение и освобождение ресурсов в цикле было не очень хорошей идеей, но я думаю, что в данной конкретной ситуации другого способа избежать этого нет. Но рабочий код выглядит примерно так:

SET NOCOUNT ON
DECLARE 
    @Fname varchar(50) -- to hold the fname column value from outer cursor loop
    ,@FK_Fname varchar(50) -- to hold the fname column value from inner cursor loop
    ,@score int
;

--prepare primary table to be iterated in the  outer loop
DECLARE @Names AS Table (Fname varchar(50))
INSERT @Names
    SELECT 'Jim' UNION
    SELECT 'Bob' UNION
    SELECT 'Sam' UNION
    SELECT 'Jo' 


--prepare secondary/detail table to be iterated in the inner loop
DECLARE @Scores AS Table (Fname varchar(50), Score int)
INSERT @Scores
    SELECT 'Jo',1 UNION
    SELECT 'Jo',5 UNION
    SELECT 'Jim',4 UNION
    SELECT 'Bob',10 UNION
    SELECT 'Bob',15 

--cursor to iterate on the primary table in the outer loop
DECLARE curNames CURSOR
FOR SELECT Fname FROM @Names


OPEN curNames
FETCH NEXT FROM curNames INTO @Fname

--cursor to iterate on the secondary table in the inner loop
DECLARE curScores CURSOR
FOR 
    SELECT FName,Score 
    FROM @Scores 
    WHERE Fname = @Fname 
 --*** NOTE: Using the primary table's column value @Fname from the outer loop

WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT 'Outer loop @Fname = ' + @Fname

    OPEN curScores
    FETCH NEXT FROM curScores INTO @FK_Fname, @Score

    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT ' FK_Fname=' + @FK_Fname + '. Score=' + STR(@Score)
        FETCH NEXT FROM curScores INTO @FK_Fname, @Score
    END
    CLOSE curScores
    FETCH NEXT FROM curNames INTO @Fname
END

DEALLOCATE curScores

CLOSE curNames
DEALLOCATE curNames

И я получаю правильные результаты:

Outer loop @Fname = Bob
    FK_Fname=Bob. Score=        10
    FK_Fname=Bob. Score=        15
Outer loop @Fname = Jim
    FK_Fname=Jim. Score=         4
Outer loop @Fname = Jo
    FK_Fname=Jo. Score=         1
    FK_Fname=Jo. Score=         5
Outer loop @Fname = Sam

Я думаю, вы могли бы сделать это намного проще с временными таблицами с номерами строк:

create table #temp1
(
 row int identity(1,1)
 , ... 
)

Похоже, вы действительно просите SQL вести себя как язык, который любит циклы. Это не так. Когда я пишу цикл в SQL, я спрашиваю себя, нужно ли это делать таким образом? 7/10 раз ответ отрицательный, я могу сделать это с наборами.

Другие вопросы по теме