404 Not Found

404 Not Found


nginx

MySQLのストアドプロシージャと関数に関する詳細な解説

ストアドプロシージャとは、あらかじめコンパイルされたSQL文の集合であり、一度作成すれば何度でも呼び出すことができます。

このレッスンでは、ストアドプロシージャと関数の作成および使用方法について解説します。

100%
graph TB
    A[CALL Stored Procedures] --> B[Parameter Passing IN/OUT/INOUT]
    B --> C[DECLARE Declare a variable]
    C --> D{Process Control}
    D --> E[IF / CASE Conditions]
    D --> F[WHILE / LOOP Loop]
    D --> G[Cursor CURSOR Iterate]
    E --> H[ExecuteSQL]
    F --> H
    G --> H
    H --> I[OUTParameter Return]
    I --> J[End]

1. 学習内容


2. 実際の事例

(1) 課題:複雑なロジックを繰り返し記述すること

毎月、各部署の給与総額を計算していますが、SQLクエリが非常に長く、繰り返し実行されています。

(2) ストアドプロシージャを用いた解決策

SQL
CREATE PROCEDURE sp_dept_salary_stats()
BEGIN
    SELECT department, SUM(salary) AS total_salary, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department;
END;

-- Call
CALL sp_dept_salary_stats();

3. ストアドプロシージャの作成

▶ 例:基本的なストアドプロシージャ

SQL
-- Create a Stored Procedure
DELIMITER //
CREATE PROCEDURE sp_get_user(IN user_id INT)
BEGIN
    SELECT * FROM users WHERE id = user_id;
END //
DELIMITER ;

-- Call
CALL sp_get_user(1);
▶ 試してみよう

▶ 例:OUTパラメータを使用する場合

SQL
DELIMITER //
CREATE PROCEDURE sp_count_users(OUT total INT)
BEGIN
    SELECT COUNT(*) INTO total FROM users;
END //
DELIMITER ;

-- Call
CALL sp_count_users(@total);
SELECT @total;
▶ 試してみよう

▶ 例:INOUTパラメータを使用する場合

SQL
DELIMITER //
CREATE PROCEDURE sp_double(INOUT num INT)
BEGIN
    SET num = num * 2;
END //
DELIMITER ;

-- Call
SET @num = 5;
CALL sp_double(@num);
SELECT @num;  -- 10
▶ 試してみよう

4. 変数の宣言

▶ 例:DECLARE 変数

SQL
DELIMITER //
CREATE PROCEDURE sp_variable_demo()
BEGIN
    DECLARE total INT DEFAULT 0;
    DECLARE avg_salary DECIMAL(10,2);
    DECLARE user_name VARCHAR(50);
    
    -- Assignment
    SELECT COUNT(*) INTO total FROM employees;
    SELECT AVG(salary) INTO avg_salary FROM employees;
    
    -- Using Variables
    SELECT total, avg_salary;
END //
DELIMITER ;
▶ 試してみよう

5. プロセス制御

▶ 例:IF文

SQL
DELIMITER //
CREATE PROCEDURE sp_check_salary(IN emp_id INT)
BEGIN
    DECLARE emp_salary DECIMAL(10,2);
    
    SELECT salary INTO emp_salary FROM employees WHERE id = emp_id;
    
    IF emp_salary > 10000 THEN
        SELECT 'High salary' AS level;
    ELSEIF emp_salary > 5000 THEN
        SELECT 'Medium salary' AS level;
    ELSE
        SELECT 'Low salary' AS level;
    END IF;
END //
DELIMITER ;
▶ 試してみよう

▶ 例:CASE文

SQL
DELIMITER //
CREATE PROCEDURE sp_order_status(IN order_id INT)
BEGIN
    DECLARE order_status VARCHAR(20);
    
    SELECT status INTO order_status FROM orders WHERE id = order_id;
    
    CASE order_status
        WHEN 'pending' THEN SELECT 'Order is pending' AS message;
        WHEN 'paid' THEN SELECT 'Order is paid' AS message;
        WHEN 'shipped' THEN SELECT 'Order is shipped' AS message;
        ELSE SELECT 'Unknown status' AS message;
    END CASE;
