SQLを書いていて、こんなことはありませんか?
- 「同じ値を何度も書くのが面倒」
- 「計算結果を一時的に保存したい」
- 「条件によって値を変えたい」
- 「複雑なクエリを読みやすくしたい」
そんなときに便利なのが、SQLの変数です。
変数を使うメリット
- 値の再利用:同じ値を何度も書かなくて良い
- 可読性向上:複雑なクエリが読みやすくなる
- 保守性向上:値の変更が一箇所で済む
- 計算効率化:中間結果を保存できる
重要な注意点 SQLの標準仕様では変数の定義がないため、データベースごとに書き方が異なります。
この記事では、主要なデータベース(MySQL、PostgreSQL、SQL Server、Oracle)での変数の使い方を詳しく説明します。
変数が必要になる具体的な場面

よくある使用場面
1. 計算で同じ値を使い回す
-- 悪い例:同じ値を何度も書く
SELECT
price * 1.10 AS tax_included_price,
price * 1.10 * 0.05 AS commission,
price * 1.10 - 100 AS discounted_price
FROM products;
-- 良い例:変数を使って明確にする
-- (MySQLの例)
SET @tax_rate = 1.10;
SELECT
price * @tax_rate AS tax_included_price,
price * @tax_rate * 0.05 AS commission,
price * @tax_rate - 100 AS discounted_price
FROM products;
2. 条件分岐での値の設定
-- 売上目標に応じた評価を設定
3. レポート作成での集計値の再利用
-- 月次売上レポートでの平均との比較
MySQL:ユーザー変数
基本的な使い方
変数の宣言と代入
SET @変数名 = 値;
基本例
-- 文字列変数
SET @name = '田中太郎';
-- 数値変数
SET @price = 1000;
SET @tax_rate = 1.10;
-- 日付変数
SET @today = CURDATE();
変数の使用
-- 変数を使った計算
SELECT @name AS 顧客名, @price * @tax_rate AS 税込価格;
実行結果
顧客名 | 税込価格
---------|--------
田中太郎 | 1100
実用的な例
例1:売上分析
-- 分析期間の設定
SET @start_date = '2024-01-01';
SET @end_date = '2024-03-31';
SET @target_amount = 1000000;
-- 期間売上の集計
SELECT
COUNT(*) AS 注文数,
SUM(amount) AS 売上合計,
AVG(amount) AS 平均単価,
CASE
WHEN SUM(amount) >= @target_amount THEN '目標達成'
ELSE '目標未達成'
END AS 評価
FROM orders
WHERE order_date BETWEEN @start_date AND @end_date;
例2:価格計算
-- 料金設定
SET @base_price = 5000;
SET @discount_rate = 0.20;
SET @tax_rate = 0.10;
-- 最終価格の計算
SELECT
product_name,
@base_price AS 基本料金,
@base_price * @discount_rate AS 割引額,
@base_price * (1 - @discount_rate) AS 割引後価格,
@base_price * (1 - @discount_rate) * (1 + @tax_rate) AS 最終価格
FROM products;
変数への代入(SELECT INTO)
クエリ結果を変数に代入
-- 最新の注文金額を変数に代入
SELECT amount INTO @latest_amount
FROM orders
ORDER BY order_date DESC
LIMIT 1;
-- 代入した値を使用
SELECT
@latest_amount AS 最新注文金額,
@latest_amount * 1.1 AS 税込金額;
PostgreSQL:ブロック構文

