PostgreSQL 入門

PostgreSQL 演算子

演算子(Operators)は、PostgreSQLにおける特殊な記号やキーワードであり、1つまたは複数の値(これらをオペランドと呼びます)に対して操作を実行し、結果を生成するために使用されます。

データベース内のデータをフィルタリング、操作、および組み合わせるエクスプレッション(式)やクエリを構築する際、演算子は不可欠な基礎ツールです。これらを使用することで、条件の定義、計算の実行、値の比較が可能になり、SQLクエリをよりダイナミックで強力なものにすることができます。

PostgreSQLは多種多様なカテゴリーの演算子をサポートしており、それぞれが特定の操作向けに設計されています。

1. 算術演算子

算術演算子は、数値に対して数学的な計算(加算、減算、乗算、除算、剰余)を実行するために使用されます。

  • 加算 (+): 2つのオペランドを足し合わせます。
    • 例:SELECT 10 + 5; 結果は 15 です。
    • 実務でのユースケース: 注文内のすべての商品の合計金額を計算します。顧客が25ドルのシャツと40ドルのパンツを購入した場合、合計コストは 25 + 40 = 65 となります。
    • 想定シナリオ: 物流企業が2つのパッケージの総重量を計算します。パッケージAが15.5kg、パッケージBが7.2kgの場合、配送料計算のための総重量は 15.5 + 7.2 = 22.7 kgとなります。
  • 減算 (-): 最初のオペランドから2番目のオペランドを引きます。
    • 例:SELECT 10 - 5; 結果は 5 です。
    • 実務でのユースケース: 売上から売上原価(COGS)を差し引いて利益率を決定します。製品の販売価格が100ドルで、製造コストが30ドルの場合、利益は 100 - 30 = 70 です。
    • 想定シナリオ: 学校が学用品を購入した後の残予算を計算します。初期予算が5000ドルで、教材に1200ドル費やした場合、残予算は 5000 - 1200 = 3800 です。
  • 乗算 (*): 2つのオペランドを掛け合わせます。
    • 例:SELECT 10 * 5; 結果は 50 です。
    • 実務でのユースケース: 同じ商品を複数購入した場合の合計金額を計算します。単価が12ドルで、顧客が3点購入した場合、合計は 12 * 3 = 36 です。
    • 想定シナリオ: 投資会社が投資の将来価値を予測します。10,000ドルの初期投資が毎年1.05倍に成長する場合、1年後の価値は 10000 * 1.05 = 10500 です。
  • 除算 (/): 最初のオペランドを2番目のオペランドで割ります。
    • 例:SELECT 10 / 5; 結果は 2 です。
    • 注意: 整数同士の除算は小数点以下が切り捨てられます。浮動小数点演算を行うには、少なくとも一方のオペランドを小数にする必要があります(例:10.0 / 3)。
    • 実務でのユースケース: 学生の平均点を計算します。5名の学生の合計点が450点の場合、平均は 450 / 5 = 90 です。
    • 想定シナリオ: 工場が一定量の原材料から何バッチの製品を生産できるか判断します。1500kgの材料があり、1バッチに250kg必要な場合、生産可能なバッチ数は 1500 / 250 = 6 です。
  • 剰余 (%): 除算を行った後の余りを返します。
    • 例:SELECT 10 % 3; 結果は 1 です。
    • 実務でのユースケース: 数値が奇数か偶数かを判定します(数値 % 2 = 0 ならば偶数)。また、経過日数から曜日を計算するなどのループ処理にもよく使われます。
    • 想定シナリオ: スケジューリングシステムがタスクを異なるシフトに割り当てます。3つのシフトがあり、タスクが連番で管理されている場合、task_number % 3 によってそのタスクがどのシフトに属するかを決定できます。

2. 比較演算子

比較演算子は2つのエクスプレッションを比較し、ブーリアン(Boolean)の結果(TRUEFALSE、または NULL)を返します。これらは主に WHERE 句でデータをフィルタリングするために使用されます。

  • 等しい (=): 2つのオペランドが等しいかチェックします。
    • 例:SELECT 'apple' = 'apple';(TRUE)、SELECT 10 = 5;(FALSE)
    • 実例: 特定の部署に所属する全従業員を検索する (WHERE department_id = 101)。
  • 等しくない (<> または !=): 2つのオペランドが等しくないかチェックします。
    • 例:SELECT 'apple' <> 'orange';(TRUE)
    • 実例: 在庫がある製品をすべて取得する (WHERE status <> 'out_of_stock')。
  • より大きい (>): 最初のオペランドが2番目より大きいかチェックします。
    • 例:SELECT 10 > 5;(TRUE)
    • 実例: 合計金額が100ドルを超えるすべての注文をリストアップする (WHERE total_amount > 100)。
  • より小さい (<): 最初のオペランドが2番目より小さいかチェックします。
    • 例:SELECT 5 < 10;(TRUE)
    • 実例: 30歳未満の従業員をすべて検索する (WHERE age < 30)。
  • 以上 (>=): 最初のオペランドが2番目より大きいか等しいかチェックします。
    • 例:SELECT 10 >= 10;(TRUE)
    • 実例: 500ドル以上を費やした顧客を抽出する (WHERE total_spent >= 500)。
  • 以下 (<=): 最初のオペランドが2番目より小さいか等しいかチェックします。
    • 例:SELECT 5 <= 10;(TRUE)
    • 実例: 価格が20ドル以下の製品を特定する (WHERE price <= 20)。