END //
DELIMITER ;
▶ 試してみよう

▶ 例:WHILEループ

SQL
DELIMITER //
CREATE PROCEDURE sp_sum_1_to_n(IN n INT, OUT total INT)
BEGIN
    DECLARE i INT DEFAULT 1;
    SET total = 0;
    
    WHILE i <= n DO
        SET total = total + i;
        SET i = i + 1;
    END WHILE;
END //
DELIMITER ;

-- Call
CALL sp_sum_1_to_n(100, @total);
SELECT @total;  -- 5050
▶ 試してみよう

6. カーソル

▶ 例:カーソルの反復処理

SQL
DELIMITER //
CREATE PROCEDURE sp_list_users()
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE user_name VARCHAR(50);
    DECLARE user_email VARCHAR(100);
    
    -- Declare cursor
    DECLARE cur_users CURSOR FOR 
        SELECT username, email FROM users;
    
    -- End of cursor handler
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    -- Open cursor
    OPEN cur_users;
    
    -- Iterate
    read_loop: LOOP
        FETCH cur_users INTO user_name, user_email;
        IF done THEN
            LEAVE read_loop;
        END IF;
        SELECT user_name, user_email;
    END LOOP;
    
    -- Close cursor
    CLOSE cur_users;
END //
DELIMITER ;
▶ 試してみよう

7. ストアド関数

▶ 例:関数の作成

SQL
DELIMITER //
CREATE FUNCTION fn_calc_bonus(salary DECIMAL(10,2))
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
    DECLARE bonus DECIMAL(10,2);
    
    IF salary > 10000 THEN
        SET bonus = salary * 0.2;
    ELSEIF salary > 5000 THEN
        SET bonus = salary * 0.1;
    ELSE
        SET bonus = salary * 0.05;
    END IF;
    
    RETURN bonus;
END //
DELIMITER ;

-- Using Functions
SELECT name, salary, fn_calc_bonus(salary) AS bonus FROM employees;
▶ 試してみよう

8. ストアドプロシージャの管理

▶ 例:表示と削除

SQL
-- View All Stored Procedures
SHOW PROCEDURE STATUS WHERE Db = 'mydb';

-- View the CREATE statement
SHOW CREATE PROCEDURE sp_get_user;

-- Delete a Stored Procedure
DROP PROCEDURE IF EXISTS sp_get_user;

-- Delete Function
DROP FUNCTION IF EXISTS fn_calc_bonus;
▶ 試してみよう

❓ よくある質問

Q ストアドプロシージャと関数の違いは何ですか?
A ストアドプロシージャはCALL文を使って呼び出され、値を返しません。一方、関数はSELECT文を使って呼び出され、値を返す必要があります。
Q DELIMITER とは何ですか?
A これはステートメントの終了記号です。ストアドプロシージャに ; が含まれているため、ステートメントが途中で終了してしまうのを防ぐために、これを // に置き換える必要があります。
Q ストアドプロシージャはパフォーマンスを向上させることができますか?
A ストアドプロシージャは事前にコンパイルされるため、ネットワークトラフィックが軽減されます。ただし、最新のMySQLオプティマイザはすでにかなり高性能であるため、パフォーマンスの向上は限定的です。
Q ストアドプロシージャと関数の違いは何ですか?
A プロシージャはCALL文を使用して呼び出され、値を返さずにOUTパラメータを持つことができますが、SELECT文で使用される関数は値をRETURNしなければなりません。
Q ストアドプロシージャはSQLインジェクションの脆弱性を持つ可能性がありますか?
A PREPAREとCONCATを組み合わせてSQL文を構築すると、インジェクションが発生する可能性があります。パラメータを直接渡す方が安全です。

📖 まとめ


📝 練習問題

  1. 基本問題(難易度:⭐):ユーザーIDを入力として受け取り、そのユーザーの情報を返すストアドプロシージャを作成してください。

  2. 上級問題(難易度 ⭐⭐):スコアに基づいて成績を返す、IF文を含むストアドプロシージャを作成してください。

  3. チャレンジ問題(難易度:⭐⭐⭐):カーソルを使用してユーザーテーブルを順に処理し、各ユーザーのポイントを計算して更新してください。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%