ぐらいとさんの日常

頭の中の整理や自己成長のためのログを取るための日記に近いブログです。

mysqlのストアドプロシージャとEXECUTEを使ってみた話


ん〜、単純にはいかない複雑処理
しかし全ユーザーデータなめるし速度が必要...
そういえばストアドとかいう機能があったなぁ...

 

※少々専門的な話になるのでご了承ください

システムエンジニア系のお話に興味がある方はどうぞ
 
 
私、LAMP環境で仕事してるんですがDBがテーブル名でシャーディングしているちょっと不思議な作りになっていまして。

何かしらのデータを触るときはシャーディングしてる各テーブル名でアクセスしなければならず、

小分けにするなりUNIONするなり面倒なクエリを発行しないといけないんですよね。

となれば、自動化できたら楽じゃない!?

ということで、やってみました。

ストアドプロシージャなる機能とEXECUTEを使えばできそうだなぁと。

 

まずストアドプロシージャとはなんぞや?と思ったのですが、

調べてみて直感的に思ったのが「mysqlで作れる関数」でした(厳密には違うかも)。

事前に処理内容を記録しておいて、あとで実行できるんですね。

これの引数が使えるというのがミソで、

同じストアドプロシージャを用いて動的で柔軟なクエリを発行できます!

そして直接Mysqlサーバー上で実行するので動作が速いんですね。

これは覚えておきたいテクニック。


今回の目的としては、

シャーディングしてる各テーブルに同じクエリを発行することです。

実はもっと複雑な処理をしたい時に威力を発揮するのですが

(仕事では複雑な処理のために使いました)テストケースとして

以下のような形で紹介しておきます。

 

ユーザーテーブルが10つあり、(user_table0〜user_table10)
特定のカラムを特定の条件(単純なwhere句)でSELECTするプロシージャを作り、

実行する

 

テスト用テーブルはこんな感じの想定で
user_data(user_id, user_name, address, favorite)

 
以下テストクエリ

-- CREATE PROCEDURE内でデリミタ";"を用いるため、一時的にデリミタを"//"に変更します
DELIMITER //

-- ここで実行するクエリをストアドプロシージャとして作成しておく
CREATE PROCEDURE loop_select(IN column VARCHAR(32), IN wcolumn VARCHAR(32), IN wvalue VARCHAR(32))
BEGIN
    DECLARE sh_cnt INT DEFAULT 0; -- シャーディングのカウンタ
    WHILE sh_cnt < 10 DO
        -- 実行するクエリを文字列で変数にセット
        SET @query_str = CONCAT(
            "SELECT user_id, ", column,
            " FROM user_table", sh_cnt,
            " WHERE ", wcolumn, " = ", wvalue, ";");

        PREPARE stmtc from @query_str; -- クエリ文字列から実行準備
        EXECUTE stmtc; -- 実行
        DEALLOCATE PREPARE stmtc; -- 実行したので開放
        SET sh_cnt = sh_cnt + 1;
    END WHILE;
END
//

-- CREATE PROCEDURE完了したのでデリミタ戻し
DELIMITER ;

-- 実際の実行部分
-- 住所が東京のユーザー名を調べる
CALL loop_select("user_name", "address", "東京");
-- 好物がりんごのユーザーの住所を調べる
CALL loop_select("address", "favorite", "りんご");

-- プロシージャを削除
DROP PROCEDURE loop_select;

こんな感じで、テーブル名やカラム名を動的に入力できるので、

うまく使えば色々捗るのです。

備忘録程度ですが、もしや誰かの助けになればと。

 
はてな記法使ってみたけど若干見た目が変?