3. 論理演算子

論理演算子はブーリアンのエクスプレッションを組み合わせたり修飾したりするために使用されます。これらは WHERE 句や HAVING 句で複雑なフィルタリング条件を作成するための土台となります。

  • AND: 両方のオペランドが TRUE の場合に TRUE を返します。
    • 例:SELECT (10 > 5) AND (3 < 7); 結果は TRUE です。
    • 実例: 在庫があり、かつ価格が50ドル未満の製品を検索する (WHERE quantity > 0 AND price < 50)。
  • OR: 少なくとも一方のオペランドが TRUE であれば TRUE を返します。
    • 例:SELECT (10 > 5) OR (3 > 7); 結果は TRUE です。
    • 実例: ニューヨークまたはカリフォルニアのユーザーを抽出する (WHERE city = 'New York' OR city = 'California')。
  • NOT: オペランドのブーリアン値を反転させます。NOT TRUEFALSE に、NOT FALSETRUE になります。
    • 例:SELECT NOT (10 < 5); 結果は TRUE です。
    • 実例: まだ発送されていない注文をすべて取得する (WHERE NOT status = 'shipped')。

4. 文字列演算子

PostgreSQLは、テキスト(文字列)の操作や比較に特化した演算子を提供しています。

  • 連結 (||): 2つ以上の文字列を繋ぎ合わせます。
    • 例:SELECT 'Hello' || ' ' || 'World'; 結果は 'Hello World' です。
    • 実務でのユースケース: レポート表示用に名(First Name)と姓(Last Name)を結合してフルネームを作成します。first_name が 'John'、last_name が 'Doe' の場合、'John' || ' ' || 'Doe' は 'John Doe' となります。
  • パターンマッチング (LIKE, ILIKE): 指定されたパターンとテキスト値を照合します。
    • LIKE: 大文字と小文字を区別(Case-sensitive)します。
    • ILIKE: 大文字と小文字を区別しません(PostgreSQL特有の機能)。
    • パターンマッチングでは2つの特殊なワイルドカードを使用します:
      • %: 0個以上の任意の文字列にマッチします。
      • _: 任意の1文字にマッチします。
    • 例 (LIKE):SELECT 'apple' LIKE 'app%';(TRUE)、SELECT 'Apple' LIKE 'app%';(FALSE)
    • 例 (ILIKE):SELECT 'Apple' ILIKE 'app%';(TRUE)
    • 実例: 名字が 'Sm' で始まるすべての顧客を検索する (WHERE last_name LIKE 'Sm%')、または説明文のどこかに 'widget' が含まれる製品を検索する (WHERE description ILIKE '%widget%')。
  • 正規表現マッチング (~, ~*): より強力な正規表現によるパターンマッチング機能を提供します。
    • ~: 大文字と小文字を区別してマッチングします。
    • ~*: 大文字と小文字を区別せずにマッチングします。
    • 例 (~):SELECT 'foobar' ~ 'foo.*';(TRUE)、SELECT 'Foobar' ~ 'foo.*';(FALSE)
    • 例 (~*):SELECT 'Foobar' ~* 'foo.*';(TRUE)
    • 実例: メールアドレスの形式をバリデーションしたり、複雑なログデータから特定のパターンを抽出したりします。

5. その他の重要な演算子

  • IS NULL / IS NOT NULL:NULL 値をチェックします。NULL はデータが欠落しているか不明であることを表し、=<> といった標準的な比較演算子では判定できません。
    • 例:SELECT NULL IS NULL;(TRUE)、SELECT 5 IS NOT NULL;(TRUE)
    • 実例: まだ配送ドライバーが割り当てられていない注文を特定する (WHERE driver_id IS NULL)。
  • BETWEEN: 値が指定された範囲内(境界値を含む)にあるかテストします。
    • 構文:value BETWEEN low AND high
    • 例:SELECT 15 BETWEEN 10 AND 20;(TRUE)
    • 実例: 価格が20ドルから50ドルの範囲にある製品を検索する (WHERE price BETWEEN 20 AND 50)。
  • IN: 値がリスト内のいずれかと一致するかテストします。
    • 構文:value IN (value1, value2, ...)
    • 例:SELECT 'apple' IN ('apple', 'orange', 'banana');(TRUE)
    • 実例: 特定の顧客IDリストに基づいて注文を取得する (WHERE customer_id IN (1, 5, 9))。
  • NOT IN: 値がリスト内のどれとも一致しないかテストします。
    • 例:SELECT 'grape' NOT IN ('apple', 'orange', 'banana');(TRUE)

