今回は、問い合わせの統合(UNION)と、NULLに関連するトピックを解説し、SELECT文の最後の回とします。
UNION句を使用すると、これまでに使用してきたSQL文の結果を統合して、1つの結果とすることができます。これは、四半期ごとの売り上げデータを別のテーブルに持っている場合に、集計した結果を1つの表として参照したい場合など、複数の問い合わせ結果のマージを実施したい場合に役立ちます。
今回は、製品ごとに付けられた単価のパターンをすべて洗い出す、というSQL文を作成してみましょう。製品の単価は、製品マスターテーブル(Products)に基本となる単価が保存されています。また、取引ごとに製品の単価を決定しているので、注文の明細テーブル(Order Details)にも、その取引における製品の単価が登録されています。これらの2つのテーブルからの別々の問い合わせを、UNION句を使用して統合します。
ではまず、次のSQL文を実行してみましょう。
【例1】
SELECT ProductName, UnitPrice FROM Products
例1は、Productsテーブルのすべての行を返すSQL文ですね。行数は77行であることが、クエリーアナライザウィンドウの右下に表示されているので確認できます。
続いて、次のSQL文を実行しましょう。
【例2】
SELECT distinct prod.ProductName, ordd.UnitPrice FROM "Order Details"ordd INNER JOIN Products prod ON ordd.ProductID = prod.ProductID
例2では、Order Detailsテーブルから、すべてのProductNameとUnitPriceの組み合わせを返しています。Order DetailsテーブルにはProductNameが保存されていませんので、Productsテーブルと結合して参照をしています。また、同じProductNameとUnitPriceの組み合わせは省くために、distinctキーワードを指定しています。結果の行数は、156行であることが確認できます。
では、これらの結果を、UNION句を使用して統合してみましょう。次のSQL文を実行します。
【例3】
SELECT ProductName, UnitPrice FROM Products UNION SELECT distinct prod.ProductName, ordd.UnitPrice FROM "Order Details"ordd INNER JOIN Products prod ON ordd.ProductID = prod.ProductID
例1のSQL文と、例2のSQL文の間に、「UNION」句を追加しているのが分かりますね。UNION句により、それぞれのSELECT文の結果がマージされ、1つの結果として返されるようになります。
UNION句でマージする複数のSELECT文は、2つの条件を満たしている必要があります。
(1)SELECT句に指定するカラムのデータ型が同じか、もしくは、変換可能であること
(2)SELECT句のカラム数が同じこと
これらの条件が満たされていれば、2つ以上のSELECT文をマージすることも可能です。
ところで、例3の結果の行数を確認すると159行ですね。例1は77行、例2は156行でしたので、単純に足した結果ではないことが分かります。これは、UNION句は標準では、同じ行は結果から省く操作が行われるためです。よって、実は2つ目のSELECT文にdistinctキーワードが含まれなくとも、例3の結果は同一となります。
同じ行も結果に含めたい場合(行数が意味を持つ場合など)は、UNIONにALLキーワードを追加します。
【例4】
SELECT ProductName, UnitPrice FROM Products UNION ALL SELECT distinct prod.ProductName, ordd.UnitPrice FROM "Order Details"ordd INNER JOIN Products prod ON ordd.ProductID = prod.ProductID
結果の行数を確認すると233行になっており、例1の77行と例2の156行を足した行数と等しくなっているのが確認できますね。
結果を並べ替えたい場合には、最後にORDER BY句を指定します。
【例5】
SELECT ProductName, UnitPrice FROM Products UNION SELECT distinct prod.ProductName, ordd.UnitPrice FROM "Order Details"ordd INNER JOIN Products prod ON ordd.ProductID = prod.ProductID ORDER BY ProductName, UnitPrice
結果が並べ替えられたのが確認できます。ORDER BY句は、最初のSELECT文に指定するとエラーとなりますので注意が必要です。
次回以降で必要になってくる概念として「NULL」があります。これまでは「外部結合」の解説の際に一部取り上げました。「NULL」について、もう少し詳しく見てみましょう。
ところで、「NULL」とは何でしょうか? これは、「データがない」もしくは「未定義」であることを表す単語です。例題として、次のSQL文を実行してみましょう。
【例6】
SELECT CustomerID, ContactName, Phone, Fax FROM Customers
顧客マスターであるCustomersテーブルの一部を表示しています。ここで、Fax列をながめてみると、NULLと表示されている行が何行かあるのが確認できますね。これは、NULLというデータを表しているわけではなく、「データがない」もしくは「未登録」であることを表しています。ただし、注意しなくてはいけないのは、本当に文字列として「NULL」と登録されているのか、「未登録」であることの「NULL」であるのかは、SQL文の結果からだと見分けがつかないということです。
NULLは「値ではない」ということには、十分注意する必要があります。例えば、SELECT文の条件にNULLが含まれる列を指定する場合を見てみましょう。次のSQL文を実行します。
【例7】
SELECT CustomerID, ContactName, Phone, Fax FROM Customers WHERE Fax like '(0%'
【例8】
SELECT CustomerID, ContactName, Phone, Fax FROM Customers WHERE Fax not like '(0%'
ここで確認していただきたいのは、結果の行数です。例7は2行、例8は68行ですね。例7のWHERE句の条件式をNOTで否定したのが例8ですので、本来であれば例7の結果行数と例8の結果行数を足すと、Customersの全体の行数になるはずですが、例6をもう一度確認すると、Customersは91行あることが確認できます。つまり、21行分が例7、例8の検索から漏れていることが分かります。
この21行が何かを調べるために、次のSQL文を実行しましょう。
【例9】
SELECT CustomerID, ContactName, Phone, Fax FROM Customers WHERE Fax is Null
例9のSELECT文は、WHERE句に「Fax is Null」と指定して、Fax列がNullの行を検索しています。ここで、「Fax = Null」ではなく「is」ステートメントを使用しているところが重要です。つまり、Nullは値ではないために、通常の検索条件で使用する「=」などのステートメントは使えないということなのです。
結果の行数を確認すると21行で、先ほどの例7と例8とこの例9を合わせると、Customers全体になることが分かります。例7と例8では、「like」ステートメントによる検索結果には、Nullは含まれないため、結果の行数を足し合わせても全体の行数にはならなかったということです。
is Nullは、notで否定することが可能です。次のSQL文を実行してみましょう。
【例10】
SELECT CustomerID, ContactName, Phone, Fax FROM Customers WHERE Fax is not Null
Nullを含む列をOrder By句に指定した場合は、どんな値よりも小さい値としてNullは扱われます。次のSQL文を実行してみましょう。
【例11】
SELECT CustomerID, ContactName, Phone, Fax FROM Customers Order By Fax
Nullを含む列をGroup By句に指定すると、Nullも1つのデータとして集計されます。次のSQL文を実行してみましょう。
【例12】
SELECT Fax FROM Customers Group By Fax
9回にわたって、SELECT文の解説をしてきました。SELECT文には、さらに複雑な使用方法もありますが、よく使うSELECT文は一通り解説をすることができました。次回からは、データの更新(UPDATE)、データの挿入(INSERT)、テーブルの作成(CREATE)の順で、解説をしていきます
Copyright © ITmedia, Inc. All Rights Reserved.