2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

T-SQL歴2,3年目に伝えたい関数や構文

Posted at

本記事は、私がT-SQLを触り始めて数年経った頃にMicrrosoftのドキュメントを斜め読みして知った関数や構文を記載してます。
知る前と知った後ではSQL文の読みやすさが変わりますので知ってて損はないと思います。

各セクションの構成は、簡単な説明と関数や構文を知らない人が書いた場合のSQL、知ってる人が書くであろうSQLを合わせて記載しています。

本記事で伝えたい関数や構文

  1. COALESCE(式)
  2. OVER句(ウィンドウ関数)
  3. GROUP BY GROUPING SETS(GROUP BY句内使用できるキーワード)
  4. PIVOT(FROM句内使用できるキーワード)
  5. 再帰処理

COALESCE(式)

ザックリ説明すると、ISNULL式の引数が複数設定できる版です。
厳密には少し動作が異なるので興味がある場合は1度ドキュメントを見る事を推奨します。

知る前
-- ISNULLで書くパターン
SELECT ISNULL(Item1,ISNULL(Item2,Item3))

-- CASEで書くパターン
SELECT 
    CASE 
        WHEN Item1 IS NOT NULL THEN Item1
        WHEN Item2 IS NOT NULL THEN Item2
        WHEN Item3 IS NOT NULL THEN Item3
        ELSE NULL END
知った後
SELECT COALESCE(Item1,Item2,Item3)

OVER句(ウィンドウ関数)

ザックリ説明すると、取得結果のレコードは集計しなくても、特定のカラムだけ任意の範囲で集計やソートが出来る機能です。

知る前
-- インラインビューパターン
WITH Tmp売上合計 AS 
(
    SELECT
        顧客コード
        ,SUM(売上金額) AS 合計売上金額
    FROM
        売上データ
    GROUP BY
        顧客コード
)
SELECT 
    T1.顧客コード
    ,T1.売上日
    ,T1.売上金額
    ,T2.合計売上金額
FROM
    売上データ AS T1
 LEFT JOIN
    Tmp売上合計 AS T2
   ON T1.顧客コード = T2.顧客コード
;

-- サブクエリパターン
SELECT 
    T1.顧客コード
    ,T1.売上日
    ,T1.売上金額
    ,(
        SELECT 
            SUM(売上金額)
        FROM
            売上データ AS T2
        WHERE T1.顧客コード = T2.顧客コード
        GROUP BY
            顧客コード
      ) AS 合計売上金額
FROM
    売上データ AS T1

知った後
SELECT 
    T1.顧客コード
    ,T1.売上日
    ,T1.売上金額
    ,SUM(T1.売上金額) OVER(PARTITION BY 顧客コード) AS 合計売上金額
FROM
    売上データ AS T1

例ではSUM関数でOVER句を使用しましたが、集計関数(AVG,MAXなど)や順位付け関数(RANK,ROW_NUMBERなど)などに使用できます。

GROUP BY GROUPING SETS(GROUP BY句内使用できるキーワード)

ザックリ説明すると、集計の組み合わせが複数のパターンで指定が出来きるGROUP BYの機能です。

知る前
SELECT '-' AS  ,'-' AS  ,'-' AS  ,SUM(人口) AS 人口 
FROM 人口データ 
--全集計の場合はGROUP BYは省略可能

UNION ALL

SELECT  ,'-' AS  ,'-' AS  ,SUM(人口) AS 人口 
FROM 人口データ 
GROUP BY 

UNION ALL

SELECT  , ,'-' AS  ,SUM(人口) AS 人口 
FROM 人口データ 
GROUP BY  ,

UNION ALL

SELECT  , , ,人口
FROM 人口データ 
知った後
SELECT 
    CASE WHEN GROUPING() = 1 THEN '-' ELSE  END AS  
    ,CASE WHEN GROUPING() = 1 THEN '-' ELSE  END AS 
    ,CASE WHEN GROUPING() = 1 THEN '-' ELSE  END AS 
    ,SUM(人口) AS 人口
FROM
    人口データ 
GROUP BY GROUPING SETS
    (()
    ,()
    ,(,)
    ,(,,))

GROUPING SETS以外にも「ROLLUP 」「CUBE」がありますので興味があれば調べてみてください。
ちなみに、今回の例の場合はROLLUPの方が簡潔に書けます。

SELECT句内でGROUPING関数を使ってますが無くても動作します。GROUPING関数は、引数で渡した項目が集計対象とされているかを判断出来る関数で、該当項目が集計された場合の設定値をNULL以外に変更するときなどに使ったりします。

PIVOT(FROM句内使用できるキーワード)

ザックリ説明すると、特定の列を横に展開し、集計する機能です。

知る前
SELECT
    年度
    ,SUM(CASE WHEN 地方区域 = '東日本' THEN 人口 ELSE 0 END) AS 東日本人口
    ,SUM(CASE WHEN 地方区域 = '中日本' THEN 人口 ELSE 0 END) AS 中日本人口
    ,SUM(CASE WHEN 地方区域 = '西日本' THEN 人口 ELSE 0 END) AS 西日本人口
FROM 人口データ
GROUP BY
    年度
知った後
SELECT
    PV.年度
    ,PV.東日本 AS 東日本人口
    ,PV.中日本 AS 中日本人口
    ,PV.西日本 AS 西日本人口
FROM 人口データ AS T
PIVOT (
    SUM(人口)
    FOR 地方区域 IN (東日本,中日本,西日本)
    ) AS PV

UNPIVOTもあり、挙動はPIVOTの反対で複数のカラムを1つのカラムにまとめる機能です。

再帰処理

ザックリ説明すると、前回の処理結果を使って同様の処理を何度も繰り返す処理です。

サンプルデータ

親コード 子コード
1 10
1 11
1 12
10 100
11 101
10 (空)
知る前
-- 再帰処理の方法を知らない場合は、繰り返す数だけサブクエリを明示的に増やすしかない。
-- ただし、この方法だと動的にSQL文を作らない限り繰り返し回数の上限が必ず存在してしまう。
WITH Tmp1 AS
(
    SELECT
        親コード
        ,子コード
        ,親コード + '/' + 子コード AS 階層
    FROM 
        サンプルデータ
    WHERE
        親コード = 1
),
Tmp2 AS
(
    SELECT
        T1.親コード
        ,T1.子コード
        ,T2.階層 + '/' + T1.子コード AS 階層
    FROM 
        サンプルデータ AS T1
    INNER JOIN
        Tmp1 AS T2
    ON T1.親コード = T2.子コード
),
Tmp3 AS
(
    SELECT
        T1.親コード
        ,T1.子コード
        ,T2.階層 + '/' + T1.子コード AS 階層
    FROM 
        サンプルデータ AS T1
    INNER JOIN
        Tmp2 AS T2
    ON T1.親コード = T2.子コード
)
SELECT * FROM Tmp1 UNION ALL
SELECT * FROM Tmp2 UNION ALL
SELECT * FROM Tmp3
知った後
WITH TmpLoop AS
(
    SELECT
        親コード
        ,子コード
        ,親コード + '/' + 子コード AS 階層
    FROM 
        サンプルデータ
    WHERE
        親コード = 1

    UNION ALL

    SELECT T1.親コード
        ,T1.子コード
        ,T2.階層 + '/' + T1.子コード AS 階層
    FROM
        サンプルデータ AS T1
        ,TmpLoop AS T2
    WHERE
        T1.親コード = T2.子コード 
)
SELECT * FROM TmpLoop
OPTION (MAXRECURSION 2) -- 繰り返し回数の上限

結果

親コード 子コード 階層
1 10 1/10
1 11 1/11
1 12 1/12
10 100 1/10/100
11 101 1/11/101
100 (空) 1/10/100/
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?