6. 演算子の優先順位

演算子の優先順位は、エクスプレッション内での演算実行順序を決定します。標準的な数学と同様に、乗除算は加減算よりも先に実行されます。デフォルトの優先順位を変更するには括弧 () を使用します。

優先順位を理解することは、エクスプレッションが期待通りに計算されるために不可欠です。

  1. 最高優先順位: 括弧 ()
  2. 次: 単項演算子(例:符号の +, -
  3. 次: 乗算 *、除算 /、剰余 %
  4. 次: 加算 +、減算 -
  5. 次: 文字列連結 ||
  6. 次: 比較演算子 =, <>, >, <, >=, <=
  7. 最低優先順位(論理演算子): NOT, AND, OR
  • 例:SELECT 5 + 3 * 2; 括弧がない場合、計算順序は 5 + (3 * 2) = 5 + 6 = 11 となります。加算を先に計算したい場合は括弧を使います:SELECT (5 + 3) * 2; 結果は 8 * 2 = 16 となります。

7. 実例とデモンストレーション

以下の構造とデータを持つ products(製品)テーブルがあると仮定します。

CREATE TABLE products (
    product_id SERIAL PRIMARY KEY,
    product_name VARCHAR(100),
    category VARCHAR(50),
    price DECIMAL(10, 2),
    stock_quantity INTEGER,
    last_updated DATE
);

INSERT INTO products (product_name, category, price, stock_quantity, last_updated) VALUES
('Laptop Pro', 'Electronics', 1200.00, 50, '2023-10-20'),
('Gaming Mouse', 'Electronics', 75.50, 200, '2023-10-18'),
('Mechanical Keyboard', 'Electronics', 120.00, 150, '2023-10-21'),
('Desk Chair Ergo', 'Furniture', 300.00, 30, '2023-10-15'),
('Monitor 27-inch', 'Electronics', 250.00, 80, '2023-10-22'),
('Wireless Earbuds', 'Audio', 99.99, 300, '2023-10-19'),
('Office Desk L-shape', 'Furniture', 450.00, 20, '2023-10-16'),
('USB-C Hub', 'Accessories', 35.00, 100, '2023-10-20'),
('External SSD 1TB', 'Storage', 150.00, 70, '2023-10-17');

7.1 算術演算子の使用

各製品の在庫総価値を計算します:

SELECT
    product_name,
    price,
    stock_quantity,
    price * stock_quantity AS total_stock_value -- 乗算演算子
FROM
    products;

このクエリは * 演算子を使用して各製品の pricestock_quantity を掛け合わせ、利用可能な在庫の総資産価値を算出します。

また、すべての 'Electronics'(電子機器)の価格を10%引き上げます:

UPDATE products
SET price = price * 1.10 -- 10%増加させるための乗算演算子
WHERE category = 'Electronics';

-- 変更の確認(UPDATE実行後に実行)
SELECT product_name, price FROM products WHERE category = 'Electronics';

7.2 比較演算子と論理演算子の使用

価格が100ドルを超える 'Electronics'(電子機器)を検索します:

SELECT
    product_name,
    category,
    price
FROM
    products
WHERE
    category = 'Electronics' AND price > 100.00; -- = と > を組み合わせ、AND で繋ぐ

在庫数量が50から150(境界値を含む)の間にある製品をリストアップします:

SELECT
    product_name,
    stock_quantity
FROM
    products
WHERE
    stock_quantity BETWEEN 50 AND 150; -- BETWEEN 演算子

'Furniture'(家具)または 'Audio'(オーディオ)カテゴリーに属する製品を特定します:

SELECT
    product_name,
    category
FROM
    products
WHERE
    category IN ('Furniture', 'Audio'); -- IN 演算子

7.3 文字列演算子の使用

表示用に製品名とカテゴリーを連結します:

SELECT
    product_id,
    product_name || ' (' || category || ')' AS product_details -- 連結演算子
FROM
    products;

|| 演算子は製品名、括弧付きのリテラル文字列、およびカテゴリーを組み合わせて、より説明的な単一の文字列を作成します。

名称に 'Desk' が含まれる製品を検索します(大文字小文字を区別しない):

SELECT
    product_name
FROM
    products
WHERE
    product_name ILIKE '%Desk%'; -- ILIKE と両端の % ワイルドカードの組み合わせ

これは 'Desk Chair Ergo' と 'Office Desk L-shape' を返します。

7.4 IS NULL / IS NOT NULL の使用

新しく追加された製品において、last_updated フィールドが時々 NULL になる可能性があると仮定します。まずデモ用に、ある製品の last_updatedNULL に更新します:

UPDATE products
SET last_updated = NULL
WHERE product_name = 'USB-C Hub';

-- last_updated が NULL である製品を取得:
SELECT
    product_name,
    last_updated
FROM
    products
WHERE
    last_updated IS NULL; -- IS NULL 演算子

このクエリにより、'USB-C Hub' の更新日が NULL 値であることが正しく識別されます。