コンテンツにスキップ

DML@SQL

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. DMLとは

テーブル上のレコードを操作するクエリのこと。


02. SELECT

はじめに

▼ 句の処理の順番

FROM ---> JOIN ---> WHERE ---> GROUP BY ---> HAVING ---> SELECT ---> ORDER BY


SELECT

▼ なし

指定したカラムを取得する。

MySQLでは、取得結果の並び順が毎回異なるため、プライマリーキーの昇順で取得したい場合は、ORDER BY句を使用して、明示的に並び替えるようにする。

SELECT
    *
FROM
    { テーブル名 };

SUM関数

指定したカラムで、『フィールド』の合計を取得する。

SELECT
    SUM({ カラム名 })
FROM
    { テーブル名 };

AVG関数

指定したカラムで、『フィールド』の平均値を取得する。

SELECT
    AVG({ カラム名 })
FROM
    { テーブル名 };

MIN関数

指定したカラムで、『フィールド』の最小値を取得する。

SELECT
    MIN({ カラム名 })
FROM
    { テーブル名 };

MAX関数

指定したカラムで、『フィールド』の最大値を取得する。

SELECT
    MAX({ カラム名 })
FROM
    { テーブル名 };

COUNT関数

指定したカラムで、『フィールド』の個数を取得する。

SELECT
    { カラム名 } COUNT(*)
FROM
    { テーブル名 };

※消去法の小技:集合関数を入れ子状にはできない

*実装例*

集合関数を集合関数の中に入れ子状にできない。

SELECT
    AVG(SUM({ カラム名 }))
FROM
    { テーブル名 };

指定したカラムで、値無しも含む『フィールド』を取得する。

SELECT
    { カラム名 } COUNT(*)
FROM
    { テーブル名 };

指定したカラムで、値無しを除いた『フィールド』を取得する。

SELECT
    { カラム名 } COUNT(*);

LAST_INSERT_ID関数

任意のテーブルに最後に挿入されたIDを読み出す。

テーブル名を指定する必要はない。

SELECT LAST_INSERT_ID();

MD5関数

文字列をハッシュ化

SELECT MD5("foo");


CASE

カラム1がtrueだったら、カラム2を取得する。

falseであったら、カラム3を取得する。

SELECT
    CASE
        WHEN { エイリアス }.{ カラム名1 } = 1 THEN { エイリアス }.{ カラム名2 }
        ELSE { エイリアス }.{ カラム名3 }
    END AS name
FROM
    { テーブル名 } AS { エイリアス };


FROM

JOIN句の種類

内部結合のベン図

LEFT JOIN (左外部結合)

『users』テーブルと『items』テーブルの商品IDが一致しているデータと、元となる『users』テーブルにしか存在しないデータが、セットで取得される。

LEFT_JOIN

INNER JOIN (内部結合)

基本情報技術者試験では、内部結合 (A∩B) しか出題されない。

▼ 内部結合にWHEREを使用する場合

2個のWHERE文が、ANDで結びつけられている時、まず1つ目のWHEREを満たすレコードを取得した後、取得したレコードの中から、2つ目のWHEREを満たすレコードを取得する。

*実装例*

-- 『カラム』のみでなく、どの『表』なの物なのかも指定
SELECT
    { テーブル名1 }.{ カラム名1 },
    -- 複数の表を指定
FROM
    { テーブル名1 },
    { テーブル名2 },
    -- まず、1つ目のフィールドと2つ目のフィールドが同じレコードを取得する。
WHERE
    -- 次に、上記で取得したレコードのうち、次の条件も満たすレコードのみを取得する。
    { レコード名1 } = { レコード名2 }
    AND { レコード名2 } = { レコード名3 }

▼ 内部結合にINNER JOIN ONを使用する場合

*実装例*

-- 『カラム』のみでなく、どの『表』なの物なのかも指定
SELECT
    { テーブル名1 }.{ カラム名1 },
    -- 複数の表を指定
