8

非常に単純なストアド プロシージャを正しく作成するのに苦労しています。次の記事の表のスニペットを検討してください。

id    replaced_by     baseID
 1              2          0
 2              3          0
 3              0          0

コピーオンライトを使用した単純な階層テーブル。記事が編集されると、現在の記事の replacement_by フィールドが新しいコピーの ID に設定されます。

baseID フィールドを追加しました。これは、将来、記事の baseID を格納する必要があります。上記の私の例では、1 つの記事 (例: id 3) があります。baseID は 1 になります。

baseID を取得するために、次のストアド プロシージャを作成しました。

DELIMITER $$

CREATE FUNCTION getBaseID(articleID INT) RETURNS INT
BEGIN
    DECLARE x INT;
    DECLARE y INT;
    SET x = articleID;
    sloop:LOOP
        SELECT id INTO y FROM article WHERE replaced_by_articleID = x;
        IF y IS NOT NULL THEN
            SET x = y;
            ITERATE sloop;
        ELSE
            LEAVE sloop;
        END IF;  
    END LOOP;
    RETURN x;
END $$

DELIMITER ;

以下を使用して実際に関数を呼び出すまでは、十分に単純に思えます。

SELECT getBaseID(3);

私は、関数が 1 を返すことを期待しています。代わりに、マシンの CPU が 100% まで上昇します (mysqld)。

REPEAT .. UNTILと を使用して同じ関数を書き直してもWHILE .. DO、同じ最終結果が得られました。

ループに入ったときに CPU が 100% 上昇する理由を誰か説明できますか?

補足: 私は単純に時間を稼ごうとしています。私はまったく同じ関数を PHP で作成しましたが、これは正常に実行されますが、MySQL の方がわずかに高速であると推測されます。約 1,800 万件のレコードをふるいにかける必要があります。少しでも節約できる時間は、それだけの価値があります。

事前に支援および/またはポインタをありがとう.


解決済みの SQL:

DELIMITER $$

CREATE FUNCTION getBaseID(articleID INT) RETURNS INT
BEGIN
    DECLARE x INT;
    DECLARE y INT;
    SET x = articleID;
    sloop:LOOP
        SET y = NULL;
        SELECT id INTO y FROM article WHERE replaced_by_articleID = x;
        IF y IS NULL THEN
            LEAVE sloop;
        END IF;  
        SET x = y;
        ITERATE sloop;
    END LOOP;
    RETURN x;
END $$

DELIMITER ;
4

2 に答える 2

2

mysqlから:

クエリが行を返さない場合、エラー コード 1329 (データなし) の警告が発生し、変数値は変更されません。

xしたがって、指定された(y変更されないまま)でレコードが見つからない場合、無限ループが発生します。SET y = (SELECT id ....)代わりに試すかSET y = null、select ステートメントの前に追加します (ループの最初のステートメントにする必要があります)。

于 2011-08-11T19:30:32.637 に答える
0

replace_by 列のインデックスが欠落しているように感じます。replacement_by にインデックスがない場合は、反復ごとに完全なテーブル スキャンを見ていることになります。

ALTER TABLE article ADD INDEX (replaced_by);

また、取得する前に行が存在することを確認する必要があります

DELIMITER $$

CREATE FUNCTION getBaseID(articleID INT) RETURNS INT
BEGIN
    DECLARE x INT;
    DECLARE y INT;
    SET x = articleID;
    sloop:LOOP
        SELECT COUNT(1) INTO y FROM article WHERE replaced_by = x;
        IF y > 0 THEN
            SELECT id INTO y FROM article WHERE replaced_by = x;
            SET x = y;
        ELSE
            LEAVE sloop;
        END IF;  
    END LOOP;
    RETURN x;
END $$

DELIMITER ;

2 倍の SQL 呼び出しが発生しますが、申し訳ありませんが安全です。

試してみる !!!

于 2011-08-11T20:26:49.223 に答える