DO文を使った基本例
PostgreSQLでは、通常のSELECT文で直接変数を使えません。DO文やファンション内で使用します。
基本構文
DO $$
DECLARE
変数名 データ型 := 初期値;
BEGIN
-- 処理
END $$;
基本例
DO $$
DECLARE
customer_name VARCHAR(50) := '田中太郎';
base_price NUMERIC := 1000;
tax_rate NUMERIC := 1.10;
final_price NUMERIC;
BEGIN
final_price := base_price * tax_rate;
RAISE NOTICE '顧客名: %, 税込価格: %', customer_name, final_price;
END $$;
実行結果
NOTICE: 顧客名: 田中太郎, 税込価格: 1100
実用的な例
例1:条件分岐処理
DO $$
DECLARE
total_sales NUMERIC;
target_sales NUMERIC := 1000000;
achievement_rate NUMERIC;
evaluation TEXT;
BEGIN
-- 売上合計を取得
SELECT SUM(amount) INTO total_sales
FROM orders
WHERE order_date >= '2024-01-01';
-- 達成率の計算
achievement_rate := (total_sales / target_sales) * 100;
-- 評価の判定
IF achievement_rate >= 100 THEN
evaluation := '目標達成';
ELSIF achievement_rate >= 80 THEN
evaluation := '良好';
ELSE
evaluation := '要改善';
END IF;
-- 結果の表示
RAISE NOTICE '売上合計: %, 達成率: %%, 評価: %',
total_sales, achievement_rate, evaluation;
END $$;
例2:ループ処理
DO $$
DECLARE
counter INTEGER := 1;
max_count INTEGER := 5;
result_text TEXT := '';
BEGIN
WHILE counter <= max_count LOOP
result_text := result_text || '項目' || counter || ' ';
counter := counter + 1;
END LOOP;
RAISE NOTICE '結果: %', result_text;
END $$;
ファンクションでの変数使用
カスタム関数の作成
CREATE OR REPLACE FUNCTION calculate_discount_price(
original_price NUMERIC,
discount_rate NUMERIC
) RETURNS NUMERIC AS $$
DECLARE
discount_amount NUMERIC;
final_price NUMERIC;
BEGIN
discount_amount := original_price * discount_rate;
final_price := original_price - discount_amount;
RETURN final_price;
END;
$$ LANGUAGE plpgsql;
-- 使用例
SELECT
product_name,
price AS 元価格,
calculate_discount_price(price, 0.20) AS 割引後価格
FROM products;
SQL Server(T-SQL):型指定が必要
基本的な使い方
変数の宣言
DECLARE @変数名 データ型;
DECLARE @変数名 データ型 = 初期値;
基本例
-- 変数の宣言と初期化
DECLARE @customer_name NVARCHAR(50) = N'田中太郎';
DECLARE @base_price INT = 1000;
DECLARE @tax_rate DECIMAL(3,2) = 1.10;
DECLARE @final_price DECIMAL(10,2);
-- 計算
SET @final_price = @base_price * @tax_rate;
-- 結果の表示
SELECT
@customer_name AS 顧客名,
@base_price AS 基本価格,
@final_price AS 税込価格;
実用的な例
例1:売上レポート
DECLARE @start_date DATE = '2024-01-01';
DECLARE @end_date DATE = '2024-03-31';
DECLARE @total_sales MONEY;
DECLARE @order_count INT;
DECLARE @avg_order MONEY;
-- 集計値の取得
SELECT
@total_sales = SUM(amount),
@order_count = COUNT(*),
@avg_order = AVG(amount)
FROM orders
WHERE order_date BETWEEN @start_date AND @end_date;
-- レポートの作成
SELECT
@start_date AS 開始日,
@end_date AS 終了日,
@order_count AS 注文数,
@total_sales AS 売上合計,
@avg_order AS 平均注文額,
CASE
WHEN @total_sales >= 1000000 THEN N'目標達成'
ELSE N'目標未達成'
END AS 評価;
例2:条件分岐処理
DECLARE @score INT = 85;
DECLARE @grade NVARCHAR(10);
DECLARE @comment NVARCHAR(100);
-- 条件分岐
IF @score >= 90
BEGIN
SET @grade = N'A';
SET @comment = N'優秀';
END
ELSE IF @score >= 80
BEGIN
SET @grade = N'B';
SET @comment = N'良好';
END
ELSE IF @score >= 70
BEGIN
SET @grade = N'C';
SET @comment = N'普通';
END
ELSE
BEGIN
SET @grade = N'D';
SET @comment = N'要改善';
END
-- 結果表示
SELECT
@score AS 点数,
@grade AS 評価,
@comment AS コメント;
ストアドプロシージャでの活用
プロシージャの作成
CREATE PROCEDURE GetCustomerSummary
@customer_id INT
AS
BEGIN
DECLARE @customer_name NVARCHAR(100);
DECLARE @total_orders INT;
DECLARE @total_amount MONEY;
DECLARE @last_order_date DATE;
-- 顧客情報の取得
SELECT @customer_name = name
FROM customers
WHERE id = @customer_id;
-- 注文履歴の集計
SELECT
@total_orders = COUNT(*),
@total_amount = SUM(amount),
@last_order_date = MAX(order_date)
FROM orders
WHERE customer_id = @customer_id;
-- 結果の返却
SELECT
@customer_name AS 顧客名,
@total_orders AS 総注文数,
@total_amount AS 総購入額,
@last_order_date AS 最終注文日;
END;
-- 実行例
EXEC GetCustomerSummary @customer_id = 123;
Oracle(PL/SQL):最も高機能

