SQL問い合わせとリレーショナル代数の基礎

リレーショナル代数はリレーショナル・データベースの理論的基盤であり,集合論に基づいている.これは,和集合や差集合などの基本的集合演算と,射影,選択,θ-結合,商などのリレーショナル固有の演算から構成される.θ-結合は重要な概念で,2つのリレーションの直積集合から特定条件を満たす要素のみを選択する演算であり,「R[AiθBj]S」と表記される.SQL問い合わせは,SELECT,FROM,WHERE句を組み合わせて条件を満たすデータを抽出することを特徴とする.θ-結合,はSQLにおいて「SELECT * FROM R, S WHERE R.AiθS.Bj」の形で実現される.データベースの整合性を保証するためにトランザクション機能が重要である.トランザクションは「begin transaction;」で開始し「commit;」で確定する一連の操作を単一のものとして扱い,エラー発生時には「rollback;」によってトランザクション内のすべての変更を取り消すことができる.

大学で使用した自作の資料等を,手直しの上公開している. クリエイティブ・コモンズ BY NC SA.

演習で行うこと

演習を行うために必要になる機能や文法

SQLite 3のSQL仕様の詳細は http://www.hwaci.com/sw/sqlite/lang.html (English Web Page) をご参照ください.

SQLiteman の起動と終了

SQL コマンドの編集や実行,データベース管理のためのインターフェースとして SQLiteman を活用する.

  1. SQLiteman の起動

    * Windows での SQLiteman の起動方法

    「SQLiteman」アイコンをダブルクリックして起動する

    SQLiteman の新規ウィンドウが開き,操作が可能になる.

    * Ubuntu での SQLiteman の起動方法

    端末で「sqliteman」と入力し,Enter キーを押すとアプリケーションが起動する.

    SQLiteman の新規ウィンドウが開き,作業を開始できる.

  2. ヘルプの表示

    Help」メニューから 「Help Content」 を選択してヘルプ情報にアクセスする.

    ヘルプ内容を確認し,SQLiteman の機能や使用方法について理解を深める.

  3. SQLiteman の終了

    File」メニューから 「Exit」を選択して適切に終了する.

SQLiteman で新しいデータベースを作成する

以下の手順で新規データベースを効率的に作成する.この操作により永続的なデータベースファイルが生成される.

  1. 新規データベース作成のため, 「File」メニューから 「New」オプションを 選択する.
  2. データベースの新規作成を開始する

    * Ubuntu での実行例(データベースファイル名「mydb2」を作成する場合)

    データベースファイル名として mydb2 を入力し, 「保存」ボタンをクリックして確定する.

    * データベースファイル名は任意だが,検索性と互換性のため英語名を強く推奨する.

    * 新規データベースを作成する場合は,存在しない新しいファイル名を指定し,データの上書きを防止する

  3. データベースの内容を確認する

    初期状態では,テーブル (Tables) 数もビュー (Views) 数も 0 であり,これから構築していく.

  4. テキスト・エンコーディングの設定を確認する

    まず,「Pragmas」タブをクリックして設定を表示する.

    encoding行に「UTF-8」と表示されていることを確認し,文字化けを防止する.

    * データベースの文字エンコーディングを変更する必要がある場合は, SQLiteman などのGUIツールではなく, sqlite.exe を使用し,「PRAGMA encoding=...;」コマンドで変更することを推奨する. 例えば「UTF-16le」に変更する場合は「PRAGMA encoding=UTF-16le;」を実行する.

  5. 終了

    操作の混乱を防ぐため,一旦 SQLiteman を終了し,作業を区切る.

    File」メニューから 「Exit」を選択して終了する.

SQLiteman で既存のデータベースを開く

既存のデータベースは,以下の手順で効率的に開くことができる.

以下のステップで,既存のデータベースファイルを正確に開き,作業を継続する.

  1. File」メニューから 「Open」を選択してファイル選択ダイアログを表示する.
  2. データベースファイルを開く

    * Windows での実行例(「C:\SQLite\mydb」を開く場合)

    データベースファイル C:\SQLite\mydb を選択し, 「開く」ボタンをクリックして読み込みを開始する

    * Ubuntu での実行例(「SQLite/mydb」を開く場合)

    データベースファイル mydb2 を選択し, 「開く」ボタンをクリックして処理を継続する

    ホームディレクトリ直下mydb2 ファイルを正確に選択する. 

  3. データベースの内容を確認し,構造を把握する

    ◆ 表示例(データベースの内容により表示が異なる)

  4. 「Tables」の数が1以上の場合は展開して詳細を確認できる

    「Tables」の数が1以上の場合,「Tables」を展開するとテーブル一覧 (List of Tables) が表示され,データベース構造を把握できる

    ◆ 展開例

  5. テキスト・エンコーディングの設定を確認し,文字化けを防止する

    まず,「Pragmas」タブをクリックして設定情報を表示する.

    encoding行に「UTF-8」と表示されていることを確認し,正しい文字表示を保証する.

    * データベースの文字エンコーディングを変更する必要がある場合は, SQLiteman などのGUIツールではなく, sqlite.exe を使用し「PRAGMA encoding=...;」コマンドで変更することを推奨する. 例えば「UTF-16le」に変更する場合などは,コマンドラインからの操作が確実である.

  6. 「System Catalogue」を展開し「sqlite_master」をクリックすると,データベース・スキーマ (database schema) が表示され,テーブル構造を詳細に確認できる

    ◆ 表示例(1)

    データベースが空の場合,表示も空となり,テーブル定義が必要であることを示している.

    ◆ 表示例(2)

    score_records, order_records などのテーブルが定義済みの場合,それらの構造情報が表示される

SQL を用いたテーブル定義と制約の記述

SQL を使用して,products テーブルを効率的に定義し,データ整合性を保証する制約を設定する.

リレーショナル・スキーマ (relational schema): products(id, product_name, type, price, created_at, updated_at)
  1. products テーブルの定義

    以下のSQL文を正確に入力し,「Run SQL」アイコンをクリックして実行する

    create table products (
        id            integer  primary key autoincrement not null,
        product_name  text     UNIQUE not null,
        type          text     not null,
        price         real,
        created_at    datetime not null,
        updated_at    datetime );
    

    * 「SQL Editor」ウィンドウで正確なSQL文を作成することで,効率的なデータベース構築が可能になる.

  2. コンソール出力の確認

    エラーメッセージが表示されていないことを確認し,テーブル作成の成功を確認する.

SQL を用いたテーブルへの行の挿入

以下のような構造と内容を持つ products テーブルを作成する.

以下の手順で,SQL を使用して products テーブルに必要なデータ行を正確に挿入する

  1. SQL プログラムの作成

    「insert into ...」文で行を挿入する.ここでは 4つのSQL文を記述し, 「begin transaction」と「commit」で囲むことでデータ整合性を保証する.

    * つまり, 挿入前に begin transaction; を実行してトランザクションを開始し,一連の挿入が完了後に commit; を実行してトランザクションを確定する.

    begin transaction;
    insert into products values( 1, 'Fukuoka apple',     'apple',  50, datetime('now', 'localtime'), NULL );
    insert into products values( 2, 'Kumamoto orange L', 'orange', 30, datetime('now', 'localtime'), NULL );
    insert into products values( 3, 'Kumamoto orange M', 'orange', 20, datetime('now', 'localtime'), NULL );
    insert into products values( 4, 'Fukuoka melon',     'melon',  NULL, datetime('now', 'localtime'), NULL );
    commit;
    

    insert into 文には2つの記述方法があり,状況に応じて適切な方法を選択できる.

    * テーブル定義の順序で全属性値を列挙する方法

    insert into products values( 1, 'Fukuoka apple',     'apple',  50, datetime('now', 'localtime') );
    

    * 属性名を指定して値の順序を明示的に指定する方法

    この場合,属性値を省略すると,テーブル定義時に指定されたデフォルト値が自動的に使用される

    insert into products values( 2, 'Kumamoto orange L', 'orange', 30, datetime('now', 'localtime') );
    
  2. 複数SQL文の一括実行

    複数のSQL文を効率的に一括実行するため,カーソルを先頭行に正確に移動し, 「Run multiple SQL statements ...」ボタンをクリックして処理を開始する.

  3. 「Script Output」ウィンドウの確認

    エラーメッセージが表示されていないことを確認し,データ挿入の成功を確認する.

    * エラーメッセージが表示された場合は,SQL文を正確に修正し,再度「Run multiple SQL statements ...」ボタンをクリックしてエラーを解消する.

    例えば,以下のようなエラーが発生した場合の対処法:

    • 「begin transaction」は完了しているが,それ以降の処理が中断されている.
    • それ以降の文は実行されていないため,再開する必要がある.

    この場合,再開したい行に正確にカーソルを移動し,「Run multiple SQL statements ...」ボタンをクリックして処理を継続する.

SQL 問い合わせの発行と評価結果の確認

ここでは,SQL による問い合わせの実行例を示し,効率的なデータ検索方法を説明する. SQL 問い合わせの詳細は別ページで説明するが,ここではテーブルの内容を確認する基本的な方法を学ぶ.

テーブルの全行を表示して全体像を把握する

SELECT * FROM products;

条件を満たす行のみを絞り込んで表示する

SELECT * FROM products WHERE type = 'orange';
SELECT * FROM products WHERE price > 25;
SELECT * FROM products WHERE price > 18;

SQL を用いたテーブル定義と一貫性制約の記述

SQL を使用して,orders テーブルを定義し,一貫性制約を設定する.データベースの基本となるテーブル構造と完全性を確保するための制約を適切に記述することが重要である.

  1. orders テーブルの定義

    以下のSQL文を入力し,「Run SQL」アイコンをクリックする.このSQL文は主キー,制約条件,既定値など重要な要素を含んでいる.

    create table orders (
        id            integer  primary key autoincrement not null,
        year          integer  not null check ( year > 2008 ),
        month         integer  not null check ( month >= 1 AND month <= 12 ),
        day           integer  not null check ( day >= 1 AND day <= 31 ),
        customer_name text     not null,
        product_id    text     not null,
        qty           integer  not null DEFAULT 1 check ( qty > 0 ),
        created_at    datetime not null );
    
  2. コンソール出力の確認

    エラーメッセージが表示されていないことを確認する.エラーがない場合,テーブルが正常に作成されたことを意味する.

SQL を用いたテーブルへの行の挿入

以下のような orders テーブルを作成する.このテーブルには注文データが格納される.

以下の手順で,SQL を使用して orders テーブルに行を挿入する.データベースにデータを格納するための基本操作である.

  1. SQL プログラムの作成

    「insert into ...」文で行を挿入する.ここでは 4つのSQL文を記述し, 「begin transaction」と「commit」でトランザクション処理として囲む.これにより,データの一貫性が保たれる.

    begin transaction;
    insert into orders values( 1001, 2014, 10, 26,  'kaneko',   1, 10, datetime('now', 'localtime') );
    insert into orders values( 1002, 2014, 10, 26,  'miyamoto', 2, 2, datetime('now', 'localtime') );
    insert into orders values( 1003, 2014, 10, 27,  'kaneko',   3, 8, datetime('now', 'localtime') );
    insert into orders values( 1004, 2014, 10, 27,  'kaneko',   3, 8, datetime('now', 'localtime') );
    commit;
    
  2. 複数SQL文の一括実行

    複数のSQL文を一括実行するため,カーソルを先頭行に移動し,「Run multiple SQL statements ...」ボタンをクリックする.これにより,すべてのSQL文が順番に実行される.

  3. 「Script Output」ウィンドウの確認

    エラーメッセージが表示されていないことを確認する.正常に実行された場合,データが適切にテーブルに挿入されている.

SQLiteman を用いたデータのブラウズ

SQL 問い合わせの発行と評価結果の確認

直積 (Cartesian product)

SQL を使用して,複数テーブルの直積を容易に取得できる.直積は2つ以上のテーブルのすべての可能な組み合わせを生成する演算である.

SELECT *
FROM orders, products;

結合問い合わせ (join query)

結合問い合わせは,直積から条件を満たす行を抽出したものとなる.これにより,複数テーブルから関連するデータを効率的に取得できる.

List all 'name', 'price' and 'orders.qty' that satisfy "orders.month = 10"

SELECT products.product_name, products.price, orders.qty
FROM products, orders
WHERE products.id = orders.product_id
           AND orders.month = 10;

SQL を用いたテーブル定義と一貫性制約の記述


実体関連図 (Entity Relationship Diagram)

SQL を使用して,bundles テーブルと shippings テーブルを定義し,一貫性制約を設定する.これにより,データベースの整合性を確保することができる.

  1. bundles テーブルの定義

    以下のSQL文を入力し,「Run SQL」アイコンをクリックする (

    create table bundles (
        id          integer  primary key autoincrement not null,
        order_id    integer  not null REFERENCES orders(id),
        qty         integer  not null,
        shipping_id integer  not null REFERENCES shippings(id),
        created_at  datetime not null );
    
  2. コンソール出力の確認

    エラーメッセージが表示されていないことを確認する.これにより,SQLの実行が正常に完了したことが検証できる.

  3. shippings テーブルの定義

    以下のSQL文を入力し,「Run SQL」アイコンをクリックする

    create table shippings (
        id         integer  primary key autoincrement not null,
        year       integer  not null check ( year > 2008 ),
        month      integer  not null check ( month >= 1 AND month <= 12 ),
        day        integer  not null check ( day >= 1 AND day <= 31 ),
        created_at datetime not null );
    
  4. コンソール出力の確認

    エラーメッセージが表示されていないことを確認する.正常にテーブルが作成されたことを示している.

  5. テーブル一覧の表示

    オブジェクト・ブラウザ (Object Browser) の「Tables」を展開する (Click 'Tables'). テーブル一覧が表示され,作成したテーブルを確認することができる.

SQL を用いたテーブルへの行の挿入

テーブル間の関係

* 以下の手順で,SQL を使用して bundles テーブルに行を挿入する.トランザクション管理を適切に行うことでデータの整合性を保つ.

  1. SQL プログラムの作成
    begin transaction;
    insert into bundles values( 1, 1001, 2, 101, datetime('now', 'localtime') );
    insert into bundles values( 2, 1001, 1, 102, datetime('now', 'localtime') );
    insert into bundles values( 3, 1002, 1, 103, datetime('now', 'localtime') );
    insert into bundles values( 4, 1003, 2, 104, datetime('now', 'localtime') );
    insert into bundles values( 5, 1004, 4, 104, datetime('now', 'localtime') );
    commit;
    
  2. 複数SQL文の一括実行

    複数のSQL文を一括実行するため,カーソルを先頭行に移動し,「Run multiple SQL statements ...」ボタンをクリックする.これにより,トランザクション内のすべての挿入操作が実行される.

  3. 「Script Output」ウィンドウの確認

    エラーメッセージが表示されていないことを確認する.これは,すべてのSQL文が正常に実行されたことを意味する.

* 以下の手順で,SQL を使用して shippings テーブルに行を挿入する.各出荷情報を適切にデータベースに記録する.

  1. SQL プログラムの作成
    begin transaction;
    insert into shippings values( 101, 2019, 10, 28, datetime('now', 'localtime') );
    insert into shippings values( 102, 2019, 10, 31, datetime('now', 'localtime') );
    insert into shippings values( 103, 2009, 11,  1, datetime('now', 'localtime') );
    insert into shippings values( 104, 2009, 11,  2, datetime('now', 'localtime') );
    commit;
    
  2. 複数SQL文の一括実行

    複数のSQL文を一括実行するため,カーソルを先頭行に移動し,「Run multiple SQL statements ...」ボタンをクリックする.トランザクション全体が一つの単位として処理される.

  3. 「Script Output」ウィンドウの確認

    エラーメッセージが表示されていないことを確認する.これにより,データが正常に挿入されたことが確認できる.

SQLiteman を用いたデータのブラウズ

SQL 問い合わせの発行と評価結果の確認

直積集合 (Cartesian product)

SQL を使用して,複数テーブルからの直積を容易に取得できる.直積操作は,二つのテーブルのすべての行の組み合わせを生成する基本的なリレーショナル演算である.

SELECT *
FROM shippings, bundles, orders;

結合問い合わせ (join query)

結合問い合わせは,直積から条件を満たす行を抽出したものとなる.WHERE句を使用することで,関連するデータのみを効率的に取得できる.

List all 'shippings.month', 'shippings.day' and 'bundles.qty' that satisfy "orders.day = 27"

SELECT shippings.month, shippings.day, bundles.qty
FROM shippings, bundles, orders
WHERE orders.id = bundles.order_id
           AND shippings.id = bundles.shipping_id
           AND orders.day = 27;

演習問題と解答例

以下の問いに答え,その後,解答例を確認する.SQL問い合わせの実行結果を正確に理解することで,データベース操作のスキルを向上させることができる. Answer the following questions. Then, inspect answers described below.

問い

  1. 次のSQL問い合わせの評価結果は何か?
    SELECT products.product_name, orders.qty
    FROM products, orders
    WHERE products.id = orders.product_id
               AND orders.qty > 2;
    

    解答例

    Fukuoka apple     |3
    Kumamoto orange M |4
    

  2. 次のSQL問い合わせの評価結果は何か?
    SELECT shippings.year, shippings.month, shippings.day
    FROM shippings, bundles, orders, products
    WHERE products.id = orders.product_id
               AND orders.id = bundles.order_id
               AND shippings.id = bundles.shipping_id
               AND price > 20;
    

    解答例

    2009|10|28
    2009|10|31
    2009|11|1
    2009|11|2
    
  3. 以下の問に解答する:
    • まず SQLite を使用して,以下のテーブルを作成する.これらのテーブルは銀行の融資データを管理するための基本構造である.
          loan(id, branchname, amount)
          borrow(id, customername, loanid)
      

      テーブル定義のSQLは以下の通りである:

      create table loan (
          id            integer  primary key autoincrement not null,
          branchname    text not null,
          amount        integer );
      
      create table borrow (
          id            integer  primary key autoincrement not null,
          customername  text not null,
          loanid        integer );
      
    • SQLite を使用して,以下のSQLを実行する.これにより,サンプルデータをテーブルに挿入する.
          begin transaction;
          insert into loan values( 1, 'fukuoka', 1000 );
          insert into loan values( 2, 'saga', 2000 );
          insert into loan values( 3, 'saga', 1500 );
          insert into loan values( 4, 'kumamoto', 3000 );
          insert into loan values( 5, 'fukuoka', 2500 );
          commit;
      
          begin transaction;
          insert into borrow values(1001, 'X', 1);
          insert into borrow values(1002, 'X', 2);
          insert into borrow values(1003, 'X', 3);
          insert into borrow values(1004, 'Y', 4);
          insert into borrow values(1005, 'X', 5);
          commit;
      
    • その後,SQLite で以下のSQLを実行したときの結果を答える (Evaluate the following SQL)
          select * from loan;
      

    解答例

    1|fukuoka|1000
    2|saga|2000
    3|saga|1500
    4|kumamoto|3000
    5|fukuoka|2500
    
  4.     select branchname from loan;
    

    解答例

    fukuoka
    saga
    saga
    kumamoto
    fukuoka
    
  5.     select distinct branchname from loan;
    

    解答例

    fukuoka
    saga
    kumamoto
    
  6.     select id, branchname, amount * 1000 from loan;
    

    解答例

    1|fukuoka|1000000
    2|saga|2000000
    3|saga|1500000
    4|kumamoto|3000000
    5|fukuoka|2500000
    
  7.     select id from loan where branchname = 'fukuoka';
    

    解答例

    1
    5
    
  8.     select id from loan where amount < 2000 and amount >= 1000;
    

    解答例

    1
    3
    
  9.     select *
        from loan, borrow;
    

    解答例

    1|fukuoka|1000|1001|X|1
    1|fukuoka|1000|1002|X|2
    1|fukuoka|1000|1003|X|3
    1|fukuoka|1000|1004|Y|4
    1|fukuoka|1000|1005|X|5
    2|saga|2000|1001|X|1
    2|saga|2000|1002|X|2
    2|saga|2000|1003|X|3
    2|saga|2000|1004|Y|4
    2|saga|2000|1005|X|5
    3|saga|1500|1001|X|1
    3|saga|1500|1002|X|2
    3|saga|1500|1003|X|3
    3|saga|1500|1004|Y|4
    3|saga|1500|1005|X|5
    4|kumamoto|3000|1001|X|1
    4|kumamoto|3000|1002|X|2
    4|kumamoto|3000|1003|X|3
    4|kumamoto|3000|1004|Y|4
    4|kumamoto|3000|1005|X|5
    5|fukuoka|2500|1001|X|1
    5|fukuoka|2500|1002|X|2
    5|fukuoka|2500|1003|X|3
    5|fukuoka|2500|1004|Y|4
    5|fukuoka|2500|1005|X|5
    
  10.     select customername, amount
        from loan, borrow;
    

    解答例

    X|1000
    X|1000
    X|1000
    Y|1000
    X|1000
    X|2000
    X|2000
    X|2000
    Y|2000
    X|2000
    X|1500
    X|1500
    X|1500
    Y|1500
    X|1500
    X|3000
    X|3000
    X|3000
    Y|3000
    X|3000
    X|2500
    X|2500
    X|2500
    Y|2500
    X|2500
    
  11.     select customername, amount
        from loan, borrow
        where loan.id = borrow.loanid;
    

    解答例

    X|1000
    X|2000
    X|1500
    Y|3000
    X|2500
    
  12.     select customername, amount
        from loan, borrow
        where loan.id = borrow.loanid AND borrow.customername = 'X';
    

    解答例

    X|1000
    X|2000
    X|1500
    X|2500