FROM
    { テーブル名1 }
    -- 2つ目の表の『レコード』と照合
    INNER JOIN { テーブル名2 }
    ON { テーブル名1 }.{ カラム名1 } = { テーブル名2 }.{ カラム名2 }
    -- 3個目の表の『レコード』と照合
    INNER JOIN { テーブル名3 }
    ON { テーブル名1 }.{ カラム名1 } = { テーブル名3 }.{ カラム名3 }


ORDER BY

▼ 使い方

*実装例*

<?php
$joinedIdList = implode(",", $idList);

// 並び替え条件を設定
$expression = call_user_func(function () use ($orders, $joinedIdList) {
    if ($orders) {
        foreach ($orders as $key => $order) {
            switch ($key) {
                case "id":
                    return sprintf("ss.id %s", $order);
            }
        }
    }

    // IN句順の場合
    return sprintf("FIELD(ss.id, %s)", $idList);
});

$sql = <<<SQL
            SELECT
                name
            FROM
                table
            ORDER BY {$expression}
        SQL;


IN句、ANY句の違い

IN句の使い方

指定した値と同じ『フィールド』を取得する。

*実装例*

指定したカラムで、指定した値の『フィールド』を取得する。

SELECT
    *
FROM
    { テーブル名 }
WHERE
    { カラム名 } in (foo, bar,...);

指定したカラムで、指定した値以外の『フィールド』を取得する。

SELECT
    *
FROM
    { テーブル名 }
WHERE
    { カラム名 } not in ({ レコード名1 }, { レコード名2 },...);

指定したカラムで、SELECTで読み出した値以外の『フィールド』を取得する。

SELECT
    *
FROM
    { テーブル名 }
WHERE
    { カラム名 } not in (
        --
        SELECT
            { カラム名 }
        FROM
            { テーブル名 }
        WHERE
            { レコード名 } >= 160
    );

【IN句を使用しなかった場合】

SELECT
    *
FROM
    fruit
WHERE
    name = "みかん"
    OR name = "りんご";

【IN句を使用した場合】

SELECT
    *
FROM
    fruit
WHERE
    name IN("みかん", "りんご");

ANY句の使い方

書き方が異なるのみで、inと同じ出力

SELECT
    *
FROM
    { テーブル名 }
WHERE
    { カラム名 } = ANY(foo, bar, baz);


GROUP BY

▼ 使い方

カラムをグループ化し、集合関数を使用して、フィールドの値を算出する。

*実装例*

指定したカラムをグループ化し、フィールドの値の平均値を算出する。

SELECT
    { カラム名1 },
    AVG({ カラム名2 })
FROM
    { テーブル名 }
GROUP BY
    { カラム名1 };


HAVING

▼ 使い方

各句の処理の順番から考慮して、GROUP BYでグループ化した結果から、HAVINGで『フィールド』を取得する。

SELECTにおける集計関数が、HAVINGにおける集計関数の結果を指定していることに注意せよ。

*実装例*

-- HAVINGによる集計結果を指定して出力。
SELECT
    { カラム名1 },
    COUNT({ カラム名2 })
FROM
    { テーブル名 }
GROUP BY
-- グループ化した結果を集計し、2個以上の『フィールド』を取得する。
    { カラム名1 }
HAVING
    COUNT(*) >= 2;

以下の場合、GROUP BY + HAVINGWHEREを使用しても、同じ出力結果になる。

SELECT
    { カラム名 }
FROM
    { テーブル名 }
GROUP BY
    { カラム名 }
HAVING
    { レコード名 };
SELECT
    { カラム名 }
FROM
    { テーブル名 }
WHERE
    { レコード名 }
GROUP BY
    { カラム名 };


WILDCARD

▼ 使い方

*実装例*

SELECT
    *
FROM
    { テーブル名 }
WHERE
    { カラム名 } LIKE "%営業";
SELECT
    *
FROM
    { テーブル名 }
WHERE
    { カラム名 } LIKE "_営業";


BETWEEN

▼ 使い方

*実装例*

指定したカラムで、1以上10以下の『フィールド』を取得する。

SELECT
    *
FROM
    { テーブル名 }
    BETWEEN 1
    AND 10;


SET

▼ 使い方

*実装例*

SET
    @A = { パラメーター値 };

SET
    @B = { パラメーター値 };