基本的な使い方
無名ブロック
DECLARE
customer_name VARCHAR2(50) := '田中太郎';
base_price NUMBER := 1000;
tax_rate NUMBER := 1.10;
final_price NUMBER;
BEGIN
final_price := base_price * tax_rate;
DBMS_OUTPUT.PUT_LINE('顧客名: ' || customer_name);
DBMS_OUTPUT.PUT_LINE('税込価格: ' || final_price);
END;
/
実用的な例
例1:カーソルを使った処理
DECLARE
CURSOR c_orders IS
SELECT order_id, amount, order_date
FROM orders
WHERE order_date >= SYSDATE - 30;
v_total_amount NUMBER := 0;
v_order_count NUMBER := 0;
v_avg_amount NUMBER;
BEGIN
FOR rec IN c_orders LOOP
v_total_amount := v_total_amount + rec.amount;
v_order_count := v_order_count + 1;
DBMS_OUTPUT.PUT_LINE('注文ID: ' || rec.order_id ||
', 金額: ' || rec.amount);
END LOOP;
IF v_order_count > 0 THEN
v_avg_amount := v_total_amount / v_order_count;
DBMS_OUTPUT.PUT_LINE('--- 集計結果 ---');
DBMS_OUTPUT.PUT_LINE('注文数: ' || v_order_count);
DBMS_OUTPUT.PUT_LINE('合計金額: ' || v_total_amount);
DBMS_OUTPUT.PUT_LINE('平均金額: ' || v_avg_amount);
END IF;
END;
/
例2:例外処理付きの処理
DECLARE
v_customer_id NUMBER := 123;
v_customer_name VARCHAR2(100);
v_order_count NUMBER;
v_not_found_error EXCEPTION;
BEGIN
-- 顧客情報の取得
BEGIN
SELECT name INTO v_customer_name
FROM customers
WHERE id = v_customer_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE v_not_found_error;
END;
-- 注文数の取得
SELECT COUNT(*) INTO v_order_count
FROM orders
WHERE customer_id = v_customer_id;
DBMS_OUTPUT.PUT_LINE('顧客名: ' || v_customer_name);
DBMS_OUTPUT.PUT_LINE('注文数: ' || v_order_count);
EXCEPTION
WHEN v_not_found_error THEN
DBMS_OUTPUT.PUT_LINE('エラー: 顧客ID ' || v_customer_id || ' が見つかりません');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('予期しないエラーが発生しました: ' || SQLERRM);
END;
/
データベース別の比較
機能比較
機能 | MySQL | PostgreSQL | SQL Server | Oracle |
---|---|---|---|---|
変数宣言 | SET @var = val | DECLARE var TYPE | DECLARE @var TYPE | var TYPE := val |
型指定 | 不要 | 必須 | 必須 | 必須 |
スコープ | セッション | ブロック内 | バッチ内 | ブロック内 |
条件分岐 | 限定的 | IF/CASE | IF/CASE | IF/CASE |
ループ処理 | なし | WHILE/FOR | WHILE | WHILE/FOR |
例外処理 | なし | EXCEPTION | TRY/CATCH | EXCEPTION |
使い分けの指針
MySQL
- シンプルな計算や値の再利用
- レポート作成での一時的な値保存
- セッション内での値の受け渡し
PostgreSQL
- 複雑なビジネスロジックの実装
- 関数やプロシージャでの処理
- データ移行やバッチ処理
SQL Server
- エンタープライズでの本格的な開発
- ストアドプロシージャでの業務処理
- レポーティングサービスとの連携
Oracle
- 大規模システムでの高度な処理
- 複雑なデータ処理ロジック
- パフォーマンスが重要なシステム
変数 vs プレースホルダ vs 一時テーブル
それぞれの使い分け
SQL変数
-- SQL内での計算に使用
SET @tax_rate = 1.10;
SELECT price * @tax_rate FROM products;
プレースホルダ(アプリケーション側)
-- アプリからの値受け渡し
SELECT * FROM users WHERE id = ?; -- PHP
SELECT * FROM users WHERE id = $1; -- PostgreSQL
一時テーブル
-- 複雑な集計結果の保存
CREATE TEMPORARY TABLE temp_sales AS
SELECT category, SUM(amount) as total
FROM orders GROUP BY category;
使い分けの基準
用途 | 推奨方法 | 理由 |
---|---|---|
アプリからの値渡し | プレースホルダ | セキュリティ・パフォーマンス |
SQL内での計算 | 変数 | 可読性・再利用性 |
大量データの中間結果 | 一時テーブル | パフォーマンス |
複雑な集計処理 | 変数 + 一時テーブル | 処理の明確化 |
実践的な活用例

売上分析レポート
MySQL版
-- 分析パラメータの設定
SET @analysis_year = 2024;
SET @target_amount = 1000000;
-- 月別売上分析
SET @jan_sales = (SELECT COALESCE(SUM(amount), 0) FROM orders WHERE YEAR(order_date) = @analysis_year AND MONTH(order_date) = 1);
SET @feb_sales = (SELECT COALESCE(SUM(amount), 0) FROM orders WHERE YEAR(order_date) = @analysis_year AND MONTH(order_date) = 2);
SET @mar_sales = (SELECT COALESCE(SUM(amount), 0) FROM orders WHERE YEAR(order_date) = @analysis_year AND MONTH(order_date) = 3);
-- レポート作成
SELECT
@analysis_year AS 分析年,
@jan_sales AS '1月売上',
@feb_sales AS '2月売上',
@mar_sales AS '3月売上',
@jan_sales + @feb_sales + @mar_sales AS '四半期合計',
CASE
WHEN @jan_sales + @feb_sales + @mar_sales >= @target_amount THEN '目標達成'
ELSE '目標未達成'
END AS '評価';
顧客セグメント分析
SQL Server版
DECLARE @high_value_threshold MONEY = 100000;
DECLARE @medium_value_threshold MONEY = 50000;
DECLARE @recent_months INT = 6;
WITH CustomerSegments AS (
SELECT
c.customer_id,
c.name,
SUM(o.amount) AS total_amount,
COUNT(o.order_id) AS order_count,
MAX(o.order_date) AS last_order_date,
CASE
WHEN SUM(o.amount) >= @high_value_threshold THEN 'プレミアム'
WHEN SUM(o.amount) >= @medium_value_threshold THEN '一般'
ELSE 'エントリー'
END AS customer_segment,
CASE
WHEN MAX(o.order_date) >= DATEADD(MONTH, -@recent_months, GETDATE()) THEN 'アクティブ'
ELSE '休眠'
END AS activity_status
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.name
)
SELECT
customer_segment,
activity_status,
COUNT(*) AS 顧客数,
AVG(total_amount) AS 平均購入額,
AVG(order_count) AS 平均注文数
FROM CustomerSegments
GROUP BY customer_segment, activity_status
ORDER BY customer_segment, activity_status;
トラブルシューティング
よくあるエラー
1. MySQL:変数が初期化されていない
-- エラーになる例
SELECT @undefined_var * 100; -- NULL * 100 = NULL
-- 正しい例
SET @var = COALESCE(@var, 0); -- 初期化してから使用
SELECT @var * 100;
2. PostgreSQL:ブロック外での変数使用
-- エラーになる例
DECLARE var_name INTEGER := 100; -- ブロック外ではエラー
-- 正しい例
DO $$
DECLARE
var_name INTEGER := 100;
BEGIN
RAISE NOTICE 'Value: %', var_name;
END $$;
3. SQL Server:データ型の不一致
-- エラーになる例
DECLARE @num INT = 'abc'; -- 文字列を数値型に代入
-- 正しい例
DECLARE @num INT = 123;
DECLARE @str NVARCHAR(10) = '123';
DECLARE @converted_num INT = CAST(@str AS INT);
デバッグのコツ
変数の値を確認
-- MySQL
SELECT @var_name AS debug_value;
-- PostgreSQL
RAISE NOTICE 'Debug: %', var_name;
-- SQL Server
PRINT 'Debug: ' + CAST(@var_name AS NVARCHAR(50));
-- Oracle
DBMS_OUTPUT.PUT_LINE('Debug: ' || var_name);
おわりに
SQLの変数は、プログラミングの基本スキルです。
この記事で学んだこと
- データベース別の変数の使い方
- 実用的な活用例
- トラブルシューティング
- 他の機能との使い分け
最初は使用するデータベースの基本的な変数操作から始めて、徐々に複雑な処理にチャレンジしていきましょう。
コメント