Visual Basic データベース講座 |
いよいよデータベース操作の要であるSQL文を説明します。今回はすべてのはじまりであるSELECT文の基本的な使用方法を取り上げます。
概要 ・クエリおよびビューデザイナを使用するとVBで簡単にSQL文を作成、実行、テストできる。 ・SELECT文は SELECT ... FROM ... WHERE ... GROUP BY ... ORDER BY ... という構造が基本。 ・SELECT句では取得する項目を列名、式を使って指定する。式には関数も使用できる。 ・FROM句では取得元となるテーブルなどを指定する。複数のテーブルから取得する場合は次次回に説明する。 ・WHERE句では対象のレコードを指定する式を記述する。式には関数も使用できる。 ・ORDER BY句には一覧の並び順を列名、式で記述する。式には関数も使用できる。 |
データベースから何らかのデータを取得する場合には必ずといっていいほどSQLのSELECT文を使用します。このデータベース講座でも今までSELECT文を使用してデータを取得する例を紹介してきました。その例の一つをもう一度見てみましょう。
'▼データ取得 Dim Cn As New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Database\Animals.mdb") Dim SQLCm As OleDbCommand = Cn.CreateCommand Dim Adapter As New OleDbDataAdapter(SQLCm) Dim Table As New DataTable SQLCm.CommandText = "SELECT 説明 FROM T_目マスタ" Adapter.Fill(Table) '▼値の表示 DataGridView1.DataSource = Table '▼後処理 Table.Dispose() Adapter.Dispose() SQLCm.Dispose() Cn.Dispose() |
■リスト1
この例はT_目マスタの説明の一覧を取得してDataTable型の変数Tableに記録します。そして、DataGridView1にTableの内容を表示するというものです。
ここで、『T_目マスタの説明の一覧』という指定を行っているのがSELECT 説明 FROM T_目マスタの部分で、これこそがSQLのSELECT文です。ですから、このSQLの内容を変えればDataGridView1にはまったく異なるものを表示するようにもできますし、エラーになる場合もあります。
値の取得だけでなく、値をデータベースに書き込むときにもSQL文を使用します。その時にはSELECT文ではなくUPDATE文やINSERT文を使用します。またレコードを削除する場合にはDELETE文を使用します。
このようにSQL文はデータベースとアプリケーションをつなぐ非常に重要な役割をもっており、SQLなくしてデータベースアプリケーションは作成できないといっても過言ではありません。
SQLにはANSIやISOによって標準的な仕様が定められており、VBだけではなくあらゆる言語、あらゆるデータベースで使用されます。ただ、残念なことにデータベース製品ごとにSQLの仕様は少しずつ異なっており、各社は完全にSQLの標準仕様に準拠しているわけではありません。
そのため基本的なSQLのルールはどの製品でも共通していますが、細かい点や拡張機能になるとある製品では有効なのにある製品ではエラーになってしまうという現象が発生します。同じマイクロソフト社の製品であるAccessとSQL Serverの間でさえ違いがあります。
これから数回にわたってSQLについて説明しますが、製品ごとに違いが大きい場合にはその都度注意書きをするつもりです。
プログラムを作成してSQLを実行させることでいろいろなSQL文のテストができますが、これでは少し煩雑です。RDBMS製品を持っている方でしたらたいていの場合その製品に付属しているツールを使ってSQL文を簡単に試すことができて効率的です。Accessの場合にはAccess自体にその機能がありますし、SQL Serverの場合にはSQL クエリ アナライザやManagement StudioといったツールでSQLを試すことができます。
幸いなことにこれらのツールを持っていなくてもVB自体にSQL文をテスト・実行する機能がありますのでその機能を紹介します。
VBを起動したらデータベースエクスプローラ(またはサーバーエクスプローラ)で対象のデータベースをクリックしてから、右クリックして「新しいクエリ」を選択してください。
メモ データベースエクスプローラまたはサーバーエクスプローラにデータベースを追加する方法はデータベース講座第2回 データベースを見る、いじるで説明しています。 |
これだけで画面が切り替わってSQLの直接実行が可能になります。画面を開いたときに「テーブルの追加」というダイアログが表示されますが、とりあえず無視して「閉じる」をクリックしてください。
この画面の正式名称は「クエリおよびビューデザイナ」と呼ぶようですが、ここでは略して「クエリデザイナ」と呼ぶことにします。
クエリデザイナでは単にSQLを実行するだけではなく、SQL文自体の生成を視覚的にサポートする機能も備わっています。
■画像1:クエリおよびビューデザイナ。通称クエリデザイナ。
各部はペインと呼ばれる領域にわかれています。それぞれのペインの役割を簡単に書いておきます。
ダイアグラムペインはSQL文の生成を補助します。視覚的なツールによってマウスの操作でテーブルや列を選択・結合してSQL文を生成するのが特徴です。
抽出条件ペインは対象のSQL文に対する条件を表形式で記述してSQL文の生成を助けます。
SQLペインには自由にSQL文を入力できます。このペインがメインになります。SQLペインに入力された内容が解析可能であればダイアグラムペインのおよび抽出条件ペインの内容は連動して変化します。逆にダイアグラムペイン、抽出条件ペインで設定を変えた場合はその変更はSQLペインに反映されます。
結果ペインはSQLを実行した場合の結果を表示します。このおかげでいちいち自分でプログラムを組まなくても簡単にSQLを試して結果を確認することができます。
ツールバーの役割も確認しておきます。この画面ではクエリデザイナツールバーを使用します。このツールバーが表示されていない場合は[表示]メニューの[ツールバー」で表示させることができます。
■画像2:クエリデザイナツールバー
今回はSQL文の説明とテストのためにクエリデザイナを使うのですから、ダイアグラムペインと抽出条件ペインは非表示にしてシンプルにSQLペイント結果ペインだけにしてしまいましょう。
■画像3:SQLペイント結果ペインだけのシンプルな構成のクエリデザイナ。
すると、この画面のように上にSQL文を入力して、ツールバーの実行を押すと下に結果が表示されるという構成になります。
これからいろいろなSQLの説明をしますが、この状態でテストすると簡単です。
SELECT文は数あるSQLの中でも最も使用頻度が高く、また最も奥が深く、最も難しいものです。SELECTを制する者はSQLを制するといっても良いでしょう。
「最も難しい」などというと腰が引けてしまう方もいらっしゃるかもしれませんが、単純なものでよいのであればかなり簡単です。
以下はSELECT文の例です。SQLペインに入力して、実行して結果を確認してみてください。
SELECT 名前 FROM T_動物マスタ |
このSELECT文はT_動物マスタに登録されている名前の一覧を取得します。実行すると結果ペインに名前の一覧が表示されるのが確認できます。また、実行後にはSQLペインの内容が自動的に次のように書き換えられます。
SELECT 名前 FROM [T_動物マスタ] |
このSQL文は最初のSQL文と同じ意味ですが、改行されているのとテーブル名の前後が [ ] でくくられている点が異なります。このようにクエリデザイナは実行時にSQLペインの内容を成形して場合によっては変更してしまいます。
SQLの勉強をする上ではこの機能はありがたくないのですが耐えるしかありません。今後説明するSQL文も実行すると整形・変更されるものがあります。これからの説明ではすべて実行前の形式を紹介します。
なお、実行前のSQL文と実行後のSQL文は書き方が異なっていても意味は一緒です。
さて、SELECT文に話を戻します。
SELECT文は既に説明したようにデータベースから値を取得するために使用します。
SELECT文の基本的な構造は次のようになっています。
SELECT ... | FROM ... | WHERE ... | GROUP BY ... | ORDER BY ... |
この各部署をSELECT句、FROM句、WHERE句などと呼びます。WHERE句以降は省略可能です。FROM句は一部の製品では省略可能ですがAccessでは省略不可です。ここでは「句」と呼んでいますが、「ステートメント」や「節」と呼ぶ場合もあります。
各ステートメントの簡単な概要をまとめておきます。
ステートメント | 読み方 | 機能 |
SELECT | セレクト | 取得する列、値を指定する。 |
FROM | フロム | データ取得元のテーブルを指定する。 |
WHERE | ホエア | レコードの抽出条件を指定する。 |
GROUP BY | グループバイ | 集計単位を指定する。 |
ORDER BY | オーダーバイ | 並び順を指定する。 |
以下ではそれぞれの句の概要を豊富な例とともに説明します。ただしGROUP BY句については次回説明します。ここで説明するのはあくまで概要ですのですべての機能を紹介することはできませんが普通のデータベースアプリケーションの作成ならば95%OKという範囲をカバーしているつもりです。
SELECT句には取得したい列名を記述します。
SELECT 名前 FROM T_動物マスタ |
取得したい列が複数ある場合にはカンマで区切って指定できます。
SELECT 名前, 説明 FROM T_動物マスタ |
キーワードASを使用すると列名に別名を付けることもできます。たとえば、[名前]列を「動物名」というように表示したければ次のように記述します。
SELECT 名前 AS 動物名 FROM T_動物マスタ |
これで実行すると結果ペインに表示される内容は同じですが、列の見出しが「動物名」になっているのが確認できます。プログラムの上ではこのSQLを使ってデータを取得した場合、プログラムから操作する列名も「動物名」になります。
SELECT句ではテーブルに属していない値を指定することもできます。たとえば、次のSQL文は名前の横に必ず「こんにちは」と表示される一覧を取得します。「こんにちは」の列にはASを使って「挨拶」という列名をつけています。
SELECT 名前, 'こんにちは' AS 挨拶 FROM T_動物マスタ |
このSQLの実行例は次の通りです。
名前 | 挨拶 |
イモリ | こんにちは |
サンショウウオ | こんにちは |
トノサマガエル | こんにちは |
… | … |
この例が示すようにSQL文の中で文字列の値を示すには ' (シングルクォーテーション)で値の前後をくくります。
文字列ではなく数値を指定することもできます。次のSQL文は名前の横に必ず「627」と表示される一覧を取得します。「627」の列にはASを使って「数値」という列名をつけています。
SELECT 名前, 627 AS 数値 FROM T_動物マスタ |
このSQLの実行例は次の通りです。
名前 | 挨拶 |
イモリ | 627 |
サンショウウオ | 627 |
トノサマガエル | 627 |
… | … |
「こんにちは」や「627」などの値の列にASを使って列名を付けなかった場合は列名はなしになります。プログラム上では列名がない列には列番号を使ったアクセスのみが可能となります。しかし、クエリデザイナは列名がなしにならないように自動的に列名をつけてしまいます。上記の例で「AS 数値」を削除して実行すると、クエリデザイナによって自動的に「AS Expr1」という列名がつけられるのが確認できます。
値は固定値だけではなく、計算した値を表示させることもできます。次のSQL文では名前の横に必ず「101」と表示される一覧を取得します。
SELECT 名前, 100 + 1 FROM T_動物マスタ |
このSQLの実行例は次の通りです。
名前 | Expr1 |
イモリ | 101 |
サンショウウオ | 101 |
トノサマガエル | 101 |
… | … |
計算式の要素には列名を使用することもできます。次のSQL文では名前の横に、…「です」と表示される一覧を取得します。
SELECT 名前, 名前 & 'です' FROM T_動物マスタ |
このSQLの実行例は次の通りです。
名前 | Expr1 |
イモリ | イモリです |
サンショウウオ | サンショウウオです |
トノサマガエル | トノサマガエルです |
… | … |
計算式に使用できる演算子はデータベース製品により若干異なります。特にAccessでは文字列の結合には「&」を使用しますが、他の多くの製品では「||」という記号を使用します。
計算式には演算子の他に関数を使用することもできます。次のSQL文では名前の横に、名前の文字数が表示される一覧を取得します。
SELECT 名前, Len(名前) AS 名前の文字数 FROM T_動物マスタ |
このSQLの実行例は次の通りです。
名前 | 名前の文字数 |
イモリ | 3 |
サンショウウオ | 7 |
トノサマガエル | 7 |
… | … |
使用できる関数の種類はデータベース製品により多いく異なりますので、それぞれのヘルプを見るほかありません。Accessはその中でも異色でVBの関数がそのままSQL文中で使用できます。ただしVBの関数といってもVB6の関数のことなのでVB6以前の経験がない方はやはりヘルプに目を通した方が良いでしょう。
関数の使用例をもうひとつ挙げておきます。このSQL文では名前の横に、名前の最後の文字を1文字だけ表示する一覧を取得します。
SELECT 名前, Right(名前, 1) AS 名前の最後の文字 FROM T_動物マスタ |
このSQLの実行例は次の通りです。
名前 | 名前の最後の文字 |
イモリ | リ |
サンショウウオ | オ |
トノサマガエル | ル |
… | … |
SELECT句には必ずしもテーブルに存在する列を指定する必要はありません。値だけを取得することもできます。次のSQL文では627という値を一覧で取得します。
SELECT 627 FROM T_動物マスタ |
このSQLの実行例は次の通りです。
Expr1 |
627 |
627 |
627 |
… |
この一覧はT_動物マスタのレコード数と同じ数だけ627を取得します。このSQL文自体はおそらく実際の役にはたたないでしょうが、このような考え方は役に立ちます。SQLのセンスを磨いておいてください。
次にT_動物マスタのすべての列を取得する例を紹介します。カンマで区切ってすべての列名を指定してもよいのですが、記号「*」を使うとそれだけで「すべて」という意味になるので楽です。
SELECT * FROM T_動物マスタ |
このSQL文を実行すると自動的にSQL文は次のように変更されます。
SELECT 動物ID, 名前, 画像, 目ID, 説明 FROM [T_動物マスタ] |
このSQL文はどちらも同じ意味です。実際のプログラム中でもどちらのSQL文を使用しても構いません。
「*」がある場合でも、カンマで区切って列や値を追加することができます。次のSQL文は有効です。
SELECT *, 627 FROM T_動物マスタ |
最後にSELECT句の少し変わった例を紹介します。SELECT句の中でTOPキーワードを使用すると最初の数件のレコードのみを取得するように指示することができます。次のSQL文ではT_動物マスタの最初の10件を取得します。
SELECT TOP 10 * FROM T_動物マスタ |
ただし、注意して欲しいのはテーブル自体には人間が言うところの並び順という概念はないということです。最初の10件といってもどの10件のことなのかランダムで決定され、結果は保障されていません。結果を保証するには後で説明するORDER BYを使って並び順を指定する必要があります。また、10番目のレコードと11番目のレコードがたまたま同じ値の場合はTOP 10と指定しているにもかかわらず11件のレコードを取得します。
TOPでは件数ではなくパーセントを指定することもできます。次のSQL文ではT_動物マスタのレコードの中から最初の10%分のレコードを取得します。
SELECT TOP 10 PERCENT * FROM T_動物マスタ |
FROM句には対象のデータをどのテーブルから取得するのかを指定します。テーブル以外にもテーブルとみなすことができるものならここに指定することができます。なにをテーブルとみなすかはデータベース製品によって異なります。
AccessではFROM句にはテーブルの他に「クエリ」と呼ばれるオブジェクトと「サブクエリ」と呼ばれるものを指定することができます。「クエリ」、「サブクエリ」については機会があったら説明します。なおAccessに限って言うと「クエリ」と「サブクエリ」は名前は似ていますがまったく別のものです。
FROM句の書き方は少し前まではデータベース製品によって大きく異なっていたのですが、最近はかなり統一されてきた感じです。
既にSELECT句の説明のところで単純なFROM句も登場しているのでここでは単純でない場合のFROM句を取り上げます。
単純でない場合とはテーブルを2つ以上使用する場合です。
たとえば、次のSQL文は動物の名前とその目の名前を並べて表示します。動物の名前はT_動物マスタから取得しますし、目の名前はT_目マスタから取得します。長いですがFROMの後ろはすべてFROM句です。
SELECT 名前, 目名 FROM T_動物マスタ INNER JOIN T_目マスタ ON T_動物マスタ.目ID = T_目マスタ.目ID |
このSQLの実行例は次の通りです。
名前 | 目名 |
イモリ | 有尾目 |
サンショウウオ | 有尾目 |
トノサマガエル | 無尾目 |
… | … |
このSQL文では単にT_動物マスタとT_目マスタの2つを指定すればよいというだけではなく、T_動物マスタとT_目マスタの関係を記述しなければなりません。われわれが使用しているAnimals.mdbではT_動物マスタの目IDはT_目マスタの目IDと一致するように設計されており、そのことをFROM句で指定しています。
2つ以上のテーブルを結合して1つの結果を得るSQL文には少し説明が必要ですので次の次の回にまとめて説明する予定です。
それまでは「詳細はよくわからないけど、こんなこともできるんだなぁ」という例として頭の片隅に留めておいてください。
発展学習 - SQL
ServerとOracleでのFROM句の省略 発展学習では意欲的な方のために現段階では特に理解する必要はない項目を解説します。 SQL ServerではFROM句は省略可能なので次のようなSQL文が記述できます。
このSQL文は計算結果を1レコードだけ返します。 OracleではFROM句は省略不可ですが、かならず1レコードだけ存在するDUALというシステムテーブルが存在するので、次のようにして同じことができます。
Accessではこのどちらも使用できません。 |
WHERE句は対象となるレコードの条件を指定します。WHERE句がない場合はすべてのレコードが対象になります。
※ただし、FROM句で指定できるINNER JOINキーワードには事実上対象のレコードを限定する機能があります。INNER JOINについては次の次の回で説明します。
WHERE句を使用するとたとえば動物IDが50以下のレコードだけを取得するということができます。
SELECT * FROM T_動物マスタ WHERE 動物ID <= 50 |
次のSQL文は名前が「ゴリラ」であるレコードだけを取得します。
SELECT * FROM T_動物マスタ WHERE 名前 = 'ゴリラ' |
次のSQL文は同様に名前が「アンドリューサルクス」であるレコードだけを取得します。
SELECT * FROM T_動物マスタ WHERE 名前 = 'アンドリューサルクス' |
しかし、初期状態のT_動物マスタには「アンドリューサルクス」という動物は登録されていないのでこのSQL文は実際には何も返しません。このようにしてレコードが存在するかしないかを確認するためにWHERE句を使用することもよくあります。
WHERE句の中では = のほかに < や >, <= , >=, LIKEが使用できます。これらの記号の意味はVBと同じですので特に説明する必要はないでしょうけれどもLIKEは重要な演算子ですので説明しておきます。
LIKEを使うといわゆるパターンマッチングが行えます。たとえば、次のSQL文は名前が「ア」から始まる動物の一覧を取得します。
SELECT * FROM T_動物マスタ WHERE 名前 LIKE 'ア%' |
次のSQL文は名前が「ア」から「オ」のどれかで始まる動物の一覧を取得します。
SELECT * FROM T_動物マスタ WHERE 名前 LIKE '[ア-オ]%' |
LIKE演算子の詳細は各データベース製品のヘルプを見てください。しかし、LIKE演算子で最もよく使うのは「%」ですので詳しいことを知らなくてもそれほど困りません。
WHERE句の中ではSELECT句と同様に関数を使うことができます。
次の例では名前が5文字である動物の一覧を取得します。
SELECT * FROM T_動物マスタ WHERE Len(名前) = 5 |
複数の条件を記述することも可能です。たとえば、次のSQL文は名前が「オオカミ」または「ウルフ」である動物の一覧を取得します。
SELECT * FROM T_動物マスタ WHERE 名前 = 'オオカミ' OR 名前 = 'ウルフ' |
ORの他にANDを使用することもできます。ORとANDの区別は中級レベルのプログラマでも時々間違えてしまうようです。というのは上記の例では「オオカミまたはウルフを取得する」と考えればORで間違いありませんが、「オオカミとウルフを取得する」と考えると日本語にひきずられてANDと思ってしまう場合があるからです。望んだ結果が得られない場合は本当にORでよいのかANDでよいのか確認してみてください。
WHERE句で範囲を指定するには=、>、<を組み合わせればよいのですが、これらとは別に範囲指定のためにBETWEEN ANDという演算子も用意されています。
次のSQL文は目IDが5〜8のレコードを取得します。
SELECT * FROM T_動物マスタ WHERE 目ID >= 5 AND 目ID <= 8 |
このSQL文はBETWEEN ANDを使って次のように書くこともできます。
SELECT * FROM T_動物マスタ WHERE 目ID BETWEEN 5 AND 8 |
どちらを使ってもよいのですが、私はBETWEENを使わない方が好みです。BETWEENに続けて記述するANDと条件を追加するときのANDが紛らわしいからです。
ところで、時々勘違いをされる方がいらっしゃるので特に書いておきますがWHERE句で指定可能な列はFROM句で指定しているテーブルの中から自由に選べます。SELECT句にない列名でもWHERE句に書くことができます。
たとえば、次のSQL文は目IDが1のレコードの動物の名前を一覧で取得します。
SELECT 名前 FROM T_動物マスタ WHERE 目ID = 1 |
このように目IDはSELECT句では指定されていませんが、T_動物マスタに属する列ですのでSELECT句とは関係なくWHERE句で指定することができます。
最後にNULLの取り扱いについて説明します。NULLとは値が入っていない状態を示す言葉です。スペースや長さが0の文字とは異なります。
T_動物マスタでは初期状態で画像列には一切値がセットされていないので画像列はすべてのレコードでNULLです。
次のSQL文は画像列がNULLのレコードを取得します。
SELECT * FROM T_動物マスタ WHERE 画像 IS NULL |
このようにNULLであるかとうかはIS演算子によってのみ判別可能です。ためしにWHERE 画像 = NULLと書くと正常に動作しないことを確認できます。
NULLではないレコードを取得するにはIS NOTを使用します。次のようになります。
SELECT * FROM T_動物マスタ WHERE 画像 IS NOT NULL |
メモ - 小技 必ず0レコード取得する WHERE句に絶対にあり得ない条件を指定して必ず0レコードを取得することができます。0レコード取得すると空のテーブル構造だけを取得できることになります。 たとえば、次のSQL文は必ず0レコードを返します。
なお、SELECT TOP 0はエラーになります。 |
ORDER BY句では取得したレコードの一覧の並び順を指定できます。ORDER BY句には並び替えの基準になる列名などを指定します。
次のSQL文はT_動物マスタの全レコードを名前順に並べて取得します。
SELECT * FROM T_動物マスタ ORDER BY 名前 |
次のSQL文はT_動物マスタの全レコードを目ID順に並べて取得します。
SELECT * FROM T_動物マスタ ORDER BY 目ID |
T_動物マスタには同じ目IDを持つレコードが複数存在しますが、同じ目IDのレコード同士の並び順はこのSQL文では特に指定されていないので不定です。
同じ目IDのレコード同士の並び順を指定するにはORDER BY句でカンマで区切って次の基準となる項目を指定します。
次のSQL文はT_動物マスタの全レコードを目ID順に並べて取得します。同じ目IDのものは名前順に並べて取得します。
SELECT * FROM T_動物マスタ ORDER BY 目ID, 名前 |
このようにORDER BY句ではいくつでも列名を指定することができます。
ORDER BY句で指定した列は通常昇順に並びますが、DESCキーワードを指定することで降順に並び替えることもできます。念のために説明しておくと昇順とは1、2、3、…のようにだんだんと値が増えていく並べ方で一般的には「小さい順」と呼びます。降順とは10、9、8、…のようにだんだんと値が減っていく並べ方で一般的には「大きい順」と呼びます。「逆順」と呼ぶ場合もあります。
次のSQL文はT_動物マスタの全レコードを目IDの降順に並べて取得します。
SELECT * FROM T_動物マスタ ORDER BY 目ID DESC |
次のSQL文はT_動物マスタの全レコードを目IDの降順に並べて取得sます。同じ目IDのものは名前の昇順に並べて取得します。
SELECT * FROM T_動物マスタ ORDER BY 目ID DESC, 名前 |
なお、明示的に昇順での並び順を指定するにはキーワードASCを使用します。
ORDER BY句でも関数や式を使用することができます。
次のSQL文は動物名の後ろの1文字の昇順で、動物の名前の一覧を取得します。
SELECT 名前 FROM T_動物マスタ ORDER BY Right(名前, 1) |
メモ - ORDER
BYがない場合の並び順の実際 既に何度も説明していますがORDER BYで並び順を指定しなかった場合には、取得したレコードの並び順は保証されていません。つまり、実行するたびに並び順が違うようでも正常なのです。ですから、並び順が重要な場合には常にORDER BYを指定する必要があるケースがほとんどです。 実際のところORDER BYが指定されていない場合の並び順はデータベース製品によって異なります。 SQL Serverではクラスタードインデックス、つまり主キーの順番で取得できます。これは直観的でわかりやすいですし場合のよっては便利です。Accessの場合には大体の場合は主キーの順番で取得できますが、なにかのタイミングでときどき並び順が変わるようです。物理的なレコードの配置順なのかもしれませんがよくわかりません。 |