UPDATE
    { テーブル名 }
SET
    { カラム名 } = @A,
WHERE
    { カラム名 } = @B;


サブクエリ

▼ 使い方

掛け算と同様に、括弧内から先に処理を実行する。

*実装例*

-- Main-query
SELECT
    *
FROM
    { テーブル名 }
WHERE
    { カラム名 } != (
        -- Sub-query
        SELECT
            max({ カラム名 })
        FROM
            { テーブル名 }
    );


Tips

▼ 各DBサイズの確認

SELECT
    table_schema,
    sum(data_length) / 1024 / 1024 AS mb
FROM
    information_schema.tables
GROUP BY
    table_schema
ORDER BY
    sum(data_length + index_length) DESC;

▼ カラムの検索

SELECT
    table_name,
    column_name
FROM
    information_schema.columns
WHERE
    column_name = { 検索したいカラム名 }
    AND table_schema = { 検索対象のDB名 }


03. EXPLAIN

実行計画

設定したSELECT句が仮に実行された場合、いずれのテーブルのいずれのカラムを取得することになるか (実行計画) を取得する。

また、想定実行時間も検出できるため、スロークエリの検出に役立つ。

EXPLAIN SELECT
    *
FROM
    t1,
    t2
WHERE
    t1.c1 = 1
    AND t1.c2 = t2.c3
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t1
         type: ref
possible_keys: index_t1_on_c1_and_c2
          key: index_t1_on_c1_and_c2
      key_len: 5
          ref: const
         rows: 10
        Extra: Using where; Using index
*************************** 2. row ***************************
           id: 1
  select_type: SIMPLE
        table: t2
         type: ref
possible_keys: index_t2_on_c3
          key: index_t2_on_c3
      key_len: 5
          ref: sample.t1.c2
         rows: 1
        Extra: Using index


読み方

select_type

SQLの種類が表示される。

サブクエリを含まないSQLはSIMPLEとなり、サブクエリを含むと、サブクエリの種類に応じて、PRIMARYSUBQUERYDEPENDENT SUBQUERYUNCACHEABLE SUBQUERYDERIVED、のいずれかが表示される。

table

設定したSELECT句がアクセスするテーブル名が表示される。

type

設定したSELECT句がテーブルにアクセスする時に、どの程度の数のカラムを検索するのかが表示される。

検索するカラムが多いSQLほど、想定実行時間が長くなる。

種類 条件 検索するカラム数 補足
ALL ・DBインデックスを使用していない。 全てのカラム 全てのカラムを検索するため、実行時間が最も長く、改善する必要がある。
index ・DBインデックスを使用していない。 全てのインデックスのカラム
range ・セカンダリーDBインデックスを使用している。
WHERE句に重複したレコード値、IN句、BETWEEN句を使用している。
特定の複数カラム
ref ・セカンダリーDBインデックスを使用している。
WHERE句に重複しないレコード値
特定の複数カラム
eq_ref ・クラスタDBインデックスを使用している。 1個のカラム 1個のカラムしかfetchしないため、JOIN句を使用したアクセスの中で、実行時間が最も短い。
const ・クラスタDBインデックスを使用している。
JOIN句を使用していない。
1個のカラム 1個のカラムしかfetchしないため、実行時間が最も短い。

possible_keys

DBインデックスとして設定されたカラムのうちで、実際に利用できるものの一覧が表示される。


05. INSERT

バルクインサート

一度のINSERT文で複数の値を挿入する。

INSERT INTO { テーブル名 } VALUES ('<カラム名>','<レコード値>'), ('<カラム名>','<レコード値>'), ('<カラム名>','<レコード値>');


06. EXEC

stored procedure

▼ stored procedureとは

あらかじめ一連のSQL文をDBに格納しておき、Call文でコールする方式。

p325

▼ 使い方

*実装例*

SELECT文のstored procedureを作成する例を考える。

-- PROCEDUREを作成し、DBへ格納しておく。
CREATE PROCEDURE SelectContact AS
SELECT
    { カラム名 }
FROM
    { テーブル名 }
-- PROCEDUREを実行
EXEC SelectContact