SELECT ステートメントを調べる
Transact-SQL (T-SQL) は、ANSI 標準 SQL 言語の方言の 1 つで、Microsoft SQL 製品およびサービスで使用されます。 これは、標準 SQL によく似ています。 ここでは主として、DML ステートメントの中で群を抜いて最も多くのオプションとバリエーションを持つ SELECT ステートメントを取り上げます。
まず、SELECT ステートメントの処理方法を簡単に確認することにしましょう。 SQL Server データベース エンジンでは、SELECT ステートメントが記述された順序どおりに評価され、処理されるわけではありません。
次のクエリがあるとします。
SELECT OrderDate, COUNT(OrderID) AS Orders
FROM Sales.SalesOrder
WHERE Status = 'Shipped'
GROUP BY OrderDate
HAVING COUNT(OrderID) > 1
ORDER BY OrderDate DESC;
このクエリは、それぞれが、取得されるデータに適用する必要がある特定の操作を定義する複数の "句" で構成される SELECT ステートメントで構成されます。 操作の実行時順を調べる前に、このクエリの実行内容を簡単に見てみましょう。ただし、さまざまな句の詳細については、このモジュールでは説明しません。
SELECT 句は、OrderDate 列と OrderID 値の数を返し、この数には、名前 (または "別名") Orders を割り当てます。
SELECT OrderDate, COUNT(OrderID) AS Orders
FROM 句は、クエリの行のソースとなるテーブル (この場合は、Sales.SalesOrder テーブル) を識別します。
FROM Sales.SalesOrder
WHERE 句は結果から行をフィルター処理して、指定された条件 (この場合は、状態が "shipped" の注文) を満たす行のみを保持します。
WHERE Status = 'Shipped'
GROUP BY 句は、フィルター条件を満たす行を受け取り、それらを OrderDate 別にグループ化して、同じ OrderDate を含むすべての行を単一のグループと見なし、各グループを 1 行として返します。
GROUP BY OrderDate
グループが形成された後、HAVING 句により、それ自体の述語に基づいてグループがフィルター処理されます。 結果には、複数の注文がある日付のみが含まれます。
HAVING COUNT(OrderID) > 1
このクエリをプレビューする目的で、最後の句として ORDER BY があります。これは、出力を OrderDate の降順に並べ替えます。
ORDER BY OrderDate DESC;
各句が何を実行するかを確認できたので、SQL Server で実際に評価を行う順序を見てみることにしましょう。
- FROM 句が最初に評価され、ステートメントの残りの部分のソース行が提供されます。 仮想テーブルが作成され、次のステップに渡されます。
- 次に WHERE 句が評価され、述語に一致するソース テーブルの行がフィルター処理されます。 フィルター処理された仮想テーブルは、次のステップに渡されます。
- 次は GROUP BY です。これは、GROUP BY リストで見つかった一意の値に従って仮想テーブルの行を整理します。 グループのリストを含む新しい仮想テーブルが作成され、次のステップに渡されます。 操作フローのこの時点から、他の要素から参照できるのは、GROUP BY リストまたは集計関数の列のみになります。
- 次に HAVING 句が評価され、その述語に基づいてグループ全体が除外されます。 ステップ 3 で作成された仮想テーブルはフィルター処理され、次の手順に渡されます。
- 最後に SELECT 句が実行され、クエリ結果に表示される列が決定されます。 SELECT 句は他のステップの後に評価されるため、そこで作成された列の別名 (この例では Orders) を GROUP BY 句または HAVING 句で使用することはできません。
- ORDER BY 句は最後に実行され、列リストによって決定されたとおりに行を並べ替えます。
この理解をクエリ例に適用すると、上記の SELECT ステートメントの実行時の論理的な順序は次のようになります。
FROM Sales.SalesOrder
WHERE Status = 'Shipped'
GROUP BY OrderDate
HAVING COUNT(OrderID) > 1
SELECT OrderDate, COUNT(OrderID) AS Orders
ORDER BY OrderDate DESC;
記述するすべての SELECT ステートメントで、使用できるすべての句が必要であるとは限りません。 必須の句は SELECT 句のみで、場合によってはこれを単独で使用することもできます。 通常は、クエリ対象のテーブルを識別するための FROM 句も含まれます。 さらに、Transact-SQL には、追加できる句が他にもあります。
既に説明したとおり、T-SQL クエリは、論理的に評価される順序と同じ順序で記述する必要はありません。 実行時の評価順序によって、どの句がどんなデータを利用できるかが決まります。これは、句でアクセスできるのは、既に処理された句から既に利用できるようになった情報のみに限られるためです。 このため、クエリを記述する際には、実際の論理的な処理順を理解することが重要です。
すべての列の選択
SELECT 句は、クエリの結果で返される値を一覧表示するため、多くの場合、SELECT "リスト" と呼ばれます。
SELECT 句の最も単純な形式は、アスタリスク文字 (*) を使用する形式で、これはすべての列を返します。 T-SQL クエリで使用される場合、これは、"スター" と呼ばれます。 SELECT はクイック テストに適していますが、次の理由から、運用環境での作業では使用しないようにしてください。
- テーブルの列を追加または再配置する変更は、クエリ結果に反映されるため、クエリを使用するアプリケーションまたはレポートで予期しない出力が生成される可能性があります。
- ソース テーブルに多数の行が含まれている場合、不要なデータを返すとクエリの速度が低下して、パフォーマンスの問題が発生する可能性があります。
たとえば、次の例では、(架空の) Production.Product テーブルからすべての列を取得します。
SELECT * FROM Production.Product;
このクエリの結果は、テーブルのすべての行のすべての列を含む行セットです。これは次のようになります。
ProductID
名前
ProductNum
Color
StandardCost
ListPrice
サイズ
Weight
ProductCatID
680
HL Road Frame - Black, 58
FR-R92B-58
Black
1059.31
1431.5
58
1016.04
18
706
HL Road Frame - Red, 58
FR-R92R-58
[赤]
1059.31
1431.5
58
1016.04
18
707
Sport-100 Helmet, Red
HL-U509-R
[赤]
13.0863
34.99
35
708
Sport-100 Helmet, Black
HL-U509
Black
13.0863
34.99
35
...
...
...
...
...
...
...
...
...
特定の列の選択
明示的な列リストを使用すると、返される列とその順序を正確に制御できます。 結果の各列には、ヘッダーとして列の名前が含まれます。
たとえば、次のクエリについて考えてみましょう。これも架空の Production.Product テーブルを使用します。
SELECT ProductID, Name, ListPrice, StandardCost
FROM Production.Product;
今度は、結果に、指定された列のみが含まれます。
ProductID
名前
ListPrice
StandardCost
680
HL Road Frame - Black, 58
1431.5
1059.31
706
HL Road Frame - Red, 58
1431.5
1059.31
707
Sport-100 Helmet, Red
34.99
13.0863
708
Sport-100 Helmet, Black
34.99
13.0863
...
...
...
...
式の選択
SELECT 句は、指定されたテーブルに格納されている列を取得するだけでなく、演算子を使用して列と値、または複数の列を組み合わせる計算と操作を実行できます。 計算または操作の結果は、結果に別の列として表示される単一値 (スカラー) の結果である必要があります。
たとえば、次のクエリには 2 つの式が含まれています。
SELECT ProductID,
Name + '(' + ProductNumber + ')',
ListPrice - StandardCost
FROM Production.Product;
このクエリの結果は次のようになります。
ProductID
680
HL Road Frame - Black, 58(FR-R92B-58)
372.19
706
HL Road Frame - Red, 58(FR-R92R-58)
372.19
707
Sport-100 Helmet, Red(HL-U509-R)
21.9037
708
Sport-100 Helmet, Black(HL-U509)
21.9037
...
...
...
これらの結果について注意すべき興味深い点がいくつかあります。
- 2 つの式によって返される列には、列名がありません。 クエリの送信に使用しているツールによっては、欠落している列名が空白の列ヘッダー、リテラル "列名なし" のインジケーター、または column1 のような既定の名前で示される場合があります。 クエリで列名に "別名" を指定する方法については、このセクションで後ほど説明します。
- 最初の式は、+ 演算子を使用して文字列 (文字ベース) 値を連結し、2 番目の式は、- 演算子を使用して 1 つの数値を別の数値から減算します。 + 演算子は、数値と組み合わせて使用すると、加算を実行します。 明らかに、式に含める列の "データ型" を理解することが重要です。 データ型については、次のセクションで説明します。
列の別名
ソースの列名の代わりとして、または式の出力に名前を割り当てるために、SELECT クエリによって返される各列に "別名" を指定できます。
たとえば、次に示すのは前と同じクエリですが、各列に別名が指定されています。
SELECT ProductID AS ID,
Name + '(' + ProductNumber + ')' AS ProductName,
ListPrice - StandardCost AS Markup
FROM Production.Product;
このクエリの結果には、指定された列名が含まれます。
id
ProductName
マークアップ
680
HL Road Frame - Black, 58(FR-R92B-58)
372.19
706
HL Road Frame - Red, 58(FR-R92R-58)
372.19
707
Sport-100 Helmet, Red(HL-U509-R)
21.9037
708
Sport-100 Helmet, Black(HL-U509)
21.9037
...
...
...
Note
別名を指定する場合、AS キーワードは省略できますが、明確にするために含めることをお勧めします。
クエリの書式指定
このセクションの例でお気付きかもしれませんが、クエリ コードの書式は柔軟に設定することができます。 たとえば、各句 (またはクエリ全体) を 1 行に記述したり、複数行に分割したりすることができます。 ほとんどのデータベース システムでは、コードで大文字と小文字が区別されず、T-SQL 言語の一部の要素 (前述の AS キーワードや、ステートメントの最後のセミコロンなど) は省略できます。
T-SQL コードを読みやすくする (したがって、理解とデバッグを容易にする) ために、次のガイドラインを考慮してください。
- SELECT、FROM、AS などの T-SQL キーワードを大文字にします。 大文字のキーワードは、複雑なステートメントの各句を見つけやすくするために一般的に使用される慣例です。
- ステートメントの主要な句ごとに改行します。
- SELECT リストに複数の列、式、または別名が含まれている場合、各列を個別の行に一覧表示することを検討します。
- 各メジャー句にどのコードが属するかを明確にするために、サブ句または列を含む行にはインデントを設定します。