9

テーブルへのカーソルを開き、すべてのレコードを反復処理するストアド プロシージャを作成しています。反復プロセスでは、最初のカーソルの結果に基づいて動的クエリを作成します。

動的 SQL でカーソルを開く必要がありますが、MySQL では許可されていません。MySQL の公式ドキュメントによると、「ハンドラーを宣言する前にカーソルを宣言する必要があります。カーソルまたはハンドラーを宣言する前に、変数と条件を宣言する必要があります」

スクリプトは次のとおりです。

DELIMITER $$

DROP PROCEDURE IF EXISTS sp_test$$

CREATE PROCEDURE `sp_test`()
BEGIN
    -- Declarations
    
    DECLARE prepared_sql VARCHAR(1000);
    DECLARE index_count INT;

    -- Cursors
    DECLARE cursor1 CURSOR FOR SELECT * from table1;
    -- Continue Handler for Cursor
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_rows = TRUE;
    -- Open cursors
    OPEN cursor1;

    -- Business Logic
    all_alerts_loop: LOOP
        -- Fetch record from cursor1 and create a dynamic sql
                
        -- Check if cursor has reached to end than leave the loop
        IF no_more_rows THEN
            LEAVE all_alerts_loop;
        END IF;
        
        
        WHILE @some_other_variable <> 0
        DO
                              -- I want to open cursor 2 on this sql
            -- set @prepared_sql =  'create dynamic sql here';  
                    END WHILE;
        
                    -- This works fine
        PREPARE stmt FROM @prepared_sql;
        EXECUTE stmt;

                    -- But can't define cursor here? so what is the solution
                    -- Gives syntax error, I have tried with @prepared_sql also rather than stmt
        DECLARE cursor2 CURSOR FOR stmt;
        
    END LOOP;
    
    -- closing cursors
    CLOSE cursor1;
    END$$

DELIMITER ;

動的クエリのカーソルを作成する方法について何か考えはありますか? MySQLで?

4

7 に答える 7

6

別のプロシージャを作成し、この新しいプロシージャにカーソルのコードを記述してから、カーソルを宣言する場所からプロシージャを呼び出します。

于 2011-10-11T15:57:36.523 に答える
0

あなたのスクリプトには、次の 2 つの問題が考えられます。

1) "declare cursor2 CURSOR FOR stmt;" おそらく、実行可能なステートメントの前に、他のすべての宣言とともにプロシージャの先頭に移動する必要があります。

2) カーソルは動的 SQL に基づくことはできません (つまり、準備されたステートメントに基づいて構築できるとは思いません)。この制限を回避するには、ビューに基づいてカーソルを宣言し、動的 ​​SQL を使用してビューを作成してからカーソルを開きます。このアプローチの問題点は、ビューがパブリックであることです。カーソル宣言にはビューの固定名が必要であるため、複数の同時ユーザーが他のユーザーが動的に定義したビューを誤って見る可能性があります。私の回避策は、ビューの存在を確認し、ビューが削除されるまでプロシージャの実行を遅らせることです。これは、忙しい環境で実行可能にするためには、ビューを作成し、カーソルをループしてから、できるだけ早くビューをドロップする必要があることを意味します。技術的にはエレガントではありませんが、そのアプローチはトラフィックの少ない状況ではうまくいきました。一時テーブルのオーバーヘッドを回避します。または、他の人が示唆しているように、一時テーブルはスレッドセーフですが、パフォーマンスに影響を与える可能性があります。

于 2013-02-01T15:59:45.400 に答える
0

karni のアプローチはそれほど面倒ではありません。各条件分岐を満たすために 2 つ以上の SP を作成します (それぞれに動的 SQL が必要でした)。ラッパー SP を作成し、この SP から「ブランチャー」SP への呼び出しをファンアウトします。

「準備済みビュー」アプローチである代替手段では、手順の実行中に、より多くの CPU サイクルとメモリ、および追加のディスク領域が必要になります。

于 2014-07-20T01:09:53.067 に答える
0

この場合、以下を使用して回避策を考えることができますstmt

  1. クエリによって返されるすべてのレコードをカウントします

  2. 制限を使用するため、クエリによって返された各レコードをループして参照します。

以下の例を参照してください。

CREATE PROCEDURE `proc_example`(IN p_where text)
BEGIN

    DECLARE v_where text default "";
    DECLARE v_cont integer default 0;
    
    #build a dynamic where
    set v_where = p_where;
    
    #Count query records 
    set @v_sqlSelect_count = 'select count(*) into @v_total ';
    set @v_sqlSelect_count = concat(@v_sqlSelect_count,'from table ');
    set @v_sqlSelect_count = concat(@v_sqlSelect_count,'where ');
    set @v_sqlSelect_count = concat(@v_sqlSelect_count,v_where);
    
    #Executa query
    PREPARE stmt_total FROM @v_sqlSelect_count;
    EXECUTE stmt_total;
    DEALLOCATE PREPARE stmt_total;
    
    #if exists records
    if (@v_total > 0) then
        
        set v_cont = 0;
        
        navRecords:loop
        
            if (v_cont > (@v_total - 1)) then
                leave getAgend;
            end if;
            
            #build select
            set @v_sqlSelect = 'select id,name ';
            set @v_sqlSelect = concat(@v_sqlSelect,'into @id,@name ');
            set @v_sqlSelect = concat(@v_sqlSelect,'from table ');
            set @v_sqlSelect = concat(@v_sqlSelect,'where ');
            set @v_sqlSelect = concat(@v_sqlSelect,v_where);
            set @v_sqlSelect = concat(@v_sqlSelect,' order by id asc limit ',v_cont,',1'); 
            
            #Execute query
            PREPARE stmt_select FROM @v_sqlSelect;
            EXECUTE stmt_select;
            DEALLOCATE PREPARE stmt_select;   
            
            #Do anything with the data @id, @name
            update table1 set desc1 = @name where id1 = @id;
            
            #Next record
            set v_cont = v_cont + 1;
            
        end loop navRecords;
        
    end if;

END
于 2021-02-18T01:03:18.463 に答える