この記事は、T-SQLのテーブル式の基礎に関するシリーズの最初の記事です。主に、T-SQLでは派生テーブル、共通テーブル式(CTE)、ビュー、およびインラインテーブル値関数(インラインTVF)として知られている4種類の名前付きテーブル式に焦点を当てます。
私は、長年知っている親友のグラント・フリッチーからこのシリーズを書くように促されました。 Grantが繰り返し指摘しているように、T-SQLで一般的なテーブル式を使用する多くの人は、SQL Serverが内部クエリ結果セットを保持していると考えており、この信念の理由はテーブルという用語の使用にあると考えています。 コンストラクトの名前で。このトピックがコミュニティのディスカッションで取り上げられると、多くの場合、構成の名前にテーブルという用語を使用することは、実際にはテーブルではないため不適切であると主張します。少なくともT-SQLでは、この構成の将来の名前の変更を期待して、名前付けキャンペーンを開始する提案もあります。いくつかの提案には、クエリ式が含まれます 、インラインビュー 、ステートメントレベルのビュー 、 その他。
おそらくこれは一部の人にとっては驚きですが、実際にはテーブルという用語が使用されています。 非常に適切な共通テーブル式で。実際、私はテーブル式という用語の使用を見つけました 適切に。私にとって、CTEがT-SQLで何であるかを説明する最良の方法は、名前付きテーブル式です。 。同じことが、T-SQLが派生テーブル(一般的な考え方とは対照的な特定の言語構造)、ビュー、およびインラインTVFと呼ぶものにも当てはまります。それらはすべて名前付きテーブル式です。
少しご容赦いただければ、この記事で私の見解の理由を説明します。名前の混乱と、テーブル式に永続性の側面があるかどうかに関する混乱の両方が、リレーショナルデータベース管理システムの分野の基本をよりよく理解することで解決できることに気づきました。これらの基本は、関係理論、SQL(標準言語)との関係、およびSQLServerとAzureSQLDatabaseの実装で使用されるT-SQL方言の両方との関係です。
出発点として、次の質問に答えられるようにする必要があります。
- 物理データの独立性とは何ですか リレーショナルモデルの原則はどういう意味ですか?
- SQLのテーブルとは何ですか?リレーショナルモデルの対応するテーブルは何ですか?
- 関係代数の閉包性とは何ですか?
- テーブル式とは何ですか?リレーショナルモデルの対応するものは何ですか?
上記の質問に正しく答えることができれば、名前付きテーブル式という用語が使用されていることに気付くでしょう。 前述の構成(T-SQLが派生テーブル、CTE、ビュー、インラインTVFと呼ぶもの)に応じて
関係理論を深く理解しているように聞こえたくありません。私の専門はT-SQLです。私は、関係理論について私が知っているよりもはるかに多くのことを知っていること、そして私が知っていると思ういくつかのことはそうではないことを認めます。このトピックに関するC.J.Datesの著作を読んだとき、私は知っていることの表面をかろうじてかじっただけであり、それをよりよく理解するために努力することができ、またそうすべきであると感じています。私は、関係理論をよく理解することが、SQLとT-SQLをよりよく理解し、より良く、より正確で、より堅牢なT-SQLコードを作成することに直接つながることを認識し、固く信じています。キャリアとしてデータを選択した人には、SQLとリレーショナル理論:C。J. Dateによる正確なSQLコード第3版の書き方(O'Reilly 2015)を読むことをお勧めします。
このシリーズの最初のパートでは、テーブル式という用語の使用法について理解を深めたいと思います。 および名前付きテーブル式 、これはDateによるこの用語の使用と一致していますが、残念ながらSQLStandardによるこの用語の使用とは一致していません。これを実現するために、リレーショナル理論とSQL標準の背景を少し説明します。しかし、私が言ったように、このトピックの真に詳細な報道については、Dateの本を読むことをお勧めします。
まず、物理データの独立性の原則の意味を説明します。次に、SQLのテーブルと、リレーショナル理論の対応するテーブルについて説明します。次に、関係代数の閉包性が何を意味するのかを説明します。テーブルとは何か、およびクロージャープロパティが何を意味するかを合理的に理解すると、テーブル式が何であるかを理解するのは非常に簡単になります。次に、T-SQLの詳細に焦点を当てます。 T-SQLのテーブル式の基本については、概念的な処理と、物理的な表現やクエリの調整に関する考慮事項などの実装の詳細の両方の観点から、多くのことを言う必要があります。
実装の詳細を詳しく調べてみると、このトピックは魅力的で非常に実用的だと思います。実際、私はそれについて多くのことを言うことがあるので、このシリーズが最終的にいくつの部分を必要とするかはわかりません。私が自信を持って言えることは、複数の部分があるということです。おそらく1より多く100未満です。今後のパートでは、名前付きテーブル式の個々のタイプ、変更に関する考慮事項、インライン化の側面、順序付けの側面、相関関係などについて詳しく説明します。
私の例では、TSQLV5というサンプルデータベースを使用します。このデータベースを作成してデータを取り込むスクリプトはここにあり、そのER図はここにあります。
物理データの独立性
物理データの独立性は、リレーショナルデータベース管理システムに対してクエリを送信するユーザーから物理的な実装の詳細を隠すか、透過的にする必要があるというリレーショナル理論の原則です。クエリでは、ユーザーは何に焦点を当てることになっています 方法ではなく、関係代数に基づく論理演算を使用する必要があります。 データを取得します。データがどのように構造化され、アクセスされ、処理されるかについて心配する必要はありません。このような物理的な実装の詳細は、異なる実装(RDBMS製品)間で大幅に異なる傾向があります。同じRDBMSを使用している場合でも、物理的な実装の詳細は、バージョンやビルドによって異なる場合があります。理論的には、物理データの独立性の原則の背後にある考え方は、RDBMSを新しいバージョンにアップグレードするとき、またはあるRDBMSから別のRDBMSに移行するときにも、ソリューションを改訂する必要をなくすことで、ユーザーの投資を保護することです。ご存知かもしれませんが、実際にはそれほど単純ではありませんが、それは別の議論のトピックです。
テーブルとは何ですか?
T-SQLまたはその他のSQLの方言をしばらく使用している場合は、テーブルが何であるかを直感的に理解できます。問題は、関係理論の背景がないと、直感的な理解があまり正確でないことが多いということです。典型的な間違いの1つは、物理的な実装の詳細に直感的に集中する傾向があることです。たとえば、テーブルとは何かを考えている場合、テーブルを論理構造(行のセット)として考えているのでしょうか、それとも(SQL Serverで)使用しているプラットフォームでの物理的な実装の詳細を考えているのでしょうか。 、ページ、エクステント、ヒープとクラスター化インデックス、非クラスター化インデックスなど)?物理データの独立性の原則に従って、テーブルをクエリするSQLコードを作成するユーザーは、テーブルを論理構造と見なし、RDBMSに物理的な実装の詳細について心配させることになっています。それでは、一歩下がって、テーブルとは何かを理解してみましょう。
テーブルは、関係理論の主要な構造である関係に対応するSQLの対応物です。物事を単純に保ち、対象範囲を制限するために、リレーション変数とリレーション値の区別については説明しません。私の推薦に従い、Dateの本を読むと、そのような微妙な点をすぐにはっきりと理解できます。
リレーションには見出しと本文があります。
リレーションの見出しはセットです。 属性の 。数学的集合論では、集合には順序も重複もありません。属性は、位置ではなく名前で識別する必要があります。したがって、属性名は一意である必要があります。
SQLの属性に対応するものを特定できますか?あなたはおそらくそれが列だと推測しているでしょう 。ただし、SQLには、実際には、CREATETABLEステートメントでの出現順序に基づいた列の順序の概念があります。たとえば、TSQLV5データベースのSales.ShippersテーブルのCREATETABLEステートメントは次のとおりです。
CREATE TABLE Sales.Shippers ( shipperid INT NOT NULL IDENTITY, companyname NVARCHAR(40) NOT NULL, phone NVARCHAR(24) NOT NULL, CONSTRAINT PK_Shippers PRIMARY KEY(shipperid) );
悪名高いSELECT *
を使用してテーブルをクエリします 、そのように:
SELECT * FROM Sales.Shippers;
システムでこのクエリを実行すると、次の出力が得られました。
shipperid companyname phone ---------- -------------- --------------- 1 Shipper GVSUA (503) 555-0137 2 Shipper ETYNR (425) 555-0136 3 Shipper ZHISN (415) 555-0138
SQLは、定義の順序に基づいて列が左から右に返されることを保証します。行で何が起こるかについては、すぐに説明します。 SQLでは、ORDER BY句のSELECTリストから列の序数位置を参照することもできます(この方法を推奨しているわけではなく、Aaron Bertrandも推奨していません):
SELECT shipperid, companyname, phone FROM Sales.Shippers ORDER BY 2;
このクエリは次の出力を生成します:
shipperid companyname phone ---------- -------------- --------------- 2 Shipper ETYNR (425) 555-0136 1 Shipper GVSUA (503) 555-0137 3 Shipper ZHISN (415) 555-0138
リレーションの本体は、タプルのセットです。 。繰り返しになりますが、セットには順序や重複がないことを思い出してください。したがって、リレーションには、タプルを一意に識別できるようにする候補キーが少なくとも1つ必要です。タプルに対応するSQLは行です。 。ただし、SQLでは、テーブルにキーを定義する必要はありません。定義しないと、行が重複する可能性があります。テーブルにキーが定義されている場合でも、テーブルに対するクエリから重複した行が返される可能性があります。次に例を示します:
SELECT country FROM HR.Employees;
このクエリは次の出力を生成します:
country -------- USA USA USA USA UK UK UK USA UK
行が重複する可能性があるため、このクエリはリレーショナル結果を生成しません。関係理論は集合論に基づいていますが、SQLは多重集合理論に基づいています。マルチセット(別名スーパーセットまたはバッグ)は重複する可能性があります。 SQLは、次のように、DISTINCT句を使用して重複を排除するツールを提供します。
SELECT DISTINCT country FROM HR.Employees;
このクエリは次の出力を生成します:
country -------- UK USA
SQLがテーブルの本体に関して関係理論から維持しているのは、順序なしのプロパティです。クエリにORDERBY句を追加しない限り、結果が行間で特定の順序になるという保証はありません。したがって、上記のクエリ結果の本文は、少なくとも重複がなく、順序が保証されていないという意味で、リレーショナルです。
SQL Serverでテーブルをクエリし、クエリにORDERBY句を含めないとします。 SQL Serverは、保証された動作として常に特定の順序で行を返すことを期待していますか?多くの人がそうします。多くの人は、クラスター化されたインデックスの順序に基づいて常に行を取り戻すと考えています。これは、物理データの独立性の原則を無視し、直感に基づいて、おそらく過去に観察された行動に基づいて仮定を立てる良い例です。 Microsoftは、ORDER BY句のないSQLクエリは結果行間の順序を保証しないことを知っています。したがって、物理レベルでデータがインデックス構造にある場合でも、SQLServerはインデックス内のデータを処理する必要はありません。注文。特定の物理的条件下ではそうすることを選択するかもしれませんが、他の物理的条件下ではそうしないことを選択するかもしれません。また、物理的な実装の詳細は、製品のバージョンやビルドによって異なる可能性があることを思い出してください。クエリが特定の順序で結果行を返すことを保証する場合、これを保証する唯一の方法は、最も外側のクエリにORDERBY句を導入することです。
ご存知かもしれませんが、SQLの設計者は、SQLをリレーショナル理論に従うことを優先するものとは考えていませんでした。そして、ここで説明したのはほんの一例です。もっとたくさんあります。前述のように、この記事での私の目標は、将来の記事でT-SQLの詳細を掘り下げる前に、テーブル式を取り巻く混乱を解消するために十分な批判的理論的背景を提供することです。
テーブル式とは何ですか?
関係代数(関係理論で関係の演算を定義する代数)には、クロージャーがあります。 財産。これが意味するのは、リレーションに対する操作がリレーションを生成するということです。関係演算子は、1つ以上の関係を入力として操作し、単一の関係を出力として生成します。クロージャプロパティを使用すると、操作をネストできます。 関係表現 リレーションを操作してリレーションを返す式です。したがって、関係代数が関係を期待する場合は、関係式を使用できます。
考えてみれば、整数の結果を生成する整数の演算と同じです。変数@iが整数変数であると仮定します。式@i+42は整数を生成するため、(@ i + 42)* 2のように、整数が期待される場所で使用できます。
SQLのテーブルが関係理論の関係の対応物であるとすると、それではあまり成功していませんが、SQLのテーブル式は関係式の対応物です。前述のように、私はC.J.Datesによるこの用語の使用に続いて用語テーブル式を使用します。 SQL標準には多くの紛らわしい用語がありますが、そのうちのいくつかはあまり適切ではないのではないかと思います。たとえば、SQL標準では、テーブル式という用語を使用して、必須のFROM句で始まり、オプションでWHERE、GROUP BY、HAVING、およびWINDOW句を含むクエリ句に基づく式を具体的に記述します(最後はTではサポートされていません)。 -SQL)、およびSELECT句を除外します。標準の仕様は次のとおりです。
7.4<テーブル式>
機能
テーブルまたはグループ化されたテーブルを指定します。
フォーマット
標準でテーブル式と呼ばれる結果がテーブルと見なされることは事実ですが、そのような式をスタンドアロンクエリとして使用することはできません。用語テーブル式の日付バージョンは、実際にはSQL標準で呼び出されるクエリ式に近いものです。 。クエリ式と呼ばれる標準の仕様は次のとおりです。
7.17<クエリ式>
機能
フォーマット
7.3<テーブル値コンストラクター>
機能
フォーマット
この仕様には、T-SQLが共通テーブル式と呼ぶものが含まれていることに注意してください。ただし、標準ではこの用語は実際には使用されておらず、リスト要素を使用して呼び出すだけです。 。また、いわゆるクエリ式はクエリに基づく必要はなく、テーブル値コンストラクタと呼ばれるものに基づくことができることにも注意してください。 (行のセットを構築するためのVALUES句の使用)。最後に、標準のクエリ式は式に基づいていますが、テーブルを返し、テーブルが通常期待される場所で使用できます。これらの理由から、Dateによるテーブル式という用語の使用がはるかに適切であると思います。
ネーミングと用語にこだわる人が、少し辛辣で、おそらく時間の無駄でさえあると感じる人がいる理由がわかります。しかし、私は非常に異なった感じがします。どの分野でも、適切な名前と用語を使用したいという願望は、基礎をよく研究し、知識を反映することを余儀なくされると私は信じています。この記事で、シリーズの次のパートに進みたくないほどあなたを遠ざけることができなかったことを願って、来月の記事から始めて、さまざまな種類の名前の付いた方法に焦点を当てます。テーブル式は、SQLServerおよびAzureSQLデータベースでT-SQLを使用して処理されます。
<テーブル式>::=
[
[
[
[<ウィンドウ句>]
テーブルを指定します。
<クエリ式>::=
[
[
WITH [RECURSIVE]
<リスト要素付き>::=
<クエリ名>[<左パレン><列リスト付き><右パレン>]
AS<テーブルサブクエリ>[<検索またはサイクル句>]
<列リスト付き>::=
<列名リスト>
<クエリ式本体>::=
<クエリ用語>
| <クエリ式本体>UNION[ALL | DISTINCT]
[<対応する仕様>]<クエリ用語>
| <クエリ式本体>EXCEPT[ALL | DISTINCT]
[<対応する仕様>]<クエリ用語>
<クエリ用語>::=
<クエリプライマリ>
| <クエリ用語>INTERSECT[ALL | DISTINCT]
[<対応する仕様>]<クエリプライマリ>
<クエリプライマリ>::=
<単純なテーブル>
| <左のパレン><クエリ式の本文>
[<順序による句>][<結果のオフセット句>][<最初の句をフェッチ>]
<右のパレン>
<単純なテーブル> ::=
<クエリ仕様>
| <テーブル値コンストラクタ>
| <明示的なテーブル>
<明示的なテーブル>::=
テーブル<テーブルまたはクエリ名>
<対応する仕様>::=
対応する[BY<左の親><対応する列リスト>
<対応する列リスト>::=
<列名リスト>
<句による順序>::=
ORDERBY<ソート仕様リスト>
<結果オフセット句>::=
OFFSET<オフセット行数>{ROW| ROWS}
<最初の句をフェッチ>::=
FETCH {FIRST | NEXT}[<最初の数量を取得>]{ROW|行}{のみ|ネクタイ付き}
<最初の数量を取得>::=
<最初の行数を取得>
| <フェッチの最初のパーセンテージ>
<オフセット行数>::=
<単純な値の指定>
<フェッチの最初の行数>::=
<単純な値の指定>
<最初のパーセンテージを取得>::=
<単純な値の指定>PERCENT
テーブルに作成する<行値式>のセットを指定します。
<テーブル値コンストラクタ>::=
VALUES<行値式リスト>
<行値式リスト>::=
<テーブル行値式>[{
<コンテキストで入力されたテーブル値のコンストラクタ>::=
VALUES<コンテキストで入力された行の値の式リスト>
<コンテキストで入力された行の値の式リスト>: :=
<コンテキストで入力された行の値の式>
[{結論