web-dev-qa-db-ja.com

MySQL:カーソルのループ結果が予定より早く終了しました

カーソルの結果を反復するループを実行しています。コードはトリガー関数内にあり、重要な部分は次のようになります。

[〜#〜] edit [〜#〜]:すみません、愚かな間違い。トリガーは、「one_table」で削除された後に実行されます。これは、トリガーのコード内で「削除」または「更新」操作を実行するのと同じテーブルではありません(下の2番目のリストを参照)

create trigger my_trigger after delete on one_table
for each row 
begin 

declare my_value int; 
declare num_rows int default 0; 
declare done int default false; 
declare my_cursor cursor for select value from table where condition; 
declare continue handler for sqlstate '02000' set done = 1; 

open my_cursor; 
select found_rows() into num_rows; 

-- This is just for debugging 
insert into log_table(key, value) values('foo', num_rows); 

if num_rows > 0 then: 
  repeat 
    fetch my_cursor into my_value; 
    -- Do stuff 
  until done end repeat; 
end if; 

close my_cursor; 

end 

クエリは11個の値を返すため、ループは11回実行する必要があります。これは「挿入」句によってチェックされます。変数「num_rows」は11です。ただし、ループが3回しか実行されないことが問題です。

これらの数値(11、3)は重要ではありません。別の量の結果を返すようにクエリを変更すると、問題が残ります。ループはスケジュールされる前に終了します。

それは意味がありますか?ループの終了を引き起こすループ内(「do stuff」部分)で何かを行う場合があります。それは私にとって論理的に聞こえる唯一のものです。

[〜#〜] edit [〜#〜]:「Do stuff」の部分を含めます。この部分では、いくつかの「選択」文が実行されるため、問題が発生した可能性があります。これらの文のいずれかが空の結果を返すと、定義されたハンドラーが実行され、doneが「true」に設定されてループが解除されます。

「実行」の部分は次のとおりです。

select some_value into some_field from other_table where some_conditions;

if (some_field is null) then
  delete from my_table where my_condition;
else
  update my_table set key1 = value1 where condition1;

よろしくお願いいたします。

5
Jorge Arévalo

解決しました。前述のように、問題は内部の「SELECT INTO ...」文が0レコードを返し、CONTINUE HANDLERがトリガーされ、TRUEに設定されたことでした。 http://dev.mysql.com/doc/refman/5.0/en/cursors.html ( David Berganによる2012年2月23日午後10時のコメント)。

したがって、コードは最終的に次のようになります

create trigger my_trigger after delete on one_table
for each row 
begin 

declare my_value int; 
declare num_rows int default 0; 
declare done int default false; 
declare my_cursor cursor for select value from table where condition; 
declare continue handler for not found set done = true; 

open my_cursor; 

my_loop: loop

    set done = false;

    fetch my_cursor into my_value; 

    if done then
      leave my_loop;
    end if;


    select some_value into some_field from other_table where some_conditions;
    if (some_field is null) then
        delete from my_table where my_condition;
    else
        update my_table set key1 = value1 where condition1;
    end if;
end loop my_loop; 

close my_cursor; 

end

RolandoMySQLDBAの回答に感謝します。

2
Jorge Arévalo

ループ内のループの終了を厳密にチェックする必要があります

_create trigger my_trigger after delete on my_table 
for each row 
begin 

declare my_value int; 
declare num_rows int default 0; 
declare done int default false; 
declare my_cursor cursor for select value from table where condition; 
declare continue handler for sqlstate '02000' set done = 1; 

open my_cursor; 

-- This is just for debugging 
insert into log_table(key, value) values('foo', num_rows); 

cursor_loop:repeat 
    if done then
        leave cursor_loop;
    end if;
    fetch my_cursor into my_value; 
    -- Do stuff 
    until done
end repeat; 

close my_cursor; 

end 
_

これにより、カウントをチェックする必要もなくなります

試してみる !!!

疑似コードを見る

_create trigger my_trigger after delete on my_table 
for each row 
begin 

declare my_value int; 
declare num_rows int default 0; 
declare done int default false; 
declare my_cursor cursor for select value from table where condition; 
declare continue handler for sqlstate '02000' set done = 1; 

open my_cursor; 

-- This is just for debugging 
insert into log_table(key, value) values('foo', num_rows); 

cursor_loop:repeat 
    if done then
        leave cursor_loop;
    end if;
    fetch my_cursor into my_value; 
    -- Do stuff 

    select some_value into some_field from other_table where some_conditions;
    if (some_field is null) then
        delete from my_table where my_condition;
    else
        update my_table set key1 = value1 where condition1;

    until done
end repeat; 

close my_cursor; 

end 
_

同じテーブルのafter UPDATEトリガーの真ん中にいるときに、my_tableでDELETEを実行することはお勧めできません。また、同じテーブルのif (some_field is null) thenでDELETEを実行していることにも注意してください

このトリガーをストアドプロシージャとして記述し、同じテーブルのDELETE内のテーブルのUPDATEをネストする代わりに、呼び出しを手動で使用する方がよいでしょう。

2
RolandoMySQLDBA