SQL Server にて扱える標準 SQL を中心にまとめた自分用メモ。
他の RDBMS 向けの内容も一部混在しているため注意。

kenkenpa198/mssql-with-docker にて管理していたファイルを当サイト上へ引っ越したもの。

リンク

目次

1. 環境関連

1.1. SQL Server 環境のバージョン・エディションを確認する

参考: SQL Server - sqlcmd でバージョンとエディションを確認する方法

SELECT @@VERSION;

/***************************
Microsoft SQL Server 2019 (RTM-CU15) (KB5008996) - 15.0.4198.2 (X64)
    Jan 12 2022 22:30:08
    Copyright (C) 2019 Microsoft Corporation
    Express Edition (64-bit) on Linux (Ubuntu 20.04.3 LTS) <X64>
***************************/

1.2. 環境で使用されている文字コードを調べる

参考: SQLServer日本語コードの確認方法 - Qiita

-- 先に確認したい列の照合順序を確認しておく(参考ページを参照)

SELECT COLLATIONPROPERTY('Japanese_CI_AS', 'CodePage');

-- 932   : Shift-JIS
-- 1200  : UTF-16
-- 65001 : UTF-8
-- 20932 : EUC-JP

-- 確認できるのは非 UNICODE 型の文字列型(char, varchar, text)に使用される文字コードのみ。
-- UNICODE 型の文字列型(nchar, nvarchar, ntext)はそもそも UTF-16 固定となる。

2. データベース操作

2.1. データベース一覧を取得する

SELECT name FROM sys.databases;

2.2. データベースを作成する

CREATE DATABASE <database-name>;

2.3. 使用するデータベースを設定する

USE <database-name>;

2.4. 使用しているデータベースを確認する

SELECT DB_NAME();

3. テーブル操作

3.1. テーブル一覧を表示する

SELECT name FROM sysobjects WHERE xtype = 'U';

3.2. テーブルの定義を表示する

すべての情報を出力する。
自作 SQL 集 > 2. カラムの情報を見やすい形式で表示する も参考に。

SELECT * FROM sys.columns WHERE object_id = object_id('Members');

3.3. テーブルを作成する

CREATE TABLE Members (
--  列名          データ型     この列の制約
    member_id     CHAR(4)      NOT NULL,
    member_name   VARCHAR(100) NOT NULL,
    sex           VARCHAR(32)  NOT NULL,
    age           INTEGER      ,
    foo_num       INTEGER      ,
    date_of_birth DATE         ,

--  このテーブルの制約
    PRIMARY KEY (member_id) -- 列の制約に「PRIMARY KEY」と付けてもよい
);

3.4. テーブルを削除する

DROP TABLE Members;

3.5. テーブルの定義を変更する

3.5.1. 列を追加する場合

ALTER TABLE <テーブル名> ADD COLUMN <列の定義>;

-- [DB2][PostgreSQL][MySQL]
ALTER TABLE Members ADD COLUMN hobby VARCHAR(128);

-- [SQL Server][Oracle] COLUMN をつけない
ALTER TABLE Members ADD hobby VARCHAR(128);

-- [Oracle] () をつけて複数の定義を一度に追加できる
ALTER TABLE Members ADD (hobby VARCHAR(128), pet VARCHAR(128));

3.5.2. 列を削除する場合

ALTER TABLE <テーブル名> DROP <列名>

-- [SQL Server][DB2][PostgreSQL][MySQL]
ALTER TABLE Members DROP COLUMN hobby;

-- [Oracle] COLUMN を省略できる。() をつけて複数の列を一度に削除できる
ALTER TABLE Members DROP (hobby, pet);

3.5.3. 列の設定を変更する場合

ALTER TABLE <テーブル名> ALTER COLUMN <列の定義>;

-- 列の定義を変更
ALTER TABLE Members ALTER COLUMN member_id INTEGER NOT NULL;
ALTER TABLE Members ALTER COLUMN first_name NVARCHAR(10);

3.5.4. 列へ主キーを付与する場合

ALTER TABLE Members ADD PRIMARY KEY (member_id, first_name);

4. ビュー

4.1. ビューを作成する

CREATE VIEW
    MembersSexCnt (
        -- ビューの列名(並び順が SELECT 文の結果と対応)
        sex,
        sex_count
    )
AS
    -- ビュー定義の本体(SELECT 文)
    SELECT
        sex,
        COUNT(*)
    FROM
        Members
    GROUP BY
        sex
;

4.2. ビューを削除する

DROP VIEW MembersSexCnt;

5. トランザクション

5.1. トランザクションの作成

-- [SQL Server][PostgreSQL]
BEGIN TRANSACTION
    ... DML 文(INSERT / UPDATE / DELETE 文) ...
COMMIT -- または ROLLBACK

-- [MySQL]
START TRANSACTION
    ... DML  ...
COMMIT -- または ROLLBACK

-- [Oracle][DB2]
ない
-- 例(SQL Server)
BEGIN TRANSACTION

    -- カッターシャツの販売単価を 1000 円値引き
    UPDATE Shohin
       SET hanbai_tanka = hanbai_tanka - 1000
     WHERE shohin_mei = 'カッターシャツ';

    -- カッターシャツの販売単価を 1000 円値引き
    UPDATE Shohin
       SET hanbai_tanka = hanbai_tanka + 1000
     WHERE shohin_mei = 'Tシャツ';

COMMIT

6. レコード操作

6.1. レコードを追加する

6.1.1. 基本

-- [SQL Server][PostgreSQL]
BEGIN TRANSACTION; -- 行の追加を開始する
    INSERT INTO <テーブル名> (1, 2, 3, ...) VALUES (1, 2, 3, ...);
COMMIT;            -- 行の追加を確定する

※ MySQL の場合、BEGIN TRANSACTION; を下記に変更する。

START TRANSACTION;

※ Oracle, DB2 の場合は BEGIN TRANSACTION; が必要ないので削除する。

6.1.2. 列リストの省略

テーブルの前列に対して INSERT を行う場合は列リストを省略できる。

BEGIN TRANSACTION;

--                          member_id  member_name  sex       age  foo_num  date_of_birth
INSERT INTO Members VALUES ('0001',    'Modane',    'female', 18,  111,     '2019-09-08');
INSERT INTO Members VALUES ('0002',    'Ryunosuke', 'male',   21,  NULL,    '2017-09-29');
INSERT INTO Members VALUES ('0003',    'Tsukune'  , 'female', 19,  222,     NULL);

COMMIT;

6.1.3. SELECT 句での抽出結果から INSERT する

INSERT INTO Members (
    member_id,
    member_name,
    date_of_birth
)
SELECT
    member_id,
    member_name,
    date_of_birth
FROM
    Foo_Table
;

-- すべての列へ値を入れる際は Members のカラム指定 (a, b, c, ...) を省略できる
INSERT INTO Members
SELECT
    member_id,
    member_name,
    date_of_birth
FROM
    Foo_Table
;

-- WHERE 句、GROUP BY 句などを追加することで集計結果を挿入することもできる。

6.2. レコードを削除する

-- レコードを全行削除
DELETE FROM Members;

-- WHERE 句で削除対象の行を指定できる(探索型 DELETE)
DELETE
FROM
    Members
WHERE
    age <= 20
;

6.3. レコードを更新する

-- 指定したカラムを全行更新
UPDATE
    Members
SET
    foo_num = 15
;

-- WHERE 句で更新対象の行を指定できる(探索型 UPDATE)
UPDATE
    Members
SET
    foo_num = 15
WHERE
    member_name = 'Modane'
;

-- 計算した結果で更新することも可能(例 : 指定列の値を10倍する)
UPDATE
    Members
SET
    foo_num = foo_num * 10
;

-- NULL で更新する(NULL クリア)
UPDATE
    Members
SET
    foo_num = NULL
;

-- 複数の列を更新する場合
UPDATE
    Members
SET
    age = age / 2,
    foo_num = foo_num * 10
WHERE
    sex = 'female'
;

7. 検索

7.1. 基本

SELECT
    member_id,
    member_name
FROM
    Members
WHERE
    age <= 20
;

7.2. カッコを使った条件式

-- 「商品分類が事務用品」かつ「登録日が2009年9月11日または2009年9月20日」
WHERE
    shohin_bunrui = '事務用品'
    AND (
        torokubi = '2009-09-11'
        OR torokubi = '2029-09-20'
    )
;

7.3. NULL を除外して行数を数える

COUNT 関数は * を引数に取ることができる。
また、引数へ * を渡した場合とカラム名を渡した場合によって動作が異なる。

-- NULL を含める場合は COUNT 関数の引数へ * を渡す。
SELECT
    COUNT(*) AS count
FROM
    Members
;

-- count : 3

-- NULL を含めない場合は引数へ列名を渡す。
SELECT
    COUNT(date_of_birth) AS count
FROM
    Members
;

-- count : 2

7.4. 相関サブクエリ

外側のクエリとサブクエリの特定のレコード同士を比較する際の書き方。

外側のクエリとサブクエリのキーとするカラムを縛り(バインドし)することで、
比較する値をひとつにして(スカラ・サブクエリ)正常な比較ができるようにする。

-- [SQL Server][DB2][PostgreSQL][MySQL]
-- [Oracle] では AS を削除する
SELECT
    shohin_bunrui,
    shohin_mei,
    hanbai_tanka
FROM
    Shohin AS S1
WHERE
    hanbai_tanka > (
        SELECT
            AVG(hanbai_tanka)
        FROM
            Shohin AS S2
        WHERE
            -- 各商品の販売単価と平均単価の比較を同じ商品分類の中で行う!
            S1.shohin_bunrui = S2.shohin_bunrui
        GROUP BY
            shohin_bunrui

    )
;

-- 結合条件を外側のクエリに書いてしまうと動作しない。
-- 外側のクエリの実行時点でサブクエリのテーブル S2 は消滅しているため(スコープのルール)。

7.5. EXISTS

ある条件に合致するレコードの存在有無を調べる。
IN で代用できるケースは多いが、パフォーマンス性に優れる。

-- EXISTS
SELECT
    *
FROM
    Members m
WHERE
    EXISTS (
        -- WHERE 句の条件を満たすレコードが存在した場合に真を返す
        SELECT
            * -- なんでもよい。1 でもよい
        FROM
            MembersSexCnt sc
        WHERE
            m.sex = sc.sex -- キーとして結合するカラムを指定
            AND sex_count >= 2 -- 条件を指定する場合は AND で繋げる
    )
;

7.6. ウィンドウ関数

別名 OLAP 関数(OnLine Analytical Processing)。
データを集約せずにデータ分析を行える。

基本は下記のような感じ。

SELECT
    <ウィンドウ関数> OVER ([PARTITION BY <列リスト>] ORDER BY <ソート用列リスト>)

-- [] 内は省略できる。
-- <ウィンドウ関数> には下記2種の関数を指定できる。
--   1. 集約関数(SUM, AVG, COUNT など)
--   2. ウィンドウ専用関数(RANK, DENSE_RANK, ROW_NUMBER など)

例)

-- 商品分類別に、販売単価の安い順で並べたランキング表を作る
SELECT
    shohin_mei,
    shohin_bunrui,
    hanbai_tanka,
    RANK() OVER (PARTITION BY shohin_bunrui ORDER BY hanbai_tanka) AS ranking
FROM
    Shohin;
-- ORDER BY     -- 結果で並べたいときは最後に ORDER BY 句をつける
--     ranking

shohin_mei | shohin_bunrui | hanbai_tanka | ranking
---------- + ------------- + ------------ + --------
フォーク   | キッチン用品  |          500 |        1
おろしがね | キッチン用品  |          880 |        2
Tシャツ    | 衣服          |         1000 |        3
ボールペン | 事務用品      |          500 |        1

7.7. GROUPING

coming soon…(いつまでコレやねん)

8. 参考文献