Smart -Web Magazine

WEB開発者に嬉しいステキ講座

MENU

LOAD DATA INFILE構文:データのインポート

2009年8月17日 コメントの追加

LOAD DATA INFILE構文のキーワード

LOAD DATA INFILE構文

LOAD DATA INFILEは、テキストファイルのデータをテーブルに登録します。LOAD DATA INFILEがデフォルトで対応している書式は、フィールド区切りがタブ、行末が改行です。

たとえば、サイト名、URL、IPアドレスというフィールド名があるデータベースにLOAD DATA INFILEを使ってデータを挿入したい場合は、下記のようなファイルを作成します(空白部分はタブで区切られています)。

smart    wwww.rfs.jp    	210.123.**.**
rhythm    www.rhythmfactory.jp      210.123.**.**
spacemonkey    www.spacemonkey.jp    210.123.**.**

上記データのファイル名が"data.txt"、それをsiteというテーブルに挿入するには、下記のような構文になります。

LOAD DATA INFILE "data.txt" INTO TABLE site;

上記の例のような項目の区切りや行末の符号以外のファイルをLOAD DATA文で読み込むこともできます。

LOAD DATA INFILE構文

LOAD DATA [LOW_PRIORITY | CONCURRENT] [LOCAL]
INFILE 'ファイル名'
    [REPLACE | IGNORE]
    INTO TABLE テーブル名
    [FIELDS
        [TERMINATED BY '区切り文字']
        [[OPTIONALLY] ENCLOSED BY 'フィールドを囲むキャラクタ']
        [ESCAPED BY 'エスケープシーケンス' ]
    ]
    [LINES TERMINATED BY '\n']
    [IGNORE 数値 LINES]
    [(フィールド名, ...)]

LOAD DATA INFILE構文のキーワード
パラメータ 説明
LOW_PRIORITY LOAD DATAの処理を他の処理よりもプライオリティを下げて行います。
CONCURRENT LOAD DATAの実行中に、他のスレッドがこのテーブルからデータを取り出すことができます。
LOCAL FILE権限なしで、ローカルファイルの読み込みが可能になります。 また、IGNOREと同じように、行の重複エラーがあっても挿入処理が続行されます。
REPLACE 同じユニークキーを持つ既存の行は新しい行で置き換えられます。
IGNORE 既存の行のユニークキーと重複するキーをもつ新しい行は飛ばされます。 
IGNORE 数値 LINES ファイルの先頭にある行を無視するのに使用されます
FIELDS [OPTIONALLY] ENCLOSED BY 項目のシングルクォートを制御します。出力の際(SELECT ... INTO OUTFILE)、OPTIONALLY語を省いたなら、全ての項目はENCLOSED BY文字で囲まれます。

項目区切りにコンマを使用して出力。

"1","a string","100.20"
"2","a string containing a , comma","102.20"

OPTIONALLYを指定すると、CHAR項目とVARCHAR項目のみENCLOSED BY文字で囲まれます。

1,"a string",100.20
2,"a string containing a , comma",102.20

ENCLOSED BYで指定したキャラクタが値の両端に現れた場合、その文字は取り去られます。
ESCAPED BY文字が前置きされたENCLOSED BY文字の出現は、現在の値の一部として処理されます。具体的には、あるフィールドがそれ自身、ENCLOSEDBY文字で始まっている場合、フィールド内部で発生する2重のENCLOSED BY文字は、単一のENCLOSED BY文字として処理されます。 

例えば、ENCLOSED BY '"'が指定されると、シングルクォートは以下のように操作されます。

"The ""BIG"" boss" -> The "BIG" boss
The "BIG" boss -> The "BIG" boss
The ""BIG"" boss -> The ""BIG"" boss

ESCAPED BY 特殊文字をどのように書き込んだり読み込んだりするかを制御します。FIELDS ESCAPED BY'\\'と指定した場合、バックスラッシュ(\)はエスケープ文字として認識されます。

FIELDS ESCAPED BY文字が空でない場合、出力において次のような文字列のプリフィックスに使用されます。

  • FIELDS ESCAPED BY文字 
  • FIELDS [OPTIONALLY] ENCLOSED BY文字
  • FIELDS TERMINATED BY値とLINES TERMINATED BY値の最初の文字
  • ASCII 0 (エスケープ文字の後に続いて実際に書かれる文字はASCII '0'で、'ゼロ値'バイトではありません) 

FIELDS ESCAPED BY文字が空であれば、どの文字もエスケープされません。

入力において、FIELDS ESCAPED BY文字が空でない場合、この文字の出現は取り去られ、後続の文字は値の一部としてそのまま受け取られます。例外は、エスケープされた`0'や`N'です(例えば、エスケープ文字が \ である時の\0や\N)。
これらのシーケンスは、ASCII 0、NULLとして処理されます。

LOAD DATA INFILE構文の使用例
USE db1;
LOAD DATA INFILE "./data.txt" INTO TABLE db2.my_table; 

出力の初期値

  • 改行を行の境界とみなす
  • タブ文字によって項目を区切る
  • クォート文字で項目を囲んでいないものとする
  • タブ文字や改行文字及び\の前に\があることにより、それらを値の一部のリテラル文字であるとして処理する。

入力の初期値

  • 項目間にタブ文字を書く
  • いずれのクォート文字でも項目を囲まない
  • \の使用により、項目値の中で使用するタブ文字や改行文字、\をエスケープする
  • 行の終りに改行文字を書く 

LOAD DATA INFILEは、SELECT ... INTO OUTFILEの補完です。データベースからファイルへデータを書き込むには、SELECT ... INTO OUTFILEを使用します。

LOCAL
LOCAL キーワードが指定されれば、ファイルはクライアント・ホストから読み込まれます。

mysqlimportユティリティは、データファイルの読み込みに使用することができます。
--localオプションは、mysqlimportに、クライアント・ホストからデータファイルを読み込ませます。クライアントとサーバが圧縮プロトコルをサポートしていれば、--compressオプションを指定することができます。 

サーバ・ホストにファイルを置く場合

  • 相対パスと共にファイル名が与えられた場合、サーバは、サーバのデータディレクトリ以下からファイルを探します。 
  • ファイル名だけが単に与えられた場合、カレントのデータベースディレクトリを探します。 

完全なパスでファイル名が与えられた場合、サーバはパス名をそのまま使用します。
1つ又は複数の構成要素から成る相対パスと共にファイル名が与えられた場合、サーバは、サーバのデータディレクトリ以下からファイルを探します。
ファイル名だけが単に与えられた場合、サーバは、カレントのデータベースディレクトリを探します。
これらのルールは、ファイルが myfile.txtのように与えられればデータベースディレクトリからファイルが読み出され、 ./myfile.txtのように与えられれば、現在選択しているデータベースのデータディレクトリからファイルが読み出されるという意味であることに注意して下さい。

例えば、以下の LOAD DATA 文は、data.txt ファイルを db1 データベースディレクトリから読みます。なぜなら、db1は現在選択されているデータベースだからです。たとえ、db2 データベースのテーブルに、ファイルから読み込んだデータを挿入するとしても。

以下に示すような構文では、ファイルは db1 データベースディレクトリから読まれます。db2 ではありません。

mysql> USE db1;
mysql> LOAD DATA INFILE "data.txt" INTO TABLE db2.my_table;

使用例

データベースからファイルへデータを書き、それから後でそのファイルからデータベースへデータを読み戻すために、SELECT ... INTO OUTFILE と対に LOAD DATA INFILEを使う場合、双方の項目と行の取扱いに関するオプションは、一致しなければなりません。さもなければ、LOAD DATA INFILE は適切にファイルを処理しないでしょう。

項目をコンマで区切ってファイルへ書き出すために、SELECT ... INTO OUTFILEを使用するとすれば

SELECT * FROM table1 INTO OUTFILE 'data.txt'
    FIELDS TERMINATED BY ','
    FROM ...

コンマ区切りファイルから読み戻すため、正しいステートメントはこうなるでしょう。

mysql> LOAD DATA INFILE 'data.txt' INTO TABLE table2 FIELDS TERMINATED BY ',';

カンマ( , )区切りと、ダブルクォーテーション( " )囲み、行区切りが改行(\n)であれば、次のようになります。 

LOAD DATA INFILE 'data.txt' INTO TABLE tbl_name
    FIELDS TERMINATED BY ',' ENCLOSED BY '"'
    LINES TERMINATED BY '\n';

LINES TERMINATED BY
リターン文字と改行文字のペアで区切られた行を書き込んだり、このような行を含んだファイルを読み込む場合、LINES TERMINATED BY \r\n を指定します。

項目と行操作オプションが確実に相互作用する事例

  • LINES TERMINATED BYが空文字列でFIELDS TERMINATED BYが空でない場合、各行もまたFIELDS TERMINATED BYで終らせられます。 
  • FIELDS TERMINATED BYとFIELDS ENCLOSED BY値が両方とも空('')の時、(区切られない)固定長行フォーマットが使用されます。固定長行フォーマットでは、項目間に区切り文字列が使用されません。
  • 項目値は、項目の表示幅を使って書き込まれたり、読み込まれます。例えば、ある項目がINT(7)で定義されている場合、項目の値は7文字のフィールドを使って書き込まれます。入力において項目は、7文字の読み込みにより得られます。

FIELDSとLINESオプションによるNULL値の多様な取扱い

  • FIELDSとLINESの初期値のために、出力時にNULLは\Nとして書き込まれ、入力時に\NはNULLとして読み込まれます(ESCAPED BY文字は \ )。
  • FIELDS ENCLOSED BYが空で無い時、定数NULLの項目値はNULL値として読み込まれます。
  • FIELDS ESCAPED BYが空の時、NULLはNULLとして書き込まれます。 
  • 固定長行フォーマット(FIELDS TERMINATED BYとFIELDS ENCLOSED BYがいずれも空の場合)において、NULLは、空白文字列として書き込まれます。

次の例は、persondataテーブルの全ての項目を読み込みます

LOAD DATA INFILE 'persondata.txt' INTO TABLE persondata;

項目リストが指定されていませんから、LOAD DATA INFILEは、入力行がテーブルのそれぞれの項目を含むものと想定します。 

テーブルの一部の項目のみ読み込みたい場合、項目リストを指定します。

LOAD DATA INFILE 'persondata.txt'
INTO TABLE persondata (col1,col2,...);

テーブル内の項目順と入力ファイルの項目順が異なる場合にも、MySQLにテーブルの項目と入力項目の対応を教えるために、項目リストを指定しなければなりません。 

空のフィールド値は変換されます

  • 文字型の場合、 項目は空文字にセット
  • 数値型の場合、項目は 0 にセット
  • 日付と時刻の型の場合、 項目は 「zero」 の意味する値がセット

TIMESTAMP項目は、項目値にNULL値が指定されていた場合もしくは、項目リストが指定されている時に TIMESTAMP項目がそのリストから除外されていた場合 (最初のTIMESTAMP項目のみ)、現在時刻が設定されるだけです。 
入力行の項目数の方が多い場合、余分な項目は無視され、警告が引き起こされます。 

このエントリーをはてなブックマークに追加

SHOW構文:テーブル情報を表示する

2009年8月17日 コメントの追加

SHOW構文を知ろう!

SHOW構文

SHOWはデータベース、テーブル、フィールド、サーバについての情報を与える。LIKEが使用された場合、文字列は通常のSQLワイルドカード (% と _) です。上記の使用例のように、"テーブル名 FROM データベース名"の代わりに、"データベース名.テーブル名"が使用できます。

SHOW 構文

SHOW DATABASES [LIKE 文字列]
or SHOW [OPEN] TABLES [FROM データベース名] [LIKE 文字列]
or SHOW [FULL] COLUMNS FROM テーブル名 [FROM データベース名] [LIKE 文字列]
or SHOW INDEX FROM テーブル名 [FROM データベース名]
or SHOW TABLE STATUS [FROM db_name] [LIKE 文字列]
or SHOW STATUS [LIKE 文字列]
or SHOW VARIABLES [LIKE 文字列]
or SHOW LOGS
or SHOW [FULL] PROCESSLIST
or SHOW GRANTS FOR ユーザ名
or SHOW CREATE TABLE テーブル名
or SHOW MASTER STATUS
or SHOW MASTER LOGS
or SHOW SLAVE STATUS

SHOW構文の使用例

SHOW INDEX FROM mydb.mytable;


構文のキーワード

SHOW構文のキーワード

SHOW DATABASES

データベース名の一覧を表示します。

SHOW DATABASES;
+----------+
| Database |
+----------+
| mydb |
| mysql |
| test |
+----------+

SHOW TABLES

カレントデータベースのテーブル名を一覧表示します。

SHOW TABLES;
+----------------+
| Tables_in_MYDB |
+----------------+
| customer |
| goods |
| purchase |
| purchase_goods |
+----------------+

SHOW FIELDS / SHOW COLUMNS

SHOW COLUMNSは指定されたテーブルのフィールドを表示します。FULLオプションを指定した場合、各フィールドへの権限も表示されます。

SHOW COLUMNS FROM customer;
+-------+-----------------------+------+-----+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------------+------+-----+
| id_c | mediumint(8) unsigned | | PRI | NULL | auto_increment |
| sei | varchar(20) | YES | | NULL | |
| mei | varchar(20) | YES | | NULL | |
| age | tinyint(3) unsigned | YES | | NULL | |
| sex | tinyint(1) unsigned | YES | | NULL | |
+-------+-----------------------+------+-----+

SHOW KEYS / SHOW INDEX

SHOW INDEXはインデックス情報を表示します。

SHOW INDEX FROM customer;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+---------+
| CUSTOMER | 0 | PRIMARY | 1 | id_c | A | 5 | NULL | NULL | |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+---------+

以下の項目が返ります。

フィールド名 意味
Table テーブル名です。
Non_unique インデックスが重複を含まないなら 0
Key_name インデックス名です。
Seq_in_index インデックスのフィールド番号。1 から始まります。
Column_name フィールド名です。
Collation インデックスでの ソート方法です。A (Ascending) か NULL (Not sorted) があいrます。
Cardinality インデックスのユニークな値の数。
Sub_part もしこのフィールドがインデックスに一部分だけ使用している場合、そのインデックスに使用しているキャラクター数をしめす。 もしキー全体がインデックスされているなら NULL 。
Comment Various remarks. For now, it tells whether index is FULLTEXT or not.

SHOW VARIABLES

MySQLシステム変数のいくつかの値を示す。もし標準値が適さないなら、ほとんどの変数をmysqld起動時に命令文ラインのオプションとして与えることにより、変更できる。

SHOW VARIABLES [LIKE wild]

SHOW VARIABLESはMySQLシステム変数のいくつかの値を示します。 mysqlshow variables 命令文でも同じ情報が得られます。もし標準値が適さないなら、ほとんどの変数をmysqld起動時に命令文ラインのオプションとして与えることにより、変更できます。

buffer size, buffer length, stack size は byte単位で与えます。これらの値の後ろにKやMを追加すると、キロバイト、メガバイトになります。例えば、16Mは16メガバイトを示します。大文字小文字の区別はなく、16Mと16mは同じ意味になります。

SHOW PROCESSLIST

そのスレッドが走っているかを表示する。もし process 特権があるなら、全てのスレッドがみれる。しかし特権がないなら、自分のスレッドしか見れない。

SHOW PROCESSLIST はどのスレッドが走っているかを表示します。 mysqlshow processlist 命令文でも同じ情報が得られます。もし process 権限があるなら、全てのスレッドがみれます。しかし権限がないなら、自分のスレッドしか見れません。 「4.5.5 KILL 構文」節参照. FULL オプションを使用しない場合、各クエリの最初の100文字だけが表示されます。

This command is very useful if you get the 'too many connections' error message and want to find out what's going on. MySQL reserves one extra connection for a client with the Process_priv privilege to ensure that you should always be able to login and check the system (assuming you are not giving this privilege to all your users).

Some frequently states in mysqladmin processlist

  • Checking table The thread doing an [automatic ?] checking of the table.
  • Closing tables Means that the thread is flushing the changed table data to disk and closing the used tables. This should be a fast operation. If not, then you should check that you don't have a full disk or that the disk is not in very heavy use.
  • Copying to tmp table on disk The temporary result set was larger than tmp_table_size and the thread is now changing the in memory based temporary table to a disk based one to save memory.
  • Creating tmp table The thread is creating a temporary table to hold a part of the result for the query.
  • deleting from main table When executing the first part of a multi-table delete and we are only deleting from the first table.
  • deleting from reference tables When executing the second part of a multi-table delete and we are deleting the matched rows from the other tables.
  • Flushing tables The thread is executing FLUSH TABLES and is waiting for all threads to close their tables.
  • Killed Someone has sent a kill to the thread and it should abort next time it checks the kill flag. The flag is checked in each major loop in MySQL, but in some cases it may still take a short time for the thread to die. If the thread is locked by some other thread, the kill will take affect as soon as the other thread releases it's lock.
  • Sending data The thread is processing rows for a SELECT statement and is also sending data to the client.
  • Sorting for group The thread is doing a sort to satsify a GROUP BY.
  • Sorting for order The thread is doing a sort to satsify a ORDER BY.
  • Opening tables This simply means that the thread is trying to open a table. This is should be very fast procedure, unless something prevents opening. For example an ALTER TABLE or a LOCK TABLE can prevent opening a table until the command is finished.
  • Removing duplicates The query was using SELECT DISTINCT in such a way that MySQL couldn't optimize that distinct away at an early stage. Because of this MySQL has to do an extra stage to remove all duplicated rows before sending the result to the client.
  • Reopen table The thread got a lock for the table, but noticed after getting the lock that the underlying table structure changed. It has freed the lock, closed the table and is now trying to reopen it.
  • Repair by sorting The repair code is using sorting to create indexes.
  • Repair with keycache The repair code is using creating keys one by one through the key cache. This is much slower than Repair by sorting.
  • Searching rows for update The thread is doing a first phase to find all matching rows before updating them. This has to be done if the UPDATE is changing the index that is used to find the involved rows.
  • Sleeping The thread is wating for the client to send a new command to it.
  • System lock The thread is waiting for getting to get a external system lock for the table. If you are not using multiple mysqld servers that are accessing the same tables, you can disable system locks with the --skip-locking option.
  • Upgrading lock The INSERT DELAYED handler is trying to get a lock for the table to insert rows.
  • Updating The thread is searching for rows to update and updating them.
  • User Lock The thread is waiting on a GET_LOCK().
  • Waiting for tables The thread got a notification that the underlying structure for a table has changed and it needs to reopen the table to get the new structure. To be able to reopen the table it must however wait until all other threads have closed the table in question. This notification happens if another thread has used FLUSH TABLES or one of the following commands on the table in question: FLUSH TABLES table_name, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE or OPTIMIZE TABLE.
  • waiting for handler insert The INSERT DELAYED handler has processed all inserts and are waiting to get new ones.

Most states are very quick operations. If threads last in any of these states for many seconds, there may be a problem around that needs to be investigated.

There are some other states that are not mentioned above, but most of these are only useful to find bugs in mysqld.

SHOW TABLE STATUS

SHOW TABLE STATUS [FROM db_name] [LIKE wild]

SHOW TABLE STATUS (バージョン 3.23 の新機能) は SHOWSTATUSのようですが、それぞれのテーブルについてより多くの情報を提供します。 mysqlshow --status db_name 命令文を実行しても同じものが得られます。以下の項目が返ってきます:

項目 意味
Name テーブル名
Type テーブルの種類 「7 MySQL テーブル型」節参照.
Row_format レコードの保存形式 (Fixed, Dynamic, or Compressed)
Rows レコード数
Avg_row_length レコードの平均長
Data_length データファイルの大きさ
Max_data_length データファイルの最大値
Index_length インデックスファイルの大きさ
Data_free 割り当てられたが使用されていないバイト数
Auto_increment 次の autoincrement 値
Create_time テーブル作成時刻
Update_time 一番最後に更新された時刻
Check_time 一番最後にチェックされた時刻
Create_options CREATE TABLE で使用された拡張オプション
Comment テーブル作成時につけられたコメント (あるいは、なぜこのテーブルにMySQL がアクセスできないかのいくつかの情報).

SHOW STATUS

サーバからのステータス情報を表示します。

ステータス変数

   
Aborted_clients クライアントが接続を閉じる前に死んでしまったために中断されたコネクション数。 
Aborted_connects クライアントが接続を閉じる前に死んでしまったために中断されたコネクション数。 
Created_tmp_tables MySQL サーバに接続を試みて失敗した数
Created_tmp_tables ステートメントを実行している間に創り出された一時的なテーブルの数. 
Delayed_insert_threads 使用中の delayed insert handler threads 数 
Delayed_writes INSERT DELAYED で書かれた行数
Delayed_errors INSERT DELAYED で書かれた行でなんらかのエラーのあった行数
Flush_commands FLUSH 命令文の実行回数
Handler_delete テーブルから行を削除するためのリクエスト数
Handler_read_first あるテーブルから特定行を最初に読み込むことを要求した数
Handler_read_key キーを元にした行の読み込みを要求した数
Handler_read_next キーの順番にそって、次の行を読み込むことを要求した数
Handler_read_rnd ある固定された位置に基づいた行を読み込んだリクエスト数
Handler_update テーブルの行を更新するための要求数
Handler_write テーブルに行を挿入するためのリクエスト数
Key_blocks_used キーキャッシュ中で使用されたブロック数
Key_read_requests キャッシュからキーブロックを読み込ませたリクエスト数
Key_reads ディスクから物理的の読み込んだキーブロックの数
Key_write_requests キャッシュにキーブロックを書かせたリクエスト数
Key_writes ディスクに物理的に書かれたキーブロック数
Max_used_connections 同時に使用された接続の最大数
Not_flushed_key_blocks 変更されたものの、まだディスクに書き出されていない、キーキャッシュ中のキーブロックの数
Not_flushed_delayed_rows INSERT DELAY キューに書きだされることを待っている行の数
Open_tables オープンされているテーブル数
Open_files オープンされているファイル数
Open_streams オープンされているストリーム数
Opened_tables オープンされたテーブル数
Questions サーバにたいする問い合わせ数
Running_threads 現在開かれているコネクション数
Slow_queries long_query_time以上にかかったクエリの数
Uptime サーバが走っている秒数

コメント

  • もし Opened_tables が大きければ、 table_cache 変数が小さすぎるのでしょう。
  • もし key_reads が大きければ、 key_cache が少なすぎるでしょう。キャッシュヒットレートは key_reads/key_read_requests で計算できます。
  • もし Handler_read_rnd が大きければ、 MySQL にテーブルをスキャンさせるような多くのクエリや、 キーを使用しない JOIN がもてる。

SHOW LOGS

SHOW LOGS shows you status information about existing log files. It currently only displays information about Berkeley DB log files.

  • File shows the full path to the log file
  • Type shows the type of the log file (BDB for Berkeley DB log files)
  • Status shows the status of the log file (FREE if the file can be removed, or IN USE if the file is needed by the transaction subsystem)

SHOW GRANTS

SHOW GRANTS FOR user はユーザの許可を複製するために発行する必要がある grant 命令文をリストします。

mysql> SHOW GRANTS FOR root@localhost;
+---------------------------------------------------------------------+
| Grants for root@localhost                                           |
+---------------------------------------------------------------------+
| GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION |
+---------------------------------------------------------------------+

SHOW CREATE TABLE

Shows a CREATE TABLE statement that will create the given table:

mysql> show create table t\G
*************************** 1. row ***************************
       Table: t
Create Table: CREATE TABLE t (
  id int(11) default NULL auto_increment,
  s char(60) default NULL,
  PRIMARY KEY (id)
) TYPE=MyISAM

SHOW CREATE TABLE will quote table and column names according to SQL_QUOTE_SHOW_CREATE option.


DESCRIBE構文 Oracle互換

構文

{DESCRIBE | DESC} テーブル名 {フィールド名| 文字列}

DESCRIBE はフィールドについての情報を与えます。

このエントリーをはてなブックマークに追加

DELETE構文:TRUNCATE構文でデータを削除する

2009年8月17日 コメントの追加

DELETEは、行を削除する際に使用します。構文自体はとシンプルですが、その操作には注意が必要です。一度削除したデータは元に戻すことができません。

DELETE構文

DELETEは、レコードを削除するための構文で、すべてのレコードを削除、もしくは条件式を満たす特定のレコードだけを削除することができます。使い方は、FROM句に対象となるテーブル名を指定し、特定のレコードのみを削除する場合はWHERE句に条件式を設定します。 DELETEはレコードを削除した後、削除したレコード数を返します。

WHERE句を省略してDELETEを発行した場合、全てのレコードが削除されるので、注意が必要です。

単一テーブル構文

DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM テーブル名
    [WHERE 条件式]
    [ORDER BY ...]
    [LIMIT rows]

複合テーブル構文

DELETE [LOW_PRIORITY] [QUICK] [IGNORE]
    テーブル名[.*] [テーブル名[.*] ...]
    FROM テーブル参照
    [WHERE 条件式]

DELETE構文のキーワード
パラメータ 説明
LOW_PRIORITY テーブルを利用するクライアントがなくなるまで待ってから、DELETEを実行します。
QUICK DELETE処理の間、インデックスのマージを行わないので、処理速度が向上します。
WHERE 条件式にマッチするレコードを選択します。  
ORDER BY 指定された順序でレコードを更新します。
LIMIT
-MYSQL拡張
削除するレコード数を指定します。

DELETEの基本的な使い方

DELETEで削除のテストができるように、削除用のテーブルを作成します。

CREATE TABLE test (
    id tinyint(3) unsigned NOT NULL auto_increment,
    name varchar(30),

    PRIMARY KEY (id_g)
);
INSERT test SET name='A';
INSERT test SET name='B';
INSERT test SET name='C';

削除用テーブル
ID(id) 名称(name)
1 A
2 B
3 C
レコードの削除

任意のレコードを削除するには、WHERE句に条件式を設定します。

次のクエリは、商品テーブルから1レコードを削除しています。WHERE句で削除するレコードの[商品ID(id_g)]を指定していることに注目してください。

DELETE FROM goods WHERE id_g = 2;
削除後のテーブル
ID(id) 名称(name)
1 A
3 C

WHEREを省略するとすべてのレコードが削除されます。

DELETE FROM goods;

ORDER BYを使ったレコードの削除

先頭から何件かを削除したい場合などに、ORDER BYとLIMITの組み合わせが役に立ちます。

次のクエリは、日付の古いものから10件のデータを削除します。

DELETE FROM goods
    ORDER BY add_time LIMIT 10;

TRUNCATE構文

TRUNCATE構文は、テーブルを削除し、再作成します。この操作は、レコードを1つずつ削除するよりはるかに迅速に処理されます。削除されたレコード数は返されません。

TRUNCATE構文

TRUNCATE TABLE テーブル名

テーブル定義ファイル table_name.frm が有効である限り、データやインデックスのファイルが破損しても、この方法でテーブルを再作成できます。
 

このエントリーをはてなブックマークに追加

UPDATE構文:データを更新する

2009年8月17日 コメントの追加

UPDATE構文

UPDATEは、レコードを更新するための構文で、すべてのレコードを一括更新、もしくは条件式を満たす特定のレコードだけを更新することができます。使い方は、INSERT構文と同様にSET句で更新するフィールドと値を指定します。

UPDATEで特定のレコードのみを更新対象にする場合は、WHERE句に条件式を設定します。 条件に当てはまるレコードが存在しない場合は更新されませんが、文法的に正しいのでエラーにはなりません。

単一テーブル構文
UPDATE [LOW_PRIORITY] [IGNORE] テーブル名
    SET フィールド名=値, [フィールド名=値, ...]
    [WHERE 条件式]
    [ORDER BY ...]
    [LIMIT 値]
複合テーブル構文
UPDATE [LOW_PRIORITY] [IGNORE] テーブル名の参照
    SET フィールド名=値, [フィールド名=値, ...]
    [WHERE 条件式]
UPDATE構文のキーワード
パラメータ 説明
LOW_PRIORITY テーブルを利用するクライアントがなくなるまで待ってから、UPDATEを実行します。
WHERE 条件式にマッチするレコードを選択します。  
LIMIT 更新するレコード数を指定します。
IGNORE 通常処理では、レコード更新の際にPRIMARYやUNIQUEと定義されたフィールドで値の重複がある場合、エラーが発生します。IGNOREを宣言しておくと、レコードの更新が行われないのは同じですが、エラーが発生しません。
ORDER BY 指定された順序でレコードを更新します。

 

UPDATE構文の使用例

それでは、UPDATE構文を使って商品テーブルの内容を変更してみましょう。変更する対象は[商品ID(id_g)]の値が"3"のレコード、[商品名(name)]"Saxophone Colussus"です。変更する内容は[商品名(name)]と[価格(price)]です。

商品ID(id_g) 商品名(name) 価格(price)
3 Saxophone Colossus NULL

特定の1件のレコードを指定する場合は、WHERE句の条件式にプライマリキーとなるフィールド名を指定します。プライマリキーの値は1つのテーブル内でユニークだということが保障されているので、間違えて関係のないレコードも変更してしまうといったミスを避けることができます。商品テーブルのプライマリキーは[商品ID(id_g)]なので、任意のレコードを選択するには、条件式で[商品ID(id_g)]を指定します。

レコードの更新

それでは、UPDATE構文を使ってレコードを更新してみましょう。まず、UPDATEの後に変更したいテーブル名を指定し、SET句で変更するフィールド名と値を指定します。最後に、WHERE句の条件式に変更するレコードのプライマリキーを指定します。

UPDATE goods
    SET name='Electric Ladyland', price=2100
    WHERE id_g=3;
変更内容の確認

UPDATEで更新した内容を、SELECTで確認します。

SELECT * FROM goods WHERE id_g=3;
+------+-------------------+-------+
| id_g | name | price |
+------+-------------------+-------+
| 3 | Electric Ladyland | 2100 |
+------+-------------------+-------+

 

商品ID(id_g) 商品名(name) 価格(price)
3 Electric Ladyland 2100

UPDATE構文で注意したいのが、WHERE句の部分です。WHERE句は省略可能ですが、WHERE句で変更対象のレコードを選択しなかった場合、すべてのレコードが変更されます。UPDATE構文を使う際は十分な注意が必要です。

式を使ったレコードの更新

フィールドを更新する際に、フィールドの値を直接指定する代わりに、式を指定することもできます。たとえば、商品テーブルの[価格(price)]フィールドを税込価格にするには、税込価格を計算してから更新処理をする必要はありません。クエリの中で[価格(price)]フィールドの値に5%をかけて、税込価格を代入します。

UPDATE goods SET price = price * 1.05;
更新前の商品テーブル
商品ID(id_g) 商品名(name) 価格(price)
1 Afro-American-Arctic 2300
2 Ready To Die 1800
3 Electric Ladyland 2100
~省略~
更新後の商品テーブル
商品ID(id_g) 商品名(name) 価格(price)
1 Afro-American-Arctic 2415
2 Ready To Die 1890
3 Electric Ladyland 2205
~省略~

UPDATEは左から右に評価します。たとえば、次のクエリは[商品番号(id_g)]"3"の[価格(price)] フィールドに"3200"を代入し、そのあと1.05倍しています。

UPDATE goods
SET price = 3200, price = price * 1.05
WHERE id_g = 3;
更新前の商品テーブル
商品ID(id_g) 商品名(name) 価格(price)
3 Electric Ladyland 2205
更新後の商品テーブル
商品ID(id_g) 商品名(name) 価格(price)
3 Electric Ladyland 3360

複数のテーブルに対するUPDATE

複数のテーブルに対するUPDATE操作も実行可能です。

UPDATE goods, goods_new
    SET items.price=goods_new.price
    WHERE goods.id_goods=goods_new.id_goods;

上の例では、カンマ演算子を使用した内部結合を示していますが、複数テーブルのUPDATE構文では、LEFT JOINなど、SELECT構文で使用可能な結合型を使用することができます。

  ※複数テーブルのUPDATE では、ORDER BYとLIMITがサポートされません。
このエントリーをはてなブックマークに追加

INSERT構文:REPLACE構文でデータを追加する

2009年8月17日 コメントの追加

INSERT構文

INSERT構文の紹介

INSERTを使って新しいレコードを挿入してみましょう。SELECTと同じく使用頻度が高く、しかもその構文はとてもシンプルです。初めてデータを操作するときは緊張するものですが、とても簡単ですからまずはやってみましょう。

INSERTは、テーブルに新しいレコードを挿入します。INSERTには、新しいレコードを挿入するために、VALUESとSETの2種類の構文が用意されています。VALUESの場合は値をテーブルを構成する全フィールドに対応するように順番に指定し、SETはフィールド名と値のペアで必要な分だけ指定していきます。

INSERT ... VALUES構文

INSERT ... VALUES構文でレコードを挿入するための基本的な構文は次の通りです。

INSERT INTO テーブル名 (フィールド名 , フィールド名 ,...) VALUES(値 , 値 ,...);

テーブル名の後に、値を設定するフィールド名を順番に記述します。全ての列にデータを入力する場合、ここのフィールド名は省略しても構いません。VALUESキーワードの後に、登録したい値を記述します。テーブル名の後で指定したフィール名の順番に対応するように記述する必要があります。

INSERT ... SET構文

INSERT ... SET構文でレコードを挿入するための基本的な構文は次の通りです。

 INSERT INTO  テーブル名 SET フィールド名='値', フィールド名='値', ...;

SETはフィールド名と値のペアを列挙します。

レコードの挿入

それでは、商品(goods)テーブルに1つのレコードを挿入してみましょう。

# INSERT ... VALUES構文
INSERT INTO goods (name,price) VALUES ('Maggot Brain', 2600);

# INSERT ... SET構文
INSERT goods SET name='We Won\'t Stop', price=1800;
フィールド名は省略可能です。その場合は、テーブル定義のフィールドの順番通りに値を宣言します。AUTO_INCREMENT宣言されているフィールドは、単純に空の値を指定すると自動的に値が割り当てられます。
レコードを追加したあとの商品テーブル
商品ID(id_g) 商品名(name) 価格(price)
1 Afro-American-Arctic 2300
2 Ready To Die 1800
3 Fresh 2300
4 Things Fall Apart 1600
5 Maggot Brain 2600
6 We Won't Stop 1800
全フィールドの値を指定して新しいレコードを追加

テーブルで定義されているすべてのフィールドの値を指定する場合は、フィールド名の指定を省略することができます。

# 通常のINSERT ... VALUES構文
INSERT INTO goods (id_g, name, price) VALUES (7, 'Welcome To The Cruel World', '2500');

# フィールド名を省略
INSERT INTO goods VALUES (7, 'Welcome To The Cruel World', '2500');
フィールドの値の指定に式を利用

INSERT構文で指定する「値」に、式を利用することができます。

INSERT INTOテーブル名 (フィールド名1, フィールド名2, ...)
VALUES(値, フィールド名1 * 2);
計算式の利用

次のクエリ文は、すでにあるレコードを利用して、税込価格のレコードを追加しています。

INSERT goods (name, price)
	SELECT name, price * 1.05
	FROM goods
INSERT構文 (mysql4.1)
INSERT [LOW_PRIORITY | DELAYED] [IGNORE]
    [INTO] テーブル名 [(フィールド名, ...)]
    VALUES ( (値1 | DEFAULT), ...), ...
    [ON DEPLICATE KEY UPDATE フィールド名=値, ...]

INSERT [LOW_PRIORITY | DELAYED] [IGNORE]
    [INTO] テーブル名 [(フィールド名, ...)]
    SET フィールド名1=(値1 | DEFAULT), ...
    [ON DEPLICATE KEY UPDATE フィールド名=値, ...]

INSERT [LOW_PRIORITY | DELAYED] [IGNORE]
    [INTO] テーブル名 [(フィールド名, ...)]
    SELECT フィールド名リスト
    FROM テーブルリスト
    WHERE 検索条件

INSERT構文のキーワード

パラメータ 説明
INTO 省略可能なキーワードです。
LOW_PRIORITY テーブルを利用するクライアントがなくなるまで待ってからINSERTを実行します。
(MYSQL:MyISAM テーブルでの使用は控えましょう。
IGNORE 通常では、レコード挿入の際にPRIMARYやUNIQUEと定義されたフィールドで値の重複がある場合、エラーが発生します。IGNOREを宣言しておくと、レコードの挿入が行われないのは同じですが、エラーが発生しません。
DELAYED
-MySQL拡張
INSERT DELAYEDの利点は、多くのクライアントからのINSERTを束ね、一つのブロックで処理されることです。これは別々のINSERTを実行するよりとても速くなります。
この命令文は、MySQLをロギングする際によく利用されます。

SELECTサブクエリを使ってレコードを追加

INSERT構文の中でSELECTサブクエリを使用することにより、他のテーブルやビューから選択したレコードを挿入することができます。

INSERT INTO テーブル名 ( フィールド名, フィールド名, ... )
    SELECTサブクエリ

まずは準備として、INSERT ... SELECT構文で使う過去の商品(goods_old)テーブルを作成します。 過去の商品テーブルはCREATE TABLE ... SELECT構文で商品テーブルをコピーします。その際、LIMITでコピーするレコード数を指定します。

# 過去の商品テーブルの作成
CREATE TABLE goods_old SELECT * FROM goods LIMIT 1;

# インデックスの属性などはコピーされないので、追加で定義します
ALTER TABLE goods_old MODIFY id_g TINYINT NOT NULL AUTO_INCREMENT, ADD INDEX id_g(id_g);

# CREATE TABLE ... SELECT構文で挿入したレコードは必要ないので削除します
DELETE FROM goods_old;

# 作成したテーブルを確認
DESC goods_old;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id_g  | tinyint(4)   |      | MUL | NULL    | auto_increment |
| name  | varchar(30)  | YES  |     | NULL    |                |
| price | decimal(9,0) | YES  |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+

これで準備が整いました。

商品テーブルに新しいレコードを追加

商品(goods)テーブルに、過去の商品(goods_old)テーブルの値(id_g, name, price)のみを挿入するには、次のようにします。

# 商品テーブルの全レコードを過去の商品テーブルにコピー
INSERT INTO goods_old (name, price) SELECT name, price FROM goods;

# 登録されたレコードを表示
SELECT * FROM goods_old;
+------+----------------------+-------+
| id_g | name                 | price |
+------+----------------------+-------+
|    1 | Afro-American-Arctic |  2300 |
|    2 | Ready To Die         |  1800 |
|    3 | Fresh                |  2300 |
|    4 | Things Fall Apart    |  1600 |
|    5 | Maggot Brain         |  2600 |
|    6 | We Won't Stop        |  1800 |
+------+----------------------+-------+

INSERTで指定した1番目のフィールドには、SELECTサブクエリで指定した1番目のフィールドの値、2番目以降も同様に挿入されます。

ここで紹介したINSERT ... SELECT構文は同じ構造のテーブルを利用しましたが、値の対応が取れていればテーブル構造が異なっていても問題ありません。

INSERT ... SELECT構文の条件は以下のとおりです。

  • クエリは、ORDER BY句を含むことができません。
  • INSERT構文のターゲットとなるテーブルは、SELECTサブクエリのFROM句に指定できません。
  • プライマリキーの重複違反を無視するには、IGNOREを指定します。

REPLACE構文

REPLACE構文は、INSERT構文と良く似ています。INSERT構文との違いは、UNIQUEインデックス、もしくはPRIMARY KEYなどのユニークなフィールドの値で重複が発生した場合、古いレコードを削除し、新しいレコードを挿入することです。INSERTの場合は古いレコードはそのままで、新しいレコードは挿入されません。

UNIQUEインデックスやPRIMARY KEYが設定されていないテーブルではREPLACEの効果がなく、INSERTとまったく同じ作用となります。

REPLACE構文

REPLACE [LOW_PRIORITY | DELAYED] [IGNORE]
    [INTO] テーブル名 [(フィールド名, ...)]
    VALUES ( (値1 | DEFAULT), ...), ...
    [ON DEPLICATE KEY UPDATE フィールド名=値, ...]

REPLACE [LOW_PRIORITY | DELAYED] [IGNORE]
    [INTO] テーブル名 [(フィールド名, ...)]
    SET フィールド名1=(値1 | DEFAULT), ...
    [ON DEPLICATE KEY UPDATE フィールド名=値, ...]

REPLACE [LOW_PRIORITY | DELAYED] [IGNORE]
    [INTO] テーブル名 [(フィールド名, ...)]
    SELECT フィールド名リスト
    FROM テーブルリスト
    WHERE 検索条件

次のような商品テーブルがあるとします。[商品ID] フィールドの値が 3 のレコードを削除して、同じ [商品ID] フィールドの値で新しいレコードを削除するケースを考えてみましょう。
商品テーブル
商品ID(id_g) 商品名(name) 価格(price)
1 Afro-American-Arctic 2300
2 Ready To Die 1800
3 Fresh 2300
4 Things Fall Apart 1600
5 Maggot Brain 2600
6 We Won't Stop 1800
古いレコードを削除して新しいレコードを挿入

商品テーブルの[商品ID(id_g)]はPRIMARY KEYでプライマリキーに設定されているので、その値はユニークであることが保障されています。ですから、INSERTで同じ商品IDを登録しようとすると、エラーが発生します。

INSERT goods SET id_g=3, name='Saxophone Colossus';
ERROR 1062 (23000): Duplicate entry '3' for key 1

INSERT構文ではなく、REPLACE構文を使えば、重複した古いレコードは削除され、新しいレコードが追加されます。

# 新しいレコードで古いレコードの上書き
REPLACE goods SET id_g=3, name='Saxophone Colossus';

# 商品テーブルの確認
SELECT * FROM goods;
+------+----------------------+-------+
| id_g | name                 | price |
+------+----------------------+-------+
|    1 | Afro-American-Arctic |  2300 |
|    2 | Ready To Die         |  1800 |
|    3 | Saxophone Colossus   |  NULL |
|    4 | Things Fall Apart    |  1600 |
|    5 | Maggot Brain         |  2600 |
|    6 | We Won't Stop        |  1800 |
+------+----------------------+-------+
REPLACEで上書き後の商品テーブル
商品ID(id_g) 商品名(name) 価格(price)
1 Afro-American-Arctic 2300
2 Ready To Die 1800
3 Saxophone Colossus NULL
4 Things Fall Apart 1600
5 Maggot Brain 2600
6 We Won't Stop 1800

[価格(price)] フィールドの値に注目してください。先ほどのREPLACE文では、 [価格(price)] フィールドに値を指定しませんでした。REPLACEの処理手順は古いレコードを削除してから新しいレコードを挿入するので、値の指定がなかったフィールドの値は、NULLかデフォルト指定の値が代入されます。前のレコードの値が使われるわけではないので、注意しましょう。

このエントリーをはてなブックマークに追加

SELECT構文:サブクエリ構文

2009年8月17日 コメントの追加

サブクエリ

サブクエリの基本的な使い方

サブクエリは、SELECT、INSERT、UPDATE、DELETEといった各ステートメントに含まれたSELECT句です。WHERE句やHAVING句の中、別のサブクエリの中など、様々な場所で利用できます。クエリに含まれるクエリなので、サブクエリと呼びます。

次に例を示します。

SELECT フィールド名 FROM テーブル名
WHERE フィールド名 演算子 ( SELECT フィールド名 FROM テーブル名2 ...);

WHERE句の「( SELECT 列名 FROM テーブル名2 ... )」という箇所がサブクエリで、サブクエリは、それ自身を括弧で囲む必要があります。ここでのサブクエリは、外側にあるSELECT句にネストされています。

等号や不等号といった演算子を使用するサブクエリは、返す値は1フィールドまでです。それ以上のフィールドを返す場合や、何も返す値がない場合は、ステートメントがエラーとなります。

下記のクエリは、購入履歴を対象に、[商品ID(id_g)]が一番小さい値のレコードを選択します。

SELECT id_p
FROM purchase
WHERE id_g = (SELECT MIN(id_g) FROM purchase);
-> +------+
-> | id_p |
-> +------+
-> | 2 |
-> | 3 |
-> +------+

WHERE句の中のサブクエリは、MIN関数を使って購入履歴テーブルの中で最小の[商品ID(id_g)]の値を算出しています。その結果をWHERE句で受け取り、最小の[商品ID(id_g)]をもったレコードの[購入履歴ID(id_p)]を出力しています。このように、サブクエリを利用することで、SELECT文の結果を利用したSELECT文を1つのクエリで表現することがが可能です。

サブクエリを使用する主な利点は次のとおりです。

  • 本来なら複数のクエリを1つのクエリで表現することができる。
  • 複雑な結合や集合処理を行わずにクエリを記述できる。
  • 一般に可読性が高い。

SELECT句のサブクエリ

サブクエリはWHERE句のみだけではなく、SELECT句でもサブクエリを利用することができます。SELECT句でサブクエリを利用する場合、サブクエリの結果をメインクエリの結果として直接表示させることができます。

たとえば、下記の例では、顧客ごとの平均受注件数と全体の平均受注件数を出力しています。単一のSELECT文ではグループ化された結果と全体の結果を同時に取得できませんが、サブクエリは異なる値を同時に取得することが可能です。

SELECT id_c, AVG(quantity), ( SELECT AVG(quantity) FROM purchase)
FROM purchase
GROUP BY id_c;
-> +------+---------------+---------------------------------------+
-> | id_c | AVG(quantity) | ( SELECT AVG(quantity) FROM purchase) |
-> +------+---------------+---------------------------------------+
-> | 1 | 3.0000 | 1.5000 |
-> | 2 | 1.0000 | 1.5000 |
-> | 3 | 1.0000 | 1.5000 |
-> | 4 | 1.5000 | 1.5000 |
-> +------+---------------+---------------------------------------+


FROM句のサブクエリ

FROM句のサブクエリは、そのサブクエリの結果をテーブルとして渡します。FROM句でのサブクエリの利用は応用性が高く、サブクエリの中でもっとも頻繁に利用される構文です。

次の例では、購入履歴テーブルを購入者でグループ化し、その平均値を算出しています。

SELECT AVG(detail_cnt) AS detail_avg
FROM ( SELECT COUNT(*) AS detail_cnt FROM purchase GROUP BY id_c ) AS t;
-> +------------+
-> | detail_avg |
-> +------------+
-> | 1.5000 |
-> +------------+

FROM句のサブクエリは、購入者テーブルを[購入者ID(id_c)]でグループ化し、SELETC句のAVG関数の引数に渡しています。サブクエリをFROM句に指定することで、その結果をテーブルとして扱うことが可能になります。


HAVING句でのサブクエリ

HAVING句にもサブクエリを使用することが可能です。使用方法はWHERE句でのサブクエリと同じです。

次の例は、[受注個数(quantity)]の平均が、顧客ごとの平均受注個数と同じか、それ以上の場合に選択するというクエリです。

SELECT id_c, AVG(quantity) AS quantity_avg FROM purchase
GROUP BY id_c
HAVING quantity_avg >= (SELECT AVG(quantity) FROM purchase);
-> +------+--------------+
-> | id_c | quantity_avg |
-> +------+--------------+
-> | 1 | 3.0000 |
-> | 4 | 1.5000 |
-> +------+--------------+

HAVINGでのサブクエリは、他のサブクエリと同様に、分割することができます。

先ほどクエリからサブクエリ部分だけを省いた、SELECT句部分だけのクエリを実行してみましょう。

SELECT id_c, AVG(quantity) AS quantity_avg FROM purchase
GROUP BY id_c;
-> +------+--------------+
-> | id_c | quantity_avg |
-> +------+--------------+
-> | 1 | 3.0000 |
-> | 2 | 1.0000 |
-> | 3 | 1.0000 |
-> | 4 | 1.5000 |
-> +------+--------------+

クエリの実行後、出力結果は顧客ごとの平均受注数です。ここでは平均受注数が1.0から3.0までの値があることがわかります。

次に、サブクエリ部分だけを実行してみましょう。サブクエリ部分は、顧客全体の平均受注数を算出しています。

SELECT AVG(quantity) FROM purchase;
-> 1.5000

以上で、顧客全体の平均受注数と、顧客ごとの平均受注数を算出できました。最初に紹介したサブクエリは、2つのクエリを1つにして、「[受注個数(quantity)]の平均が、顧客ごとの平均受注個数と同じか、それ以上の場合に選択」というクエリを実現しています。


 

このエントリーをはてなブックマークに追加

SELECT構文:JOINを使ってテーブルを結合する

2009年8月17日 4 COMMENTS

JOINは複数のテーブルを連結させるために用意されたキーワードです。単純にテーブルを連結させる以外にも、LEFT JOIN ONやUSINGなどの便利なキーワードがあるので、いろいろ試してみましょう。

リレーションによる関係付け

テーブルの作成とデータの挿入

ここでは、購入履歴テーブル(テーブル名=purchase)、商品テーブル(テーブル名=goods)、顧客テーブル(テーブル名=customer)を作成し、それを使って説明していきます。CREATE TABLEとINSERTを使って、テーブルの作成とデータの挿入をしておきましょう。

テーブルを作成する際の手順は、CREATE TABLEに続き、作成するテーブル名を指定します。次に、カッコ内にフィールドの定義を列挙していきます。フィールドの定義はフィールド名とデータ型、それに制約などを必要なだけ宣言します。

CREATE TABLE構文

CREATE TABLE テーブル名 (
    # 作成定義と制約
    フィールド名 データ型 オプション,
    フィールド名 データ型 オプション,
    ...
);

INSERTは、テーブルに新しいレコードを挿入します。新しいレコードを挿入するには、VALUESとSETの2種類があります。VALUESの場合は値をテーブルを構成する全フィールドに対応するように順番に指定し、SETはフィールド名と値のペアで必要な分だけ指定していきます。

INSERT構文

INSERT INTO テーブル名 VALUES ( 値, 値, ...), ...

INSERT INTO テーブル名 SET フィールド名=値, フィールド名=値, ...

以上の構文を使って、2つのテーブルを定義し、データを挿入します。

# 購入履歴テーブルの作成
CREATE TABLE purchase (
    id_p int unsigned NOT NULL auto_increment,
    id_c int unsigned NOT NULL,
    id_g int unsigned NOT NULL,
    quantity tinyint unsigned,

    PRIMARY KEY (id_p)
);

# データの挿入
INSERT purchase SET id_c=3, id_g=2, quantity=1;
INSERT purchase SET id_c=1, id_g=1, quantity=3;
INSERT purchase SET id_c=4, id_g=1, quantity=2;
INSERT purchase SET id_c=2, id_g=4, quantity=1;
INSERT purchase SET id_c=4, id_g=2, quantity=1;
INSERT purchase SET id_c=2, id_g=1, quantity=1;

# 商品テーブルの作成
CREATE TABLE goods (
    id_g tinyint(3) unsigned NOT NULL auto_increment,
    name varchar(30),
    price decimal(9,0),

    PRIMARY KEY (id_g)
);

# データの挿入
INSERT goods SET name='LEGO',price='3000';
INSERT goods SET name='LUMINOX WATCH',price='28000';
INSERT goods SET name='PS2',price='25000';
INSERT goods SET name='PDA',price='50000';

上記のクエリで作成されるテーブルは次のようになります。

購入履歴(purchase)テーブル
購入ID (id_p) 顧客ID (id_c) 商品ID (id_g) 数量 (quantity)
1 3 2 1
2 1 1 3
3 4 1 2
4 2 4 1
顧客(customer)テーブル
顧客ID (id_c) 姓 (sei) 年齢 (age) 性別 (sex) メールアドレス (email)
1 エラ・フィッツジェラルド 48 2 ella@exsample.jp
2 トミー・ゲレロ 34 1 tommy@exsample.jp
3 マディ・ウォーターズ 55 1 muddy@exsample.jp
4 エディ・ロバーツ 28 1  
5 マーサ・リーブス 38 2 martha@exsample.jp
6 スライ・ストーン 34 1  
商品(goods)テーブル
商品ID (id_g) 商品名 (name) 価格 (price)
1 Afro-American-Arctic 2300
2 Ready To Die 1800
3 Fresh 2300
4 Things Fall Apart 1600

購入履歴テーブルは顧客テーブルのプライマリキー(プライマリ・キー)の[顧客ID(id_c)]フィールドと、商品テーブルのプライマリキーの[商品ID(id_g)]フィールドと同じ名前のフィールドを用意していて、レコードの追加時にそれぞれのID情報を記録することができるようになっています。


リレーションの作成

リレーショナル・データベースで最も特徴的なのがリレーションです。複数のテーブルに定義したリレーションにより、複数のテーブルを1つのテーブルに結合してデータを扱うことができます。

購入履歴(purchase)テーブル、顧客(customer)テーブル、商品(goods)テーブルの中で、中心となるのが購入履歴テーブルです。購入履歴テーブルは顧客テーブルのプライマリキーと、商品テーブルのプライマリキーと同じフィールドを持っていて、顧客テーブルと商品テーブルのプライマリキーを格納することができます。このように、他のテーブルのプライマリキーを参照するフィールドのことを外部キーと呼びます。購入履歴テーブルの場合は[顧客ID(id_c)]と[商品ID(id_g)]が外部キーにあたります。

3つのテーブル間のリレーションは次の図のようになっています。

各テーブル間のリレーション

図

顧客履歴テーブルは購入情報を記録するためのテーブルで、購入処理が正しく処理された際に、データ登録が行われます。その際、購入に関連するデータ、数量や日付などとともに、購入したユーザの[顧客ID(id_c)]と、購入された商品の[商品ID(id_g)]が記録されます。

顧客テーブルからは姓名や年齢、商品テーブルからは商品名や価格などを取り出すことが可能です。

結合後のテーブル

図

JOIN構文

複数のテーブルから情報を取得する必要がある際、テーブルを連結することでクエリの発行から情報の取得までを一度で済ませることができます。複数のテーブルを連結させるには、FROMで複数のテーブルを指定することで実現することができます。複数のテーブルを指定するには、テーブル名とテーブル名の間にカンマ( , )、もしくはJOINを挟みます。

SELECT * FROM テーブル名, テーブル名

JOINを使って同様のことが行えます。

SELECT * FROM テーブル名 JOIN テーブル名

次のクエリは、購入履歴(purchase)テーブルと顧客(customer)テーブルを結合し、[購入ID(id_p)] フィールド、[名前(fullname)] フィールドを表示します。

SELECT id_p, fullname
FROM purchase JOIN customer ON purchase.id_c=customer.id_c;
+------+--------------------------------------+
| id_p | fullname |
+------+--------------------------------------+
| 1 | マディ・ウォーターズ |
| 2 | エラ・フィッツジェラルド |
| 3 | エディ・ロバーツ |
| 4 | トミー・ゲレロ |
| 5 | エディ・ロバーツ |
| 6 | トミー・ゲレロ |
+------+--------------------------------------+

購入履歴(purchase)テーブルと顧客(customer)テーブルを結合するために、JOINを使っています。ON以降の「purchase.id_c = customer.id_c」という箇所は、「顧客履歴テーブルと顧客テーブルの[顧客ID(id_c)]フィールドの値が一致したレコードを選択する」という意味です。複数のテーブルにリレーションを張るには、FROMの部分に複数のテーブルをカンマ( , )かJOINで区切って並べ、ONでそれらのテーブルを連結する条件を指定します。

内部結合

JOIN、カンマ( , )は2つのテーブルを比較し、結合条件に一致した行だけを返します。これを内部結合と呼び、他の結合方法と区別しやすいように、INNER JOINと記述することも可能です。JOIN、カンマ( , )、INNER JOINはどれも意味は同じです。

購入履歴(purchase)テーブルと商品(goods)テーブルを[商品ID(id_g)]で内部結合してみましょう。

SELECT purchase.id_g,name
FROM goods INNER JOIN purchase ON purchase.id_g=goods.id_g;
+------+----------------------+
| id_g | name |
+------+----------------------+
| 2 | Ready To Die |
| 1 | Afro-American-Arctic |
| 1 | Afro-American-Arctic |
| 4 | Things Fall Apart |
| 2 | Ready To Die |
| 1 | Afro-American-Arctic |
+------+----------------------+

内部結合は2つのテーブルを比較し、結合条件に一致したレコードだけを返す結合方法です。どちらかのテーブルに無いレコードは表示されないので、購入履歴テーブルに記録の無い商品、ここでは[商品ID(id_g)]が3のレコードは表示されません。

外部結合

内部結合が両方のテーブルを比較し、結合条件に一致したレコードだけを返す結合方法だったのに対し、外部結合は、結合条件に一致した行に加え、指定したテーブルに関しては結合条件に一致しなくてもレコードを返す結合方法です。

外部結合には、LEFT JOINキーワードとRIGHT JOINキーワードの2種があります。JOINを中心にして、JOINの前に記述したテーブルを左、JOINの後に記述したテーブルを右として、LEFT JOINは左側のテーブルが結合条件に一致しなくてもレコードを返し、RIGHTJOINは右側のテーブルが結合条件に一致しなくてもレコードを返します。

それでは、購入履歴(purchase)テーブルと商品(goods)テーブルを[商品ID(id_g)]で外部結合してみましょう。ここれではLEFT OUTER JOINを使います。OUTERは省略可能です。

SELECT purchase.id_g,name
FROM goods LEFT OUTER JOIN purchase ON purchase.id_g=goods.id_g;
+------+----------------------+
| id_g | name |
+------+----------------------+
| 1 | Afro-American-Arctic |
| 1 | Afro-American-Arctic |
| 1 | Afro-American-Arctic |
| 2 | Ready To Die |
| 2 | Ready To Die |
| NULL | Electric Ladyland |
| 4 | Things Fall Apart |
| NULL | Maggot Brain |
| NULL | We Won't Stop |
+------+----------------------+

LEFT OUTER JOINキーワードを使っているので、左に指定した商品(goods)テーブルは条件に一致しなくてもレコードが返されます。

JOIN構文

テーブル名, テーブル名
テーブル名 JOIN テーブル名
テーブル名 INNER JOIN テーブル名 JOIN条件式
テーブル名 STRAIGHT_JOIN テーブル名
テーブル名 LEFT [OUTER] JOIN テーブル名 JOIN条件式
テーブル名 LEFT [OUTER] JOIN テーブル名
テーブル名 NATURAL [LEFT [OUTER]] JOIN テーブル名

{ oj テーブル名 LEFT OUTER JOIN テーブル名 ON 条件式 }
テーブル名 RIGHT [OUTER] JOIN テーブル名 JOIN条件式
テーブル名 RIGHT [OUTER] JOIN テーブル名
テーブル名 NATURAL [RIGHT [OUTER]] JOIN テーブル名

[JOIN]

JOINとカンマ( , )は、同義語です。どちらもテーブル間の直積です。

[テーブル名]

[テーブル名] は次のように定義と同じです。

テーブル名 [[AS] エイリアス名] [USE INDEX (キー・リスト)] [IGNORE INDEX (キー・リスト)]

[テーブル名] は、テーブル名 AS エイリアス名や "テーブル名 エイリアス名" によるエイリアス名を指定することができます。

SELECT t1.sei, t2.quantity
FROM customer AS t1, goods AS t2
WHERE t1.id_c = t2.id_c;

[JOIN条件式]

[JOIN条件式] は次のような定義と同じです。

ON 条件式 | USING (フィールド・リスト)

[LEFT OUTER JOIN]

LEFT OUTER JOIN は、ODBCとの互換性のために用意されています。

[OUTER]

外部結合を明示するためのキーワードで、省略可能です。


データベースオブジェクトの参照

JOINを使って購入履歴テーブルと顧客テーブルを結合する際に注意したいのは、購入履歴テーブルと顧客テーブルの両方で使われるフィールド名をSELECT句に指定する際は、どちらかのテーブル名で修飾する必要があるということです。テーブル名の記述がないと、DBMSはどちらのテーブルのフィールドを表示すればよいのか判断できず、エラーとなります。

下記クエリでは、購入履歴テーブルと顧客テーブルで共通して使われる[顧客ID(id_c)]をテーブル名で修飾せずに記述しているので、DBMSからエラーメッセージを返されています(DBMSによってエラーメッセージは異なります)。

SELECT id_c, id_p, fullname FROM customer JOIN purchase;
ERROR 1052 (23000): Column 'id_c' in field list is ambiguous

フィールド名をテーブル名で修飾するには、ドット( . )でテーブル名とフィールド名をつなげます。

SELECT テーブル名1.フィールド名, テーブル名2.フィールド名
FROM テーブル名1, テーブル名2

[顧客ID(id_c)]を「purchase.id_c」と購入履歴のテーブル名で修飾した結果が下記のとおりです。

SELECT purchase.id_c, id_p, fullname FROM customer JOIN purchase;
+------+------+--------------------------------------+
| id_c | id_p | fullname |
+------+------+--------------------------------------+
| 3 | 1 | エラ・フィッツジェラルド |
| 3 | 1 | トミー・ゲレロ |
| 3 | 1 | マディ・ウォーターズ |
| 3 | 1 | エディ・ロバーツ |
| 3 | 1 | マーサ・リーブス |
| 3 | 1 | スライ・ストーン |
| 1 | 2 | エラ・フィッツジェラルド |
| 1 | 2 | トミー・ゲレロ |
| 1 | 2 | マディ・ウォーターズ |
| 1 | 2 | エディ・ロバーツ |
....(省略)

JOIN構文のキーワード

これから紹介するオプションで重要なのは、LEFT JOIN ON、USING、NATURAL LEFT JOIN までです。STRIGHT JOIN を使う機会はほとんどありませんし、RIGHT JOIN、INNER JOIN は余分です。

LEFT JOIN ON

リレーションを設定したテーブル結合の場合、通常はリレーションが成立しなかったレコードは表示されません。LEFT JOINを宣言すると、右側のテーブルにマッチするレコードが無かった場合でも、レコードが表示されます。その際の右側のテーブルのフィールド値はすべてNULLです。

レコード選択の条件式はWHEREの代わりにONを使います。ONとWHEREの書式は同じです。

テーブル名 LEFT [OUTER] JOIN テーブル名 ON 条件式

次のクエリは、購入履歴(purchase)テーブルと顧客(customer)テーブルを[顧客ID(id_c)]フィールドで結合しています。

SELECT id_p, fullname
FROM customer JOIN purchase
ON purchase.id_c = customer.id_c;
+------+--------------------------------------+
| id_p | fullname |
+------+--------------------------------------+
| 1 | マディ・ウォーターズ |
| 2 | エラ・フィッツジェラルド |
| 3 | エディ・ロバーツ |
| 4 | トミー・ゲレロ |
| 5 | エディ・ロバーツ |
| 6 | トミー・ゲレロ |
+------+--------------------------------------+

実行結果では、購入履歴テーブルに記録のない顧客は表示されていません。

次のクエリは、LEFT JOINに変更した場合で、購入履歴テーブルに記録されていない顧客も表示されています。

SELECT id_p, fullname
FROM customer LEFT JOIN purchase
ON
purchase.id_c = customer.id_c;
+------+--------------------------------------+
| id_p | fullname |
+------+--------------------------------------+
| 2 | エラ・フィッツジェラルド |
| 4 | トミー・ゲレロ |
| 6 | トミー・ゲレロ |
| 1 | マディ・ウォーターズ |
| 3 | エディ・ロバーツ |
| 5 | エディ・ロバーツ |
| NULL | マーサ・リーブス |
| NULL | スライ・ストーン |
+------+--------------------------------------+

2つ以上のテーブル結合でもLEFT JOINは使えます。購入履歴(purchase)テーブルを中心にして、顧客(customer)テーブルと商品(goods)テーブルからデータを取得します。その際、顧客(customer)テーブルにあるレコードは全て表示するようにLEFT JOINで指定します。

SELECT id_p, fullname, name FROM customer
LEFT JOIN purchase ON purchase.id_c = customer.id_c
LEFT JOIN goods ON purchase.id_g = goods.id_g;
+------+--------------------------------------+----------------------+
| id_p | fullname | name |
+------+--------------------------------------+----------------------+
| 2 | エラ・フィッツジェラルド | Afro-American-Arctic |
| 4 | トミー・ゲレロ | Things Fall Apart |
| 6 | トミー・ゲレロ | Afro-American-Arctic |
| 1 | マディ・ウォーターズ | Ready To Die |
| 3 | エディ・ロバーツ | Afro-American-Arctic |
| 5 | エディ・ロバーツ | Ready To Die |
| NULL | マーサ・リーブス | NULL |
| NULL | スライ・ストーン | NULL |
+------+--------------------------------------+----------------------+

USING

USING句は、ON句の略記法で、結合する条件が同じ名前のフィールド名であれば、USINGで結合条件を指定することができます。USINGの引数に指定したフィールド名で、テーブル間のリレーションを作成します。

次の構文は意味的には同じです。

# USING を使った構文
SELECT * FROM A LEFT JOIN B USING (F1)

# ON 条件式を使った構文
SELECT * FROM A LEFT JOIN B ON A.F1=B.F1

フィールド名をカンマ( , )で区切って条件式を複数指定することも可能です。

# USING を使った構文
SELECT * FROM A LEFT JOIN B USING (F1, F2, F3)

# ON 条件式を使った構文
SELECT * FROM A LEFT JOIN B ON A.F1=B.F1 AND A.F2=B.F2 AND A.F3=B.F3,...

次のクエリは、購入履歴(purchase)テーブルと顧客(customer)テーブルを[顧客ID(id_c)]フィールドで結合し、なおかつ顧客(customer)テーブルにあるレコードをすべて表示します。

SELECT id_p, fullname FROM customer LEFT JOIN purchase
USING (id_c);
+------+--------------------------------------+
| id_p | fullname |
+------+--------------------------------------+
| 2 | エラ・フィッツジェラルド |
| 4 | トミー・ゲレロ |
| 6 | トミー・ゲレロ |
| 1 | マディ・ウォーターズ |
| 3 | エディ・ロバーツ |
| 5 | エディ・ロバーツ |
| NULL | マーサ・リーブス |
| NULL | スライ・ストーン |
+------+--------------------------------------+

NATURAL LEFT JOIN

同じフィールド名を持つ2つのテーブルの NATURAL [LEFT] JOINは、 USINGを伴ったINNER JOINやLEFT JOINと同じです。

テーブル名 NATURAL [LEFT [OUTER]] JOIN テーブル名
テーブル名 NATURAL [RIGHT [OUTER]] JOIN テーブル名

次のクエリは、購入履歴(purchase)テーブルと顧客(customer)テーブルを[顧客ID(id_c)]フィールドで結合し、なおかつ顧客(customer)テーブルにあるレコードをすべて表示します。

SELECT id_p, fullname FROM customer
NATURAL LEFT JOIN purchase;
+------+--------------------------------------+
| id_p | fullname |
+------+--------------------------------------+
| 2 | エラ・フィッツジェラルド |
| 4 | トミー・ゲレロ |
| 6 | トミー・ゲレロ |
| 1 | マディ・ウォーターズ |
| 3 | エディ・ロバーツ |
| 5 | エディ・ロバーツ |
| NULL | マーサ・リーブス |
| NULL | スライ・ストーン |
+------+--------------------------------------+

STRAIGHT JOIN

STRAIGHT JOIN は、常に左側のテーブルを先に読むことを除けば、JOIN と同じです。これは、不当な順序でテーブルを出力するようなまれな事態に有効ですが、ほとんどの場合は必要ありません。

テーブル名 STRAIGHT JOIN テーブル名


INNER JOIN

INNER JOINとカンマ( , )は、同義語で、通常のJOINの別名です。

テーブル名 INNER JOIN テーブル名 ON 条件式

次の 2 つのSQLは、同じ意味です。

# WHERE を使った結合
SELECT id_p, fullname
FROM customer, purchase
WHERE purchase.id_c = customer.id_c;

# INNER JOIN を使った結合
SELECT id_p, fullname
FROM customer INNER JOIN purchase
ON
purchase.id_c = customer.id_c;
+------+--------------------------------------+
| id_p | fullname |
+------+--------------------------------------+
| 1 | マディ・ウォーターズ |
| 2 | エラ・フィッツジェラルド |
| 3 | エディ・ロバーツ |
| 4 | トミー・ゲレロ |
| 5 | エディ・ロバーツ |
| 6 | トミー・ゲレロ |
+------+--------------------------------------+

OUTER JOIN

デフォルトのJOINは、リレーションを作成した際、リレーションが作成できなかったレコードに対しては表示しません。たとえば、次のようにリレーションを作成した場合、購入履歴(purchase)テーブルに情報がない[顧客ID(id_c)]の5番、6番は表示されません。

SELECT id_p, customer.id_c, fullname
FROM customer JOIN purchase
ON purchase.id_c = customer.id_c;
+------+------+--------------------------------------+
| id_p | id_c | fullname |
+------+------+--------------------------------------+
| 1 | 3 | マディ・ウォーターズ |
| 2 | 1 | エラ・フィッツジェラルド |
| 3 | 4 | エディ・ロバーツ |
| 4 | 2 | トミー・ゲレロ |
| 5 | 4 | エディ・ロバーツ |
| 6 | 2 | トミー・ゲレロ |
+------+------+--------------------------------------+

これは、[顧客ID(id_c)]の5番、6番は購入したことがなく、購入履歴(purchase)テーブルに情報がないためです。購入履歴(purchase)テーブルに登録されていない顧客情報なので、リレーションを作成した際に選択レコードからはずされています。これを、INNER JOIN(内部結合)と呼びます。

しかし、場合によっては、注文が無い顧客が無いということを明示した上で、リストには表示してほしいことがあります。これを実現するのが、OUTER JOIN(外部結合)です。

SELECT id_p, customer.id_c, fullname
FROM customer LEFT OUTER JOIN purchase
ON purchase.id_c = customer.id_c;
+------+------+--------------------------------------+
| id_p | id_c | fullname |
+------+------+--------------------------------------+
| 2 | 1 | エラ・フィッツジェラルド |
| 4 | 2 | トミー・ゲレロ |
| 6 | 2 | トミー・ゲレロ |
| 1 | 3 | マディ・ウォーターズ |
| 3 | 4 | エディ・ロバーツ |
| 5 | 4 | エディ・ロバーツ |
| NULL | 5 | マーサ・リーブス |
| NULL | 6 | スライ・ストーン |
+------+------+--------------------------------------+

今度は[顧客ID(id_c)]の5番、6番が表示されましたが、購入履歴がないため、[購入履歴ID(id_p)]はNULLと表示されています。


RIGHT JOIN

RIGHT JOINはLEFT JOINが左のテーブルを必ず出力させるのと逆に、右側のテーブルを必ず出力しようとします。どちらを使ってもよいのですが、LEFT JOIN、RIGHT JOINの両方を使うと混乱を招くことになるので、LEFT JOINのみを使うことをオススメします。

# LEFT JOINを使った構文
SELECT id_p, fullname
FROM customer LEFT JOIN purchase
ON
purchase.id_c = customer.id_c;

# RIGHT JOIN を使った構文
SELECT id_p, fullname
FROM purchase RIGHT JOIN customer
ON purchase.id_c = customer.id_c;

JOINの使い方

2つ以上のテーブルの結合

リレーションは、2つ以上のテーブルに対しても処理できます。手順はほとんど同じで、FROMに結合するテーブル名を並べ、ONで連結する条件を指定します。

購入履歴(puarchase)テーブル、顧客(customer)テーブル、商品(goods)テーブルを結合し、[購入ID(id_p)]フィールド、[名前(fullname)]フィールド、[商品名(name)]フィールドを表示するクエリを紹介します。

SELECT id_p, fullname, name
FROM purchase JOIN customer JOIN goods
ON purchase.id_c = customer.id_c
And purchase.id_g = goods.id_g
;
+------+--------------------------------------+----------------------+
| id_p | fullname | name |
+------+--------------------------------------+----------------------+
| 1 | マディ・ウォーターズ | Ready To Die |
| 2 | エラ・フィッツジェラルド | Afro-American-Arctic |
| 3 | エディ・ロバーツ | Afro-American-Arctic |
| 4 | トミー・ゲレロ | Things Fall Apart |
| 5 | エディ・ロバーツ | Ready To Die |
| 6 | トミー・ゲレロ | Afro-American-Arctic |
+------+--------------------------------------+----------------------+

購入履歴(purchase)テーブルを中心にして、[顧客ID(id_c)]フィールドと[商品ID(id_g)]フィールドを使ってリレーションを作成しています。上記の場合、AND演算子で条件式をつなげているので、2つのリレーションが成立するレコードだけが選択されます。


複雑なリレーションの作成

テーブル名のエイリアス

テーブルの結合を行う際は、テーブルのエイリアス名を付けることがよくあります。方法は、フィールド名のエイリアスと同様で、テーブル名の後ろにスペース( )で区切ってエイリアス名を指定するか、テーブル名とエイリアス名の間にASを挟みます。

SELECT id_p, fullname, name
FROM purchase AS p, customer, goods
ON purchase.id_c = customer.id_c
And purchase.id_g = goods.id_g;

自己結合:自己自身とリレーションを作成

1つのテーブルに登録されたレコードの中から、同じ値をもつレコードのペアを見つけたい場合があります。たとえば、顧客テーブルに登録された顧客データの中から、同じ名前や同じ住所の顧客がいれば、そのペア情報だけを抜き取りとりたいといった要望があった際などです。1つのテーブルで条件式を作成しようとすると、テーブル名で修飾しても「顧客テーブル.年齢=顧客テーブル.年齢」となり、名前の衝突が発生します。

自己自身とリレーションを作成したい際は、テーブル名の別名を利用して、1つのテーブルを2つのテーブルのように扱うことことで解決できます。このように、同じテーブルをエイリアスを使用して結合することを自己結合と呼びます。

次の例は、顧客(customer)テーブルから、同じ[年齢(age)]の値を持つ顧客のペアを選択します。FROM句のテーブル名が同じになるので、別名を付けてテーブル名を明確に区別します。

SELECT C1.fullname, C1.age, C2.fullname, C2.age
FROM customer AS C1 JOIN customer AS C2
ON C1.age=C2.age
AND C1.id_c < C2.id_c;
+-----------------------+-----+--------------------------+-----+
| fullname | age | fullname | age |
+-----------------------+-----+--------------------------+-----+
| トミー・ゲレロ | 34 | スライ・ストーン | 34 |
+-----------------------+-----+--------------------------+-----+

FROMで2つのテーブルを指定していますが、実際には顧客(customer)テーブルに、C1とC2という二つのエイリアス名を指定しています。

ON以降の箇所は少し特殊なことをしています。下記は再帰結合のために重複するレコードを排除するための条件式です。

AND C1.id_c < C2.id_c

※再帰結合とは、同一のテーブルを別名を使って結合することです。

このエントリーをはてなブックマークに追加

SELECT構文:WHEREで検索条件を設定する

2009年8月17日 コメントの追加

SELECT構文の中でも特に重要なWHERE句を紹介します。WHEREを使えるようになると、膨大なレコードの中から特定のレコードを抽出することが可能になります。

WHEREを使って検索する

SELECT構文を使って特定のレコードを検索するには、WHERE句を使います。WHEREの後に条件式を指定することにより、その条件にマッチしたレコードだけを選択することができます。

SELECT フィールド名
FROM テーブル名
WHERE 条件式

特定の条件を満たすレコードを取り出す

条件式は、検索対象となるフィールド名と、演算子、関数、定数などを使って表現します。たとえば、顧客(customer)テーブルの中から男性だけを取り出すには次のような条件式を設定します。

# sex フィールドの値が1のレコードを取り出す
SELECT * FROM customer WHERE sex=1;
+------+--------------------------------+-----+-----+
| id_c | fullname | age | sex |
+------+--------------------------------+-----+-----+
| 2 | トミー・ゲレロ | 34 | 1 |
| 3 | マディ・ウォーターズ | 55 | 1 |
| 4 | エディ・ロバーツ | 28 | 1 |
| 6 | スライ・ストーン | 34 | 1 |
+------+--------------------------------+-----+-----+

複数の条件式を指定する際は、論理演算子のANDやORで接続します。顧客(customer)テーブルの中から男性だけを取り出し、年齢が34歳の顧客だけを選択するには次のような条件式を設定します。

SELECT * FROM customer WHERE sex=1 AND age=34;
+------+--------------------------+-----+-----+-------------------+
| id_c | fullname | age | sex | email |
+------+--------------------------+-----+-----+-------------------+
| 2 | トミー・ゲレロ | 34 | 1 | tommy@exsample.jp |
| 6 | スライ・ストーン | 34 | 1 | NULL |
+------+--------------------------+-----+-----+-------------------+


WHERE句で使える演算子

演算子のほとんどはWHERE句で使用することができます。次の表は、WHERE句で使用することができる演算子の一覧です。

演算子 説明
= 等しい
> 大きい
< 小さい
>= 大きい、もしくは等しい
<= 小さい、もしくは等しい
!=
<>
等しくない
AND 2つの条件を結合し、両方の条件が真
OR 2つの条件のうち、どちらか一方が真
NOT 式の結果を反転
BETWEEN a AND b 対象のフィールドが a と b の範囲内
IN 対象のフィールドが式の一覧の1つに一致
LIKE 対象のフィールドがパターンに一致

比較演算子

比較演算子は、値の大小を比較し、その結果を返します。次の例はWHERE句で簡単でよく利用される条件式で、フィールドの値を指定して、それにマッチしたレコードだけを選択します。

SELECT * FROM customer WHERE id_c = 1;
+------+--------------------------------------+-----+-----+
| id_c | fullname | age | sex |
+------+--------------------------------------+-----+-----+
| 1 | エラ・フィッツジェラルド | 48 | 2 |
+------+--------------------------------------+-----+-----+

次の例では、[顧客ID(id_c)]フィールドの値が 1 以外のレコードだけを選択します。

SELECT * FROM customer WHERE id_c != 1;
+------+--------------------------------+-----+-----+
| id_c | fullname | age | sex |
+------+--------------------------------+-----+-----+
| 2 | トミー・ゲレロ | 34 | 1 |
| 3 | マディ・ウォーターズ | 55 | 1 |
| 4 | エディ・ロバーツ | 28 | 1 |
| 5 | マーサ・リーブス | 38 | 2 |
| 6 | スライ・ストーン | 34 | 1 |
+------+--------------------------------+-----+-----+

> や < のような値の大小を比べる演算子は、論理演算子のANDを併用することで選択範囲を指定することができます。次の例では、[顧客ID(id_c)]フィールドの値が 2~4 までのレコードを選択しています。

# id_c フィールドの値が 2~4 のレコードを選択
SELECT * FROM customer WHERE id_c >= 2 AND id_c <= 4;
+------+--------------------------------+-----+-----+
| id_c | fullname | age | sex |
+------+--------------------------------+-----+-----+
| 2 | トミー・ゲレロ | 34 | 1 |
| 3 | マディ・ウォーターズ | 55 | 1 |
| 4 | エディ・ロバーツ | 28 | 1 |
+------+--------------------------------+-----+-----+


論理演算子

WHEREの中で複数の条件を指定したい場合は、論理演算子を使います。論理演算子を使うことにより、「~で~のレコードを選択」、「~もしくは~のレコードを選択」といったAND/OR検索が可能になります。

複数の条件を指定する

ANDやORを組み合わせることにより、複数の条件を指定することができます。

# id_c フィールドの値が 1 以上で 4 以下のレコードを選択
SELECT * FROM customer WHERE id_c > 1 AND id_c < 4;
+------+--------------------------------+-----+-----+
| id_c | fullname | age | sex |
+------+--------------------------------+-----+-----+
| 2 | トミー・ゲレロ | 34 | 1 |
| 3 | マディ・ウォーターズ | 55 | 1 |
+------+--------------------------------+-----+-----+

# id_c フィールドの値が 2、もしくは 4 のレコードを選択
SELECT * FROM customer WHERE id_c = 2 OR id_c = 4;
+------+--------------------------+-----+-----+
| id_c | fullname | age | sex |
+------+--------------------------+-----+-----+
| 2 | トミー・ゲレロ | 34 | 1 |
| 4 | エディ・ロバーツ | 28 | 1 |
+------+--------------------------+-----+-----+

複数のANDやORを組み合わせる場合、優先順位を明確にするために式を括弧で囲むことがあります。括弧内の式は括弧外の式よりも優先順位が高いため、先に実行されます。実行の順番によっては結果が変わってくるので、複数の式を組み合わせる際はなるべく括弧を使って記述しましょう。

# id_c フィールドの値が 2、もしくは 4 、もしくは[性別(sex)]フィールドの値が
# 1 のレコードを選択
SELECT * FROM customer
WHERE (id_c >= 2 AND id_c <=4) OR sex = 1
;

指定した条件に一致しないレコードを指定したい場合は、NOT演算子で条件を否定します。

# id_c フィールドの値が 2 ではないレコードを選択
SELECT * FROM customer WHERE NOT (id_c = 2);
+------+--------------------------------------+-----+-----+
| id_c | fullname | age | sex |
+------+--------------------------------------+-----+-----+
| 1 | エラ・フィッツジェラルド | 48 | 2 |
| 3 | マディ・ウォーターズ | 55 | 1 |
| 4 | エディ・ロバーツ | 28 | 1 |
| 5 | マーサ・リーブス | 38 | 2 |
| 6 | スライ・ストーン | 34 | 1 |
+------+--------------------------------------+-----+-----+

式に対してNOTを使う場合、式を括弧で囲む必要があります。括弧を省略すると、上記の場合はフィールド名だけがNOTの対象になり、思ったような結果になりません。


LIKE演算子

LIKEはワイルドカードを使って文字列を検索するための演算子です。ワイルドカードとは、何かと一致する特殊な文字で、「任意の文字列」を表すパーセント( % )と、「1文字の任意の文字」を表すアンダスコア( _ )の2種類があります。

LIKEで使用できるワイルドカードには次の2つがあります。

アンダスコア記号(_)
任意の1文字にマッチする。
パーセント記号(%)
0個以上のの連続した文字にマッチする。

次の例では、WHERE句に[フルネーム(fullname)]フィールドの値が"マディ"で始まるという条件を設定しています。

SELECT * FROM customer WHERE fullname LIKE 'マディ%';

"マディ"から始まる値を探しているので、 [姓名(fullname)]フィールドの値の先頭に"マディ"の文字があれば、次にどのような文字列がきてもTRUEが帰ってきます。このような処理方法を『パターンマッチング』と呼びます。

% や _ などのワイルドカード自体を検索対象にしたい場合は、\ を使ってエスケープ処理を行います。たとえば、任意の文字列の後に % があるような値を検索したい場合は、"%\%"とします。最初の % がワイルドカードで、2番目の % は % そのものです。


BETWEEN演算子

BETWEEN演算子は、対象の値が指定した2つの値の範囲以内にあるかを判定します。

対象値 BETWEEN 最低値 AND 最大値

対象値には式やフィールド名を指定します。

BETWEEN~AND

次の例では、顧客テーブル(customer)から、[年齢(age)]フィールドの値が40から55までのレコードを選択しています。

SELECT fullname, age FROM customer
WHERE age BETWEEN 40 AND 55;
+--------------------------------------+-----+
| fullname | age |
+--------------------------------------+-----+
| エラ・フィッツジェラルド | 48 |
| マディ・ウォーターズ | 55 |
+--------------------------------------+-----+

上記のとおり、BETWEEN演算子は~以上、~以下の範囲をとるので、指定した境界値も含みます。簡単に境界値を含まない~未満、~超を実現するには、超の値に+1、未満の値に-1を加えるとよいでしょう。

SELECT fullname, age FROM customer
WHERE age BETWEEN 40+1 AND 55-1;
+--------------------------------------+-----+
| fullname | age |
+--------------------------------------+-----+
| エラ・フィッツジェラルド | 48 |
+--------------------------------------+-----+

IN演算子

IN演算子は対象の値が指定した値のリストの中にあるかを判定します。

対象値 IN(値, 値, ...)

対象値には式やフィールド名を指定します。

次の例では、顧客テーブル(customer)から、[年齢(age)]フィールドの値が28、38、48の行を選択しています。

SELECT fullname, age FROM customer
WHERE age IN(28, 38, 48);
+--------------------------------------+-----+
| fullname | age |
+--------------------------------------+-----+
| エラ・フィッツジェラルド | 48 |
| エディ・ロバーツ | 28 |
| マーサ・リーブス | 38 |
+--------------------------------------+-----+

否定のNOT演算子を使って、指定した以外の値を持ったレコードを選択することもできます。

SELECT fullname, age FROM customer
WHERE age NOT IN(28, 38, 48);
+--------------------------------+-----+
| fullname | age |
+--------------------------------+-----+
| トミー・ゲレロ | 34 |
| マディ・ウォーターズ | 55 |
| スライ・ストーン | 34 |
+--------------------------------+-----+


ISNULL(expr) / expr IS NULL

NULLは特殊な値で、通常の値のように比較演算子や論理演算子を使ってNULLを検索することができません。たとえば、顧客テーブル(customer)の[メールアドレス(email)]フィールドの値がNULLのレコードを検索する際、下記のようなSQL文では正しい結果を得ることができません。

SELECT * FROM customer WHERE email=NULL;

NULLを検索する際は、ISNULL演算子、もしくはIS NULL演算子を使います。ISNULL演算子、IS NULL演算子は引数exprがNULLなら 1 を、そうでなければ 0 を返します。

SELECT * FROM customer
WHERE email IS NULL;
+------+--------------------------+-----+-----+-------+
| id_c | fullname | age | sex | email |
+------+--------------------------+-----+-----+-------+
| 4 | エディ・ロバーツ | 28 | 1 | NULL |
| 6 | スライ・ストーン | 34 | 1 | NULL |
+------+--------------------------+-----+-----+-------+

SELECT * FROM customer
WHERE ISNULL(email);
+------+--------------------------+-----+-----+-------+
| id_c | fullname | age | sex | email |
+------+--------------------------+-----+-----+-------+
| 4 | エディ・ロバーツ | 28 | 1 | NULL |
| 6 | スライ・ストーン | 34 | 1 | NULL |
+------+--------------------------+-----+-----+-------+

ISNULLの否定形にはNOTを使います。

SELECT * FROM customer
WHERE email IS NOT NULL;
+------+--------------------------------------+-----+-----+--------------------+
| id_c | fullname | age | sex | email |
+------+--------------------------------------+-----+-----+--------------------+
| 1 | エラ・フィッツジェラルド | 48 | 2 | ella@exsample.jp |
| 2 | トミー・ゲレロ | 34 | 1 | tommy@exsample.jp |
| 3 | マディ・ウォーターズ | 55 | 1 | muddy@exsample.jp |
| 5 | マーサ・リーブス | 38 | 2 | martha@exsample.jp |
+------+--------------------------------------+-----+-----+--------------------+

SELECT * FROM customer
WHERE NOT ISNULL(email);
+------+--------------------------------------+-----+-----+--------------------+
| id_c | fullname | age | sex | email |
+------+--------------------------------------+-----+-----+--------------------+
| 1 | エラ・フィッツジェラルド | 48 | 2 | ella@exsample.jp |
| 2 | トミー・ゲレロ | 34 | 1 | tommy@exsample.jp |
| 3 | マディ・ウォーターズ | 55 | 1 | muddy@exsample.jp |
| 5 | マーサ・リーブス | 38 | 2 | martha@exsample.jp |
+------+--------------------------------------+-----+-----+--------------------+

 

このエントリーをはてなブックマークに追加

SELECT構文:レコードを選択する

2009年8月14日 コメントの追加

SELECT構文

SELECTはテーブルからレコードを取得するための命令文で、SQL命令文の中で最も使用頻度が高いといえます。SELECTには数多くのキーワードが用意されていますが、その中でもGROUP BYLIMITORDER BYWHEREは特に重要です。

SELECT構文

SELECT [STRAIGHT_JOIN] [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
       [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS] [HIGH_PRIORITY]
       [DISTINCT| DISTINCTROW | ALL]
    フィールド名 / 式 [ AS 別名 ], ...
    [INTO {OUTFILE | DUMPFILE} 'ファイル名' オプション]
    [FROM テーブル名
        [WHERE 条件式]
        [GROUP BY {整数 | フィールド名| 式} [ASC | DESC], ... [WITH ROLLUP]]
        [HAVING 条件式]
        [ORDER BY {整数 | フィールド名| 式} [ASC | DESC] ,...]
        [LIMIT [オフセット,] 行指定]
        [PROCEDURE 手続き名(属性一覧)]
        [FOR UPDATE | LOCK IN SHARE MODE]
    ]

SELECT構文の基本構成は、SELECT句の次に表示させたいフィールド名のリストを並べます。ここで指定したフィールドの値が結果として返されます。フィールド名の変わりにメタ文字のアスタリスク( * )を使用すると、すべてのフィールドの値が返されます。

続いてFROM句では、参照するテーブル名を指定します。FROM句以降はオプションとして、条件式を設定するWHERE句や、レコードをグループ化するGROUP BY句、レコードの表示順を設定するORDER BY句を追加設定していきます。


SELECTの使い方

SELECTを使う前の準備

ここでは、顧客テーブル(テーブル名=customer)を作成し、そのデータを使って説明していきます。まずは準備としてCREATE TABLEとINSERTでテーブルの作成とデータの挿入をしておきましょう。

顧客(customer)テーブルの作成とデータの挿入
# 顧客テーブルの作成
CREATE TABLE customer (
    id_c int unsigned NOT NULL auto_increment,
    fullname varchar(40) NOT NULL,
    age tinyint unsigned NOT NULL ,
    sex tinyint unsigned NOT NULL,
    email varchar(50),

    PRIMARY KEY (id_c)
);
# データの挿入
INSERT customer SET fullname='エラ・フィッツジェラルド',age='48',sex='2',email='ella@exsample.jp';
INSERT customer SET fullname='トミー・ゲレロ',age='34',sex='1',email='tommy@exsample.jp';
INSERT customer SET fullname='マディ・ウォーターズ',age='55',sex='1',email='muddy@exsample.jp';
INSERT customer SET fullname='エディ・ロバーツ',age='28',sex='1';
INSERT customer SET fullname='マーサ・リーブス',age='38',sex='2',email='martha@exsample.jp';
INSERT customer SET fullname='スライ・ストーン',age='34',sex='1';

上記命令文で作成されるテーブルは次のようになります。

顧客(customer)テーブル
顧客ID
(id_c)
フルネーム
(fullname)
性別
(sex)
年齢
(age)
email
1 エラ・フィッツジェラルド 2 48 ella@exsample.jp
2 トミー・ゲレロ 1 34 tommy@exsample.jp
3 マディ・ウォーターズ 1 55 muddy@exsample.jp
4 エディ・ロバーツ 1 28  
5 マーサ・リーブス 2 38 martha@exsample.jp
6 スライ・ストーン 1 34  

顧客テーブルの性別には"男性"と"女性"という属性がありますが、ここでは、それらの値を数値に対応させてフィールドに格納しています。値が1の場合は男性、値が2の場合は女性を意味します。

性別のように、いくつかの決まった文字列が複数のレコードで繰り返し使われる場合、一般的に、その文字列を数値と対応させて、フィールドには数値の方を格納するといった方法がよく使われます。理由としては、文字列で管理するよりも、数値で管理したほうがデータ容量が少なくて済み、検索処理で発生する負荷を軽減させることができるからです。

SELECT命令文で全レコードを選択

SELECT命令文を使ってテーブルに登録された全レコードを選択する場合は、SELECTの後に続くアスタリスク( * )を指定し、FROMで表示対象のテーブル名を指定します。

SELECT * FROM テーブル名;

アスタリスク( * )は全フィールドを意味し、蒸気の命令文で使った場合、選択されたレコードの全フィールドを表示します。表示フィールドの数が多いほど、処理の負荷が高くなりパフォーマンスが低下しますので、注意してください。

顧客(customer)テーブルから全レコードを選択するには、下記のように簡単な1行命令文になります。

SELECT * FROM customer;
+------+--------------------------------------+-----+-----+--------------------+
| id_c | fullname | age | sex | email |
+------+--------------------------------------+-----+-----+--------------------+
| 1 | エラ・フィッツジェラルド | 48 | 2 | ella@exsample.jp |
| 2 | トミー・ゲレロ | 34 | 1 | tommy@exsample.jp |
| 3 | マディ・ウォーターズ | 55 | 1 | muddy@exsample.jp |
| 4 | エディ・ロバーツ | 28 | 1 | NULL |
| 5 | マーサ・リーブス | 38 | 2 | martha@exsample.jp |
| 6 | スライ・ストーン | 34 | 1 | NULL |
+------+--------------------------------------+-----+-----+--------------------+
6 rows in set (0.00 sec)

顧客(customer)テーブルに登録されいている全レコードの全部の値が表示されました。

表示するフィールド名を指定

クエリを発行する際は、無駄な処理でリソースを消費しないように、必要なレコード、必要なフィールドを指定します。たとえば顧客テーブルから[姓名(fullname)]のみが必要な場合は、アスタリスク( * )ですべてのフィールドを指定せずに、[姓名(fullname)]のみを指定します。

表示するフィールドを指定するには、SELECTの後にフィールド名を指定します。

SELECT フィールド名 FROM テーブル名;

下記では、顧客(customer)テーブルから[姓名(fullname)]フィールドの値のみを表示させるために、SELECTにフィールド名の"fullname"を指定しています。

SELECT fullname FROM customer;
+--------------------------------------+
| fullname |
+--------------------------------------+
| エラ・フィッツジェラルド |
| トミー・ゲレロ |
| マディ・ウォーターズ |
| エディ・ロバーツ |
| マーサ・リーブス |
| スライ・ストーン |
+--------------------------------------+
6 rows in set (0.00 sec)

複数フィールドの指定

複数のフィールドの値を取得したい場合は、複数のフィールド名をカンマ( , )で区切って指定します。

SELECT フィールド名1, フィールド名2
FROM テーブル名;

顧客(customer)テーブルから、名前と年齢を取り出すときは、次のようにします。

SELECT fullname, age FROM customer;
+--------------------------------------+-----+
| fullname | age |
+--------------------------------------+-----+
| エラ・フィッツジェラルド | 48 |
| トミー・ゲレロ | 34 |
| マディ・ウォーターズ | 55 |
| エディ・ロバーツ | 28 |
| マーサ・リーブス | 38 |
| スライ・ストーン | 34 |
+--------------------------------------+-----+
6 rows in set (0.00 sec)

SELECT構文のキーワード

ALL / DISTINCT
選択されたすべての行を返す / 選択された行から重複行の削除

ALLはデフォルトのオプションで、選択されたすべての行を返します。DISTINCTは、指定したフィールドの値を調べて、重複した値があれば1行にまとめて返します。

SELECT DISTINCT フィールド名
FROM テーブル名;

ALLはデフォルトのオプションなので、ALLかDISTINCTの指定が無ければ、ALLを選択したことになります。一般的にALLは記述しません。

DISTINCTは選択された全レコードを対象に、指定したフィールドの値を調べて、重複した値があればそれらのレコードを1行にまとめて返します。以下はALLも同様ですが、DISTINCTは1つのSELECT文に1回だけ使うことができます。SELECTの後にDISTINCTを指定し、その後にフィールド名を列挙、もしくはアスタリスク( * )を指定します。

DISTINCTの使い方
# ALL は省略可能
SELECT sex FROM customer;
+-----+
| sex |
+-----+
| 2 |
| 1 |
| 1 |
| 1 |
| 2 |
| 1 |
+-----+

# DISTINCT を指定
SELECT DISTINCT sex FROM customer;
+------+
| sex |
+------+
| 1 |
| 2 |
+------+

DISTINCTの後にアスタリスク( * )を指定した場合、すべての値が同じ場合のみ適用されます。

AS
フィールド名に別名を設定

DBMSの種類によりますが、SELECTを実行すると最初の行に見出しが表示されます。この見出しにあるフィールド名は変更可能で、「フィールド名 AS 別名」 という構文を使用します。ASは省略可能です。

SELECT フィールド名 AS 別名
FROM テーブル名;

ASキーワードを使って、[姓名(fullname)]フィールドに"name"という別名を設定してみましょう。

SELECT fullname AS name FROM customer;
+--------------------------------------+
| name |
+--------------------------------------+
| エラ・フィッツジェラルド |
| トミー・ゲレロ |
| マディ・ウォーターズ |
| エディ・ロバーツ |
| マーサ・リーブス |
| スライ・ストーン |
+--------------------------------------+
6 rows in set (0.00 sec)

見出しが別名に指定した"name"へ変更されています。

HAVINGで別名を使う

ASを使って作成した別名は、ORDER BY句やHAVING句で利用することができます。たとえば下記は、AVG関数を使って性別毎の平均年齢を計算しています。

SELECT sex, AVG(age) AS avgage
FROM customer GROUP BY sex;

+-----+----------+
| sex | avgage |
+-----+----------+
| 1 | 37.7500 |
| 2 | 43.0000 |
+-----+----------+
2 rows in set (0.00 sec)

平均年齢40歳以上のグループのみを表示させるには、上記SELECT文にHAVINGを追加します。その際、ASでつけた別名avgageを利用できます。

SELECT sex, AVG(age) AS avgage
FROM customer GROUP BY sex
HAVING avgage>38;
+-----+---------+
| sex | avgage |
+-----+---------+
| 2 | 43.0000 |
+-----+---------+
1 row in set (0.00 sec)

テーブル名も、"テーブル名 AS 別名"を使って表示する名称を変更することができます。テーブル名に別名をつけても一般的なDBMSでは表示上は変化がありませんが、後ほど紹介する自己結合などのときに役立ちます。

SELECT *
FROM テーブル名 AS 別名

ASC / DESC
昇順に並び替え / 降順に並び替え

ASCDESCは、ORDER BYで指定したフィールドの並び順を変更することができます。昇順の場合はASC(デフォルト)、降順の場合はDESCです。

SELECT *
FROM テーブル名
ORDER BY フィールド名 DESC;

ORDER BYはデフォルトで昇順に並び替えるので、ASCは指定する必要はありません。

ORDER BYでDESCを使う

顧客(customer)テーブルに格納された[年齢(age)]フィールドの値で降順に並び替える場合は、ORDER BY句に[年齢(age)]フィールドを指定し、DESCキーワードを追加します。

# 年齢順に並び替える
SELECT fullname, age FROM customer
ORDER BY age DESC;

+--------------------------------------+-----+
| fullname | age |
+--------------------------------------+-----+
| マディ・ウォーターズ | 55 |
| エラ・フィッツジェラルド | 48 |
| マーサ・リーブス | 38 |
| トミー・ゲレロ | 34 |
| スライ・ストーン | 34 |
| エディ・ロバーツ | 28 |
+--------------------------------------+-----+

[性別(sex)]フィールドを昇順で並び替えたデータを、さらに[年齢(age)]フィールドで降順に並び替えることも可能です。

SELECT fullname, sex, age
FROM customer
ORDER BY sex, age DESC;

+--------------------------------------+-----+-----+
| fullname | sex | age |
+--------------------------------------+-----+-----+
| マディ・ウォーターズ | 1 | 55 |
| トミー・ゲレロ | 1 | 34 |
| スライ・ストーン | 1 | 34 |
| エディ・ロバーツ | 1 | 28 |
| エラ・フィッツジェラルド | 2 | 48 |
| マーサ・リーブス | 2 | 38 |
+--------------------------------------+-----+-----+
6 rows in set (0.00 sec)

レコードの並び替え時のNULLの扱い
レコードの並び替え時のNULL値の扱いはデータベースによって異なるので、注意が必要です。SQL Server、MySQLでは、レコードの並び替え時にNULL値を一番小さな値として扱いますが、Oracle、PostgreSQLは一番大きな値として扱います。レコードの並び替えは利用頻度が高いので、移植する際に問題になることがよくあります。NULLが許されているフィールドをソート処理の対象にする際は、十分に注意しましょう。

GROUP BY
フィールドの値や式の値でグループ化

GROUP BYは特定の列のフィールドの値や式の値でグループ化します。グループ化したレコードは、集計関数などを使って各グループの合計値や平均値などを算出することが可能です。

SELECT COUNT(*) FROM テーブル名
GROUP BY フィールド名;

複数のフィールドをグループ化したい場合は、GROUP BYの後に複数のフィールド名をカンマ( , )で区切って指定します。

SELECT COUNT(*) FROM テーブル名
GROUP BY フィールド名, フィールド名, ... ;

GROUP BYの使い方

GROUP BYを使ってcustomerテーブルのsexフィールドの値でグループ化し、COUNT関数で男性と女性毎の合計人数を出力します。

# 男女別に購入点数の合計を計算
SELECT sex, COUNT(*) FROM customer
GROUP BY sex;
+-----+----------+
| sex | count(*) |
+-----+----------+
| 1 | 4 |
| 2 | 2 |
+-----+----------+

上記では、SELECT句に列の合計値を求めるCOUNT関数を指定し、GROUP BYで集計のキーとなるフィールド名、[性別(sex)]を指定しています。これによって、GROUP BYでグループ化した[性別(sex)]の合計人数を求めることができます。

COUNT関数やAVG関数といった、GROUP BYでよく使う集計関数を紹介します。

集計関数の一覧
関数名 解説
AVG 平均値を求める
COUNT レコード数を求める
MIN 最小値を求める
MAX 最大値を求める
SUM 総和を求める

FOR UPDATE

選択された行をロックします。ページ/レコードロックとともにテーブルハンドラ上でFOR UPDATEを使用すると、検査されるレコードは書き込みロックされます。

SELECT * FROM customer FOR UPDATE;

FROM
参照するテーブルを設定

FROM句には、選択対象のテーブルを指定します。複数のテーブルを結合したい場合はテーブル名をカンマ( , )で区切って記述します。JOINを使えば、結合条件をFROM句で指定することも可能です。

SELECT * FROM テーブル名;

SELECT * FROM テーブル名, テーブル名, ...;

FROMの使い方

顧客(customer)テーブルの[姓名(fullname)]フィールドの値を全て表示するには、次のような命令文になります。

SELECT fullname FROM customer;
+--------------------------------------+
| fullname |
+--------------------------------------+
| エラ・フィッツジェラルド |
| トミー・ゲレロ |
| マディ・ウォーターズ |
| エディ・ロバーツ |
| マーサ・リーブス |
| スライ・ストーン |
+--------------------------------------+
6 rows in set (0.00 sec)

顧客(customer)テーブルからすべての[姓名(fullname)]フィールドが出力されました。

HAVING
選択したレコードに対して条件を設定する

HAVING句はWHERE句と同じように、条件にマッチしたレコードを選択します。HAVING句がWHERE句と異なる点は、HAVING句の場合は集計後に条件式を適用するということです。WHERE句は集計前に条件式を適用します。

HAVING句は集計関数とセットでよく使われます。たとえば、顧客(customer)テーブルの[性別(sex)]フィールドでグループ化し、その後、[性別(sex)]ごとに[年齢(age)]の平均を算出する場合、以下のようなクエリになります。

SELECT sex, AVG(age) AS avgage
FROM customer
GROUP BY sex;
+-----+---------+
| sex | avgage |
+-----+---------+
| 1 | 37.7500 |
| 2 | 43.0000 |
+-----+---------+

この後、平均年齢40歳以上の性別だけを選択したい場合は、以下のようにHAVINGで条件を設定します。

SELECT sex, AVG(age) AS avgage
FROM customer
GROUP BY sex
HAVING avgage>40;
+------+--------+
| name | total |
+------+--------+
| PDA | 350000 |
+------+--------+

最頻値の算出

最頻値とは、最も度数( その値を取るデータ数 )の多い値のことです。

たとえば、あるサイトの1週間のページビューを集計した結果、スポットで広告を掲載した月曜日のページビューが50万程度、他の曜日は1万ページビューで、広告があった場合とない場合では50倍以上の差があったとします。1日の平均を計算すると、1週間のトータルが56万ページビューとなり、それを7で割った1日の平均が8万ページビューです。

曜日 ページビュー
日曜日 10,000
月曜日 500,000
火曜日 10,000
水曜日 10,000
木曜日 10,000
金曜日 10,000
土曜日 10,000

計算上では1日の平均は8万ページビューとなりますが、広告がなければ、1日の平均は1万ページビューです。このことから分かるように、例外の値が大きい場合は、平均値も大きく影響されます。こういうケースでは、集団の傾向をもっと正確に示す指標、たとえば最頻値を使うとよいでしょう。最頻値は単純に最も多かった値になるので、ページビューの最頻値は1万となります。

DBMSによっては、最頻値を求める独自の関数を用意している場合もありますが、HAVINGを使って、最頻値を求めることもできます。customerテーブルの年齢の最頻値を求める場合、以下のようなSQL文となります。

SELECT age FROM customer
GROUP BY age
HAVING COUNT(*) >= ALL(SELECT COUNT(*) FROM customer GROUP BY age);
+-----+
| age |
+-----+
| 34 |
+-----+

INTO OUTFILE / DUMPFILE
選択された行をファイルに書き込む

SELECT ... INTO OUTFILE "ファイル名" 構文は、選択された行をファイルに書き込みます。既に存在するファイルには書き込めません。SELECT ... INTO OUTFILEで書き出したファイルは、LOAD DATA INFILEで読み込むことができます。
オプションの構文は、LOAD DATA INFILE 構文で使われるFIELDSやLINESと同じです。詳しくは【LOAD DATA INFILE 構文】を参照してください。

一番簡単な INTO OUTFILE の利用法です。

SELECT * INTO OUTFILE "c:/customer.txt" FROM customer;

"customer.txt"ファイルの中身は次のようになります。

1 エラ・フィッツジェラルド 48 2
2 トミー・ゲレロ 34 1
3 マディ・ウォーターズ 55 1
4 エディ・ロバーツ 28 1
5 マーサ・リーブス 38 2
6 スライ・ストーン 34 1

INTO OUTFILEを使うとき、エスケープ・キャラクタ、ASCIIの0 、全てのターミネータ・キャラクタはエスケープされます。また、オプションの ESCAPED BYで指定した文字、FIELDS TERMINATED BY、LINES TERMINATED BYの最初の文字をエスケープします。

次は、CSV形式のファイルを作成するためのオプションです。

SELECT * INTO OUTFILE "/home/user/dump.txt"
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY "\n"
FROM table;

SELECT ... INTO OUTFILEはローカルで使用されることを前提に最適化されているので、別のホスト上では使用できません。その場合は、mysqldump --tab や mysql -e "SELECT ..." > outfile を代わりに使ってください。

INTO OUTFILEの代わりにINTO DUMPFILEを使用すると、 ファイルに1レコードだけを書きます。フィールドや行の終端とすべてのエスケープを含みません。これはファイル内にBLOBを格納したい場合に便利です。

※INTO OUTFILEとINTO DUMPFILEによって生成されたファイルは、すべてのユーザに読み出し可能です。

LIKE
ワイルドカードを使った文字列検索

LIKEは、WHEREの条件式内で、ワイルドカードを使った文字列検索に使用できます。

LIKEの詳しい情報は【WHEREでレコードを検索する】を参照してください。

LIMIT
選択された行数の件数指定

LIMITを使って、SELECTが返す行数を指定することができます。LIMITは、2つまでの引数を受け付けます。

SELECT * FROM テーブル名
LIMIT 件数;


SELECT * FROM テーブル名
LIMIT 最初の行からのオフセット, 最大数;

引数が1つの場合は、行の最大数を指定したことになります。

# 3行まで返す
SELECT * FROM customer LIMIT 3;
+------+--------------------------------------+-----+-----+-------------------+
| id_c | fullname | age | sex | email |
+------+--------------------------------------+-----+-----+-------------------+
| 1 | エラ・フィッツジェラルド | 48 | 2 | ella@exsample.jp |
| 2 | トミー・ゲレロ | 34 | 1 | tommy@exsample.jp |
| 3 | マディ・ウォーターズ | 55 | 1 | muddy@exsample.jp |
+------+--------------------------------------+-----+-----+-------------------+
3 rows in set (0.00 sec)

引数が2つの場合は、最初の引数は最初の行からのオフセット、2つめの引数は行の最大数です。初めの行のオフセットは 0 です。

# 3~4行目を返す
SELECT * FROM customer LIMIT 2, 2;
+------+--------------------------------+-----+-----+-------------------+
| id_c | fullname | age | sex | email |
+------+--------------------------------+-----+-----+-------------------+
| 3 | マディ・ウォーターズ | 55 | 1 | muddy@exsample.jp |
| 4 | エディ・ロバーツ | 28 | 1 | NULL |
+------+--------------------------------+-----+-----+-------------------+
2 rows in set (0.00 sec)

ORDER BY
フィールドの値でレコードの順番を並び替え

ORDER BYは、指定したフィールドの値で行をソートします。ORDER BYの後に並び替えしたい列のフィールド名、もしくは式を指定します。

SELECT * FROM テーブル名
ORDER BY フィールド名

顧客(customer)テーブルを[年齢(age)]フィールドで昇順、降順に並び替えてみましょう。

# 顧客(customer)テーブルを[年齢(age)]フィールドで昇順に並び替え
SELECT fullname, age FROM customer
ORDER BY age;
+--------------------------------------+-----+
| fullname | age |
+--------------------------------------+-----+
| エディ・ロバーツ | 28 |
| トミー・ゲレロ | 34 |
| スライ・ストーン | 34 |
| マーサ・リーブス | 38 |
| エラ・フィッツジェラルド | 48 |
| マディ・ウォーターズ | 55 |
+--------------------------------------+-----+

ORDER BY句はデフォルトで昇順に並び替えますが、フィールド名の後にDESCをつけることで、並び順を降順にすることができます。

# 顧客(customer)テーブルを年齢で降順に並び替え
SELECT fullname, age FROM customer
ORDER BY age DESC;
+--------------------------------------+-----+
| fullname | age |
+--------------------------------------+-----+
| マディ・ウォーターズ | 55 |
| エラ・フィッツジェラルド | 48 |
| マーサ・リーブス | 38 |
| トミー・ゲレロ | 34 |
| スライ・ストーン | 34 |
| エディ・ロバーツ | 28 |
+--------------------------------------+-----+

複数のフィールドをカンマ( , )で区切って列挙することもできます。その場合は、指定された先頭のフィールドから順に並び替えられます。

SELECT * FROM テーブル名
ORDER BY フィールド名1, フィールド名2, ...;

顧客(customer)テーブルを[性別(sex)]フィールドで並び替えてから、[年齢(age)]フィールドで昇順に並び替えてみましょう。

SELECT fullname, sex, age FROM customer
ORDER BY sex, age;
+--------------------------------------+-----+-----+
| fullname | sex | age |
+--------------------------------------+-----+-----+
| エディ・ロバーツ | 1 | 28 |
| トミー・ゲレロ | 1 | 34 |
| スライ・ストーン | 1 | 34 |
| マディ・ウォーターズ | 1 | 55 |
| マーサ・リーブス | 2 | 38 |
| エラ・フィッツジェラルド | 2 | 48 |
+--------------------------------------+-----+-----+

PROCEDURE

PROCEDURE節には、結果セット内のデータの処理に使用するプロシージャを指定します。

WHERE
条件にマッチしたレコードからフィールドの値を取得

条件がTRUEとなる行だけを選択します。

任意のフィールドから、条件にマッチしたデータのみを取得するには、WHEREに条件式を指定します。

SELECT フィールド名 FROM テーブル名 WHERE 条件式;

条件式は検索フィールドのフィールド名と演算子、関数などを使って表現します。たとえば、顧客(customer)テーブルの中から女性の名前を全て表示する場合を考えてみます。

たとえば、顧客(customer)テーブルの中から女性(sex=2)の名前(fullname)を全て表示するには次のような命令文になります。

SELECT fullname FROM customer WHERE sex=2;

顧客(customer)テーブルを設計する際、性別フィールドの値が男性の場合は数値の1、女性の場合は数値の2と定義しました。そのため、女性のみを選択したい場合は、WHEREに"sex=2"という式を指定します。WHEREの詳しい使い方は、【WHEREでレコードを検索する】を参照してください。

出力結果は下記の通りです。

+--------------------------------------+
| fullname |
+--------------------------------------+
| エラ・フィッツジェラルド |
| マーサ・リーブス |
+--------------------------------------+
2 rows in set (0.01 sec)

WHEREの詳しい情報は【WHEREでレコードを検索する】を参照してください。

WITH ROLLUP

GROUP BYにはWITH ROLLUP修飾子を使用できます(MySQLバージョン4.1.1以降)。


独自拡張構文と実装していない構文

パラメータ 説明
ANSI SQL92 に対するMySQL 拡張構文
HIGH_PRIORITY 常にSELECT処理を優先させる。すぐに完了するクエリにのみ適用すること。
STRAIGHT_JOIN FROMで記述したテーブルの順序通りに結合するように指定する。
> SELECT * FROM t1 STRAIGHT_JOIN t2;
SQL_SMALL_RESULT GROUP BYDISTINCTを伴って使用する事ができ、 結果をより小さく最適化するように指示する。
SQL_BIG_RESULT SQL_BIG_RESULTは、GROUP BYやDISTINCTと共に使用する事ができ、結果セットが多くのレコードを持つことをオプティマイザに通知する。
SQL_BUFFER_RESULT 結果をテンポラリテーブルに格納する。これにより、テーブルロックの解除が早くなり、長い時間がかかるような処理結果の送信などを避けることができる。
SQL_CACHE SQL_QUERY_CACHE_TYPE=2 が指定されている場合、クエリの結果をキャッシュする。
SQL_NO_CACHE クエリの結果をキャッシュしない。
SQL_CALC_FOUND_ROWS SQL_CALC_FOUND_ROWS(MySQLバージョン4.0.0以降)では、LIMIT 節を無視した場合に結果セットに含まれるすべてのレコード数を計算するよう MySQLに指示できる。その後、SELECT FOUND_ROWS()を使用して、計算されたレコード数を取り出せる。
MySQLにないSQL構文
OF 結合内の特定の表の選択された行だけをロックする。
NOWAIT 他ユーザがロックしている行をロックしようとすると、制御が戻される。この句を省略すると、行が使用可能になるまで待ってからSELECT文の結果が戻される。
このエントリーをはてなブックマークに追加

テーブル操作

2009年8月14日 コメントの追加

テーブルの作成や変更、削除などのテーブル操作を紹介します。とくにテーブルの作成は複雑な構文になっていますが、必要最低限の構文をおぼえるだけでも十分にテーブルを作成できますので、徐々にマスターしていくと良いでしょう。

CREATE TABLE構文

CREATE TABLEは、データベース内にテーブルを作成するための命令文です。テーブル作成時にフィールドのデータ型やプライマリキーの設定などを行うので、他の命令文と比べて構文が長くなりがちです。ただ、基本的にはテーブル名とフィールド名、それにフィールドとして設定するデータ型を設定するだけなので、構成はシンプルでおぼえやすいでしょう。

簡単なCREATE TABLEの構文は次のとおりです。

CREATE TABLE構文

CREATE TABLE テーブル名 ( フィールド定義, フィールド定義, ... )

CREATE TABLEの後にテーブル名を指定し、フィールドを定義していきます。フィールド定義とは、フィールド名やデータ型などの属性を指定することです。

簡単なCREATE TABLE構文の使用例
# id_c、fullname、sexのフィールドを持ったcustomerテーブルを作成します。
CREATE TABLE customer (
    id_c INT,
    fullname VARCHAR(40),
    sex TINYINT,
);

テーブル名に"customer"を指定し、id_c、fullnameといったフィールドを定義しています。id_cフィールドには、UNSIGNEDとNOT NULLという属性を追加しています。フィールド名を決める前に、すべて小文字で統一するか、単語の接続はアンダーライン( _ )を使うのか、ハイフン( - )を使うかなどを決めておくと良いでしょう。

標準SQL

CREATE TABLE構文は基本的な操作はほぼ同じですが、細部は各DBMS製品によって大きく仕様が異なります。ここではMySQLのCREATE TABLEをベースに説明していきますが、他のDBMSのことも考慮できるように、標準のSQLを確認しておきましょう。ここではSQL99の構文を紹介します。

SQL99の構文

CREATE {GLOBAL TEMPORARY or LOCAL TEMPORARY} TABLE テーブル名
    [ON COMMIT {PRESERVE ROWS or DELETE ROWS}
    フィールド名 データ型 属性 ,...
    or [LIKE テーブル名]
    or [table_constraint],...]

TEMPORARYは一時表を作成します。GLOBALを指定した場合は全てのユーザセッションから利用でき、LOCALを指定した場合はそれを作成したユーザセッションだけから利用できます。ON COMMIT PRESERVE ROWSは一時表に対するデータ変更を保存し、ON COMMIT DELETE ROWSは、COMMIT後に表の内容を全て消します。LIKE テーブル名オプションは、既存の表と同じ列定義と表制約を持つ新しい表を作成します。

CREATE TABLE構文

次は、MySQLのCREATE TABLE構文です。

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] テーブル名
    [(作成定義, ...)] [テーブルオプション] SELECT構文

キーワードの説明
TEMPORARY
TEMPORARY属性は、テンポラリテーブルを作成します。テンポラリテーブルは、コネクションごとに作成できるテーブルで、切断時に自動的に削除されます。
IF NOT EXISTS
IF NOT EXISTS属性は、テーブル名で指定したテーブルがすでに存在する場合、エラーを発生せずにテーブルの作成をキャンセルします。

[作成定義]、[テーブル定義]、[SELECT構文] などの太字の語句に関しては、下記を参照してください。

作成定義

作成定義では、フィールド名や属性などを定義します。

フィールド定義

フィールド名 データ型 [NOT NULL or NULL] [DEFAULT デフォルト値]
    [AUTO_INCREMENT] [UNIQUE [KEY] or [PRIMARY] KEY]
    [COMMENT '文字列'] [リファレンス定義]

NOT NULL
フィールドの値としてNULLを許可するかどうかを指定します。NOT NULLを指定しなければ、NULLは許可された設定になります。
INDEX、UNIQUE、PRIMARY KEYのいずれかを指定したフィールドは、NOT NULLを指定する必要があります。
DEFAULT
DEFAULT属性は、フィールドのデフォルト値を設定します。レコード登録時にフィールドの値が指定されていなかった場合、DEFAULT属性があれば自動的にデフォルト値が割り当てられます。
AUTO_INCREMENT -MySQL拡張
AUTO_INCREMENT属性は、新しいレコードの登録時に、自動的にAUTO_INCREMENT属性のあるフィールドの最大値+1を設定します。AUTO_INCREMENTを指定できるのは整数型のフィールドに対してだけです。AUTO_INCREMENTの値は1からはじまります。

自動インクリメントの定義は、各DBMS製品によって名称、仕様が違います。MySQLでは、AUTO_INCREMENTを使います。

※ 1つのテーブルには1つしかAUTO_INCREMENTフィールドを指定できません。

UNIQUE
UNIQUEを宣言したフィールドは、値の重複が禁止されます。重複する値を持ったレコードを挿入しようとした場合は、エラーが発生します。
[PRIMARY KEY]
PRIMARY KEY属性は、プライマリキーを設定します。プライマリキーとは、レコードを特定するためのユニークな値が保障されたフィールドのことです。プライマリキーはテーブルに対して1つの列だけで、その値は重複することなく、NULLも許可されません。このような条件を満たすことで、レコードを1行ずつ識別することが可能になります。
PRIMARY KEY ( )
PRIMARY KEYは複合プライマリキーにすることができます。複合プライマリキーは、この書式のみ設定可能で、上記の[PRIMARY KEY]書式では設定できません。
※プライマリキーが複数のフィールドから構成されている場合、それを強調したい際に複合プライマリキーと呼ぶことがあります。

先に定義したフィールドは、PRIMARYやUNIQUE、INDEXなどの制約を追加できます。

フィールド定義
    or [CONSTRAINT [シンボル]] PRIMARY KEY [インデックス型] (インデックスフィールド名,...) [インデックスオプション ...]
    or {INDEX|KEY} [インデックス名] [インデックス型] (インデックスフィールド名,...) [インデックスオプション ...]
    or [CONSTRAINT [シンボル]] UNIQUE [INDEX|KEY] [インデックス名] [インデックス型] (インデックスフィールド名,...)
    [インデックスオプション ...]
    or {FULLTEXT|SPATIAL} [INDEX|KEY] [インデックス名] (インデックスフィールド名,...) [インデックスオプション ...]
    or [CONSTRAINT [シンボル]] FOREIGN_KEY [インデックス名] (インデックスフィールド名,...) [リファレンス定義]
    or CHECK (expr)

CONSTRAINT

インデックスと似ていますが、さらに外部キーも設定できます。

インデックスフィールド名

インデックスのフィールド名を指定します。

フィールド名 [(長さ)] [ASC or DESC]

インデックス型

BTREE、もしくはHASHのインデックス型を指定します。

USING {BTREE or HASH}

インデックスオプション

インデックスキーのブロックサイズや、インデックス型といったインデックスのオプションを指定します。

KEY_BLOCK_SIZE値
    or インデックス型
    or WITH PARSER parser_name

 

INDEX / KEY
INDEX属性は、インデックスを作成します。インデックスとは、レコードの索引のことで、データの検索速度を向上させるためのものです。 INDEXを指定したフィールドにはNOT NULLを指定する必要があります。
KEYはINDEXの同義語です。
FOREIGN KEY
CHECK
REFERENCES
互換性のためだけに用意されており、実際には何も行いません。
FULLTEXT-MySQL拡張
FULLTEXT属性は、フルテキスト検索用のインデックスを作成します。
CHAR、VARCHAR、TEXT型のみ作成する事ができます。

MyISAMストレージ エンジンだけがFULLTEXTインデックスをサポートします。
データ型

フィールドに指定できるデータ型は下記の通りです。

BIT[(長さ)]
    or TINYINT[(長さ)] [UNSIGNED] [ZEROFILL]
    or SMALLINT[(長さ)] [UNSIGNED] [ZEROFILL]
    or MEDIUMINT[(長さ)] [UNSIGNED] [ZEROFILL]
    or INT[(長さ)] [UNSIGNED] [ZEROFILL]
    or INTEGER[(長さ)] [UNSIGNED] [ZEROFILL]
    or BIGINT[(長さ)] [UNSIGNED] [ZEROFILL]
    or REAL[(長さ,小数点)] [UNSIGNED] [ZEROFILL]
    or DOUBLE[(長さ,小数点)] [UNSIGNED] [ZEROFILL]
    or FLOAT[(長さ,小数点)] [UNSIGNED] [ZEROFILL]
    or DECIMAL(長さ,小数点) [UNSIGNED] [ZEROFILL]
    or NUMERIC(長さ,小数点) [UNSIGNED] [ZEROFILL]
    or DATE
    or TIME
    or TIMESTAMP
    or DATETIME
    or YEAR
    or CHAR(長さ) [CHARACTER SET キャラクタセット名] [COLLATE collation_name]
    or VARCHAR(長さ) [CHARACTER SET キャラクタセット名] [COLLATE collation_name]
    or BINARY(長さ)
    or VARBINARY(長さ)
    or TINYBLOB
    or BLOB
    or MEDIUMBLOB
    or LONGBLOB
    or TINYTEXT [BINARY] [CHARACTER SET キャラクタセット名] [COLLATE collation_name]
    or TEXT [BINARY] [CHARACTER SET キャラクタセット名] [COLLATE collation_name]
    or MEDIUMTEXT [BINARY] [CHARACTER SET キャラクタセット名] [COLLATE collation_name]
    or LONGTEXT [BINARY] [CHARACTER SET キャラクタセット名] [COLLATE collation_name]
    or ENUM(値1,値2,値3,...) [CHARACTER SET キャラクタセット名] [COLLATE collation_name]
    or SET(値1,値2,値3,...) [CHARACTER SET キャラクタセット名] [COLLATE collation_name]
    or 特殊データ型

[BINARY]
バイナリ文字列を指定します。

リファレンス定義

リファレンス制約を設定します。

REFERENCES テーブル名 [(インデックスフィールド名,...)]
    [MATCH FULL or MATCH PARTIAL or MATCH SIMPLE]
    [ON DELETE リファレンスオプション]
    [ON UPDATE リファレンスオプション]

リファレンスオプション

RESTRICTやCSCADEといった参照操作を設定します。

リレーショナルを作成するには、参照元と参照側でデータ整合性が取れている必要があります。レコードが削除、更新される際にデータ整合性を維持するために5種類の参照操作が用意されています。

RESTRICT or CASCADE or SET NULL or NO ACTION

テーブルオプション

テーブルの設定を行います。たとえば、テーブルのストレージエンジンを指定したり、初期のAUTO_INCREMENT値、デフォルト文字セットなどが指定可能です。

[TABLESPACE テーブルスペース名 STORAGE DISK]
ENGINE [=] エンジン名
    or AUTO_INCREMENT [=] 値
    or AVG_ROW_LENGTH [=] 値
    or [DEFAULT] CHARACTER SET キャラクタセット名
    or CHECKSUM [=] {0 or 1}
    or COLLATE collation_name
    or COMMENT [=] '文字列'
    or CONNECTION [=] 'connect_文字列'
    or DATA DIRECTORY [=] 'absolute path to directory'
    or DELAY_KEY_WRITE [=] {0 or 1}
    or INDEX DIRECTORY [=] 'absolute path to directory'
    or INSERT_METHOD [=] { NO or FIRST or LAST }
    or KEY_BLOCK_SIZE [=] 値
    or MAX_ROWS [=] 値
    or MIN_ROWS [=] 値
    or PACK_KEYS [=] {0 or 1 or DEFAULT}
    or PASSWORD [=] '文字列'
    or ROW_FORMAT [=] {DEFAULT|DYNAMIC|FIXED|COMPRESSED|REDUNDANT|COMPACT}
    or UNION [=] (テーブル名[,テーブル名]...)

AUTO_INCREMENT
AUTO_INCREMENTの初期値を指定します。
CHARACTER SET
テーブルのデフォルト文字セットを指定します。
COMMENT
フィールドに255文字までのコメントを設定します。SHOW CREATE TABLE、SHOW FULL COLUMNS構文で表示されます。

 

パーティションオプション

パーティションオプションは、テーブルの領域確保をコントロールします。

PARTITION BY
    [LINEAR] HASH(expr)
    or [LINEAR] KEY(フィールドリスト)
    or RANGE(expr)
    or LIST(expr)
[PARTITIONS num]
[SUBPARTITION BY
    [LINEAR] HASH(expr)
    or [LINEAR] KEY(フィールドリスト)
    [SUBPARTITIONS num]
]
[(パーティション定義 [, パーティション定義] ...)]

パーティション定義

パーティションの属性を設定します。

PARTITION パーティション名
    [VALUES {LESS THAN (expr) or MAXVALUE or IN (値リスト)}]
    [[STORAGE] ENGINE [=] エンジン名]
    [COMMENT [=] 'コメントテキスト' ]
    [DATA DIRECTORY [=] 'データのディレクトリ']
    [INDEX DIRECTORY [=] 'インデックスディレクトリ']
    [MAX_ROWS [=] max_number_of_rows]
    [MIN_ROWS [=] min_number_of_rows]
    [TABLESPACE [=] (tablespace_name)]
    [NODEGROUP [=] node_group_id]
    [(サブパーティション定義 [, サブパーティション定義] ...)]

サブパーティション定義

パーティションはSUBPARTITION BYでサブ・パーティションに分解する事ができます。サブパーティションは、パーティションと同じように機能します。

SUBPARTITION logical_name
    [[STORAGE] ENGINE [=] engine_name]
    [COMMENT [=] 'comment_text' ]
    [DATA DIRECTORY [=] 'data_dir']
    [INDEX DIRECTORY [=] 'index_dir']
    [MAX_ROWS [=] max_number_of_rows]
    [MIN_ROWS [=] min_number_of_rows]
    [TABLESPACE [=] (tablespace_name)]
    [NODEGROUP [=] node_group_id]

SELECT構文

CREATE TABLE構文の最後にSELECT構文を追加して、既存のテーブルをベースにして新しいテーブルを作成する事ができます。

[IGNORE or REPLACE] [AS] SELECT ... (Some legal select statement)

CREATE TABLEの使い方

テーブルを作成する際の手順は、CREATE TABLEに続き、作成するテーブル名を指定します。次に、カッコ内にフィールドの定義を列挙していきます。フィールドの定義はフィールド名とデータ型、それに制約などを必要なだけ宣言します。

CREATE TABLE構文の使用例

# テーブルの作成
CREATE TABLE customer (
   # 作成定義と制約
    id_c INT UNSIGNED NOT NULL AUTO_INCREMENT,
    fullname VARCHAR(40) NOT NULL,
    age TINYINT UNSIGNED NOT NULL ,
    sex TINYINT UNSIGNED NOT NULL,
    email VARCHAR(50),

   # 制約
    PRIMARY KEY (id_c)
);

# テーブルのコピー
CREATE TABLE note AS
    SELECT id_c, fullname FROM customer;

フィールド名とデータ型の宣言は必須ですが、デフォルト値やフィールドの制約は省略可能です。idフィールドはプライマリキーですので、NOT NULL で NULL値を拒否するように制約しています。また、AUTO_INCREMENTで、自動的に数値が割り当てられるように設定しています。
制約の行で、プライマリキーとインデックスを設定しています。


ALTER TABLE構文

ALTER TABLEは、テーブルの構造を変更するための構文です。例えば、フィールドの追加や削除、インデックスの作成や削除、既存のフィールド属性の変更、フィールド名やテーブル名の変更などの機能が用意されています。
ALTER TABLEは、CREATE TABLEと同じく複雑な構文となっていますが、シンプルに使うこともできます。

ALTER TABLE構文

ALTER [IGNORE] TABLE テーブル名 [詳細定義 , 詳細定義 ...]

キーワードの説明
IGNORE -MySQL拡張
ユニークキーの重複があった行に対し、最初のレコードだけを使用し、他を削除します。
IGNOREが指定されない場合はエラーが発生し、ロールバックされます。

ロールバックとは
ロールバックとは、データ更新などで障害が起こったときに、記録してあるチェックポイントまでデータを戻し、改めて処理を開始することです。

ALTER TABLE構文の属性の多くは、CREATE TABLE構文と似ています。それらの詳細については、「CREATE TABLE構文」 をご参照ください。

詳細定義

  ADD [COLUMN] 作成定義 [FIRST or AFTER フィールド名 ]
      or ADD [COLUMN] (作成定義, 作成定義, ...)
      or ADD INDEX [インデックス名] (インデックスフィールド名, ...)
      or ADD PRIMARY KEY (インデックスフィールド名,...)
      or ADD UNIQUE [インデックス名] (インデックスフィールド名, ...)
      or ADD FULLTEXT [インデックス名] (インデックスフィールド名, ...)
      or ADD [CONSTRAINT シンボル] FOREIGN KEY インデックス名 (インデックスフィールド名, ...) [リファレンス定義]
      or ALTER [COLUMN] フィールド名 {SET DEFAULT リテラル or DROP DEFAULT}
      or CHANGE [COLUMN] 変更前フィールド名 作成定義 [FIRST or AFTER 変更後フィールド名]
      or MODIFY [COLUMN] 作成定義 [FIRST or AFTER フィールド名]
      or DROP [COLUMN] フィールド名
      or DROP PRIMARY KEY
      or DROP INDEX インデックス名
      or DISABLE KEYS
      or ENABLE KEYS
      or RENAME [TO] テーブル名
      or CHARACTER SET キャラクタセット名 [COLLATE collation_name]
      or ORDER BY フィールド名
      or テーブルオプション

テーブルオプション

テーブルオプション ...
or ADD [COLUMN] フィールド定義 [FIRST or AFTER フィールド名 ]
or ADD [COLUMN] (フィールド定義,...)
or ADD {INDEX|KEY} [インデックス名] [インデックス型] (インデックスフィールド名,...)
or ADD [CONSTRAINT [symbol]]
    PRIMARY KEY [インデックス型] (インデックスフィールド名,...)
or ADD [CONSTRAINT [symbol]]
    UNIQUE [INDEX|KEY] [インデックス名] [インデックス型] (インデックスフィールド名,...)
or ADD FULLTEXT [INDEX|KEY] [インデックス名] (インデックスフィールド名,...)
    [WITH PARSER parser_name]
or ADD SPATIAL [INDEX|KEY] [インデックス名] (インデックスフィールド名,...)
or ADD [CONSTRAINT [symbol]]
    FOREIGN KEY [インデックス名] (インデックスフィールド名,...)
    [リファレンス定義]
or ALTER [COLUMN] フィールド名 {SET DEFAULT literal or DROP DEFAULT}
or CHANGE [COLUMN] old_フィールド名 フィールド定義
    [FIRST|AFTER フィールド名]
or MODIFY [COLUMN] フィールド定義 [FIRST or AFTER フィールド名]
or DROP [COLUMN] フィールド名
or DROP PRIMARY KEY
or DROP {INDEX|KEY} インデックス名
or DROP FOREIGN KEY fk_symbol
or DISABLE KEYS
or ENABLE KEYS
or RENAME [TO] 新しいテーブル名
or ORDER BY フィールド名 [, フィールド名] ...
or CONVERT TO CHARACTER SET キャラクタセット名 [COLLATE collation_name]
or [DEFAULT] CHARACTER SET キャラクタセット名 [COLLATE collation_name]
or DISCARD TABLESPACE
or IMPORT TABLESPACE
or PARTITION BY パーティション・オプション
or ADD PARTITION (パーティション定義)
or DROP PARTITION パーティション名
or COALESCE PARTITION number
or REORGANIZE PARTITION パーティション名 INTO (パーティション定義)
or ANALYZE PARTITION パーティション名
or CHECK PARTITION パーティション名
or OPTIMIZE PARTITION パーティション名
or REBUILD PARTITION パーティション名
or REPAIR PARTITION パーティション名
or REMOVE PARTITIONING

インデックスフィールド名:

フィールド名 [(長さ)] [ASC or DESC]

インデックス型:

USING {BTREE or HASH}

キーワードの説明
[テーブルオプション]
[テーブルオプション]はCREATE TABLEで利用する事ができるENGINE、AUTO_INCREMENT、AVG_ROW_LENGTHといったテーブルオプションを意味します。

下記のクエリは、ALTER TABLEでAUTO_INCREMENTのカウンターを初期化しています。

ALTER TABLE t1 AUTO_INCREMENT = 0;

IGNORE
-MySQL拡張
IGNOREは、新しいテーブルのユニークキーに複製があった場合でも、エラーを発生せずに、ユニークキーに複製された最初のレコードだけを使い、それ以外のレコードを削除します。不正な値は、適合する許容値に一番近い値まで切り捨てられます。
ADD
ADDは、新しいフィールドを追加します。 FIRST、または AFTERを指定することで、テーブルの指定した位置にフィールドを追加することができます。
ALTER TABLE テーブル名 ADD フィールド名A VARCHAR(20) AFTER フィールド名B;
AFTER
FIRST
FIRST、AFTERは、テーブルの指定した位置に、フィールドを追加することができます。FIRSTが指定したフィールドの前、AFTERが指定したフィールドの後に新しいフィールドを挿入します。デフォルトでは、フィールドはテーブルの最後に追加されます。
CHANGE
-MySQL拡張
フィールドの名前を変更します。

次は、現在のフィールド名から新しいフィールド名に変更し、新しいデータ型を指定する方法です。

ALTER TABLE テーブル名 CHANGE 元のフィールド名 新しいフィールド名 INTEGER;

データ型だけを変更したい場合はMODIFYを使ってください。

MODIFY
-Oracle拡張
フィールドのデータ型のみを変更します。
ALTER TABLE テーブル名 MODIFY フィールド名
    TINYINT NOT NULL
;
DROP
-MySQL拡張
フィールドを削除します。
ALTER TABLE テーブル名 DROP フィールド名;
ORDER BY
ORDER BYは、指定した順にフィールドを並び替えて新しいテーブルを生成することができます。このオプションは、毎回同じ順番でレコードのクエリを行う場合に有用です。

※挿入と削除の後にはこの順序は保持されません。テーブルに大きな変更をした後にこのキーワードを使用することで、より高い性能を得ることができます
DROP INDEX
-MySQL拡張
インデックスを削除します。インデックス元の全てのフィールドが削除されると、そのインデックスも削除されます。
ALTER TABLE テーブル名 DROP INDEX フィールド名;
DROP PRIMARY KEY
プライマリ・インデックスを削除します。もしプライマリ・インデックスが存在しなければ、そのテーブルの最初のUNIQUE インデックスが削除されます。
ALTER TABLE テーブル名 DROP PRIMARY KEY;
DISABLE KYES -MySQL拡張
ENABLE KEYS -MySQL拡張
ALTER TABLE ... DISABLE KEYSによって、MyISAMテーブルの非ユニークなインデックスの更新を停止します。 その後、ALTER TABLE ... ENABLE KEYSによって、欠落しているインデックスを再作成できます。MySQLでは、キーを無効化することにより、大量の挿入の際の処理速度が大幅に迅速化されます。
FOREIGN KEY
REFERENCES
InnoDBストレージエンジンにサポートされています。

ALTER TABLEの使用例

ALTER TABLEをテストするために、テスト用のテーブルt1を作成します。

CREATE TABLE t1 (a INTEGER, b CHAR(10));

フィールド名aのデータ型と属性をINTEGERからTINYINT NOT NULLに変更します。

ALTER TABLE t1 MODIFY a TINYINT NOT NULL;

フィールド名aのフィールド名をaからbに変更します。

ALTER TABLE t1 CHANGE a b TINYINT NOT NULL;

TIMESTAMP型のフィールドをcという名称で追加します。

ALTER TABLE t1 ADD c TIMESTAMP;

フィールド名aにインデックスを追加します。

ALTER TABLE t1 ADD INDEX (a);

フィールド名cを削除します。

ALTER TABLE t1 DROP c;

フィールド名cをAUTO_INCREMENT属性つきで追加します。

ALTER TABLE t1 ADD c INT UNSIGNED NOT NULL AUTO_INCREMENT, ADD PRIMARY KEY (c);

テーブル名t1をt2に変更します。

ALTER TABLE t1 RENAME t2;


OPTIMIZE TABLE構文

OPTIMIZE TABLE構文

OPTIMIZE TABLE テーブル名

OPTIMIZE TABLEは、テーブルの最適化を行います。レコードの大部分を削除したり、VARCHAR、BLOB、TEXTフィールドなどの可変長行となっているテーブルに多くの変更を加えたりした場合などに OPTIMIZE TEABLE処理を行うと良いでしょう。


DROP TABLE構文

テーブルを削除するには、DROP TABLEを使います。テーブルのデータも削除されるので、注意してください。

DROP TABLE構文

DROP TABLE [IF EXISTS] テーブル名 [, テーブル名,...]

DROP TABLE構文の使用例

# customer テーブルを削除
DROP TABLE customer;

キーワードの説明
IF EXISTS
-MySQL 拡張
データベースが存在しないことに関するするエラーを防ぎます。
IF EXISTSを指定せずに、存在しないデータベース名を指定した場合は、エラーが発生します。
このエントリーをはてなブックマークに追加

Jump to the top