0

次のように、整数の有名な AlphaID 表現を作成する PHP 関数があります。

function alphaID( $input ) {

    $index = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

    $base    = strlen( $index );
    $input  += pow( $base, 4 );
    $output  = '';

    for( $i = floor( log( $input, $base ) ); $i >= 0; $i-- ) {

        $bcp     = bcpow( $base, $i );
        $start   = floor( $input / $bcp ) % $base;
        $output .= substr( $index, $start, 1 );
        $input   = $input - ( $start * $bcp );
    }

    return $output;
}

たとえば、PHP_MAX_INT定数 (2147483647) によって提供される最大整数をエンコードすると、cwuCBb が返されます。

しかし、アプリケーション全体では少し遅すぎると思ったので、MYSQL 関数を作成しようとしました。理論的には、クエリ中にデータの準備ができたら、PHP でそのような変換を行うことでパフォーマンスを浪費することはありません。

MySQL のマニュアルはあまり親切ではありませんが、あちこち検索すると、次のようになりました。

DROP FUNCTION IF EXISTS ENCODE_ALPHAID;

DELIMITER $$

CREATE FUNCTION ENCODE_ALPHAID( input integer ) RETURNS CHAR( 6 ) DETERMINISTIC

BEGIN

    DECLARE output CHAR( 6 );

    DECLARE letters CHAR( 62 );
    DECLARE base TINYINT( 2 );

    DECLARE iterator TINYINT( 2 );
    DECLARE bcp CHAR( 9 );
    DECLARE start TINYINT( 2 );

    SET output   = '';

    SET letters  = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

    SET base     = CHAR_LENGTH( letters );
    SET input    = input + POW( base, 4 );

    SET iterator = FLOOR( LOG( base, input ) );

    ENCODING: LOOP

        SET bcp      = POW( base, iterator );
        SET start    = FLOOR( input / bcp ) % base + 1;

        SET output   = CONCAT( output, SUBSTR( letters, start, 1 ) );

        SET input    = input - ( start * bcp );

        SET iterator = iterator-1;

        IF iterator < 0 THEN LEAVE ENCODING; END IF;

    END LOOP ENCODING;

    RETURN output;

END $$

DELIMITER ;

しかし、使用している MySQL マネージャー ( DBNinja ) を介して実行すると、明らかに何も起こりません。クエリを実行すると、影響を受ける行が 0と表示され、これが期待される出力かどうかわかりません。

正しいかどうかに関係なく、クエリで使用しようとしたとき、関数はどちらの方法でも機能しませんでした。

SELECT ENCODE_ALPHAID( `c`.`cid` ) from `table` c WHERE `c.user` = 1

table.ENCODE_ALPHAIDが存在しないというエラーを最初に受け取りました。その後、実際に、使用しようとしていたデータベースとは別のデータベースで関数を作成したことに気付きました。

しかし、適切なデータベースで再度作成しましたが、同じエラーが発生しました。

だからここに質問があります:

  1. 私は何を間違えましたか?このようなことをしようとするのはこれが初めてで、正確にはわかりません
  2. 私の PHP コードの実装は有効な MySQL 対応物ですか? 何か見逃しましたか?
  3. この関数を「グローバル」にすることはできますか? つまり、必要なデータベースで利用できますか? 私が取り組んでいるアプリケーションでは、関連する 2 つのサブシステムを分離しておくために、2 つのデータベースを作成しました。

更新しました

どうやら、関数が作成されないという問題は DBNinja 側にあるようです。なぜなら、MySQL コンソールで直接ステートメントを実行したためです。同じ出力ステータスの0 行が影響を受けましたが、ステートメントで関数を効果的に使用できたので、関数は結局作成されました。

ただし、ルーチン自体に何か問題があるため、テストしたところ、結果の文字列は...「1つ先」でした。

たとえば、次の PHP コードを実行するとします。

echo alphaID( 2 );

これはbaaacを生成しますが、MySQL 関数はbaaadを返します。これは、整数3の AlphaID 表現になります。

これは間違っているだけでなく、最大 2147483647 に達したときに使用される INT 型をオーバーフローさせる可能性もあると思います

4

2 に答える 2

1

Bruno さん、MySQL のバージョンから不足しています:

  1. $input += pow( $base, 4 );
  2. PHP の SUBSTR は 0 から始まり、MySQL では 1 から始まります。
  3. ループ終了条件は次のとおりです。

    IF iterator < 0 THEN LEAVE ENCODING; END IF;

  4. CONCAT を使用する場合は、CONCAT NULL を使用しないでください (パラメーターを初期化する必要があります)。

ルーチンは常に 1 つのデータベースで作成されます。クエリにデータベース名を含めることで、それらのいずれかでそれらを呼び出すことができます (権限がある場合)。

call mydb.ENCODE_ALPHAID( 2147483647 );

また、変数を宣言するときは、変数が可能な列名/予約語と混同されないように、プレフィックスを使用することをお勧めします。たとえば、「出力」の代わりに「v_output」を使用します。

于 2016-07-11T21:02:09.287 に答える