標準クエリ演算子の変換 (LINQ to SQL)
更新 : November 2007
LINQ to SQL では、標準クエリ演算子から SQL コマンドへの変換が行われます。SQL 変換の実行のセマンティクスは、データベースのクエリ プロセッサによって決まります。
標準クエリ演算子はシーケンスに対して定義されています。シーケンスは順序付きで、シーケンスの各要素の参照 ID に基づいています。詳細については、「標準クエリ演算子の概要」を参照してください。
SQL が扱う対象は主に、順序なしの値のセットです。通常、順序付けは、明示的な後処理の操作として、クエリの中間結果ではなく最終結果に対して適用されます。ID は値で定義されます。このため、SQL クエリは、セット (集合) ではなくマルチセット (多重集合) を扱うものとして理解されます。
以下、標準クエリ演算子とその SQL 変換との違いを、LINQ to SQL 用 SQL Server プロバイダの場合について説明します。
演算子のサポート
Concat
Concat<TSource> メソッドは、受信側と引数の順序が同じである、順序付けされたマルチセットに対して定義されます。Concat<TSource> は、共通の順序に従ったマルチセットに対する UNION ALL として機能します。
最後の手順は、結果を生成する前の SQL での順序付けです。Concat<TSource> は引数の順序を維持しません。適切な順序にするには、Concat<TSource> の結果を明示的に順序付けする必要があります。
Intersect、Except、Union
Intersect メソッドと Except メソッドは、セットに対してのみ正しく定義されます。マルチセットのセマンティクスは未定義です。
Union メソッドは、マルチセットの順序なし連結メソッドとして、マルチセットに対して定義されます (事実上、SQL の UNION ALL 句の結果)。
Take、Skip
Take<TSource> メソッドと Skip<TSource> メソッドは、順序付けされたセットに対してのみ正しく定義されます。順序付けされていないセットまたはマルチセットのセマンティクスは未定義です。
メモ : |
---|
Take<TSource> と Skip<TSource> を SQL Server 2000 に対するクエリで使用する場合は、いくつかの制限があります。詳細については、「トラブルシューティング (LINQ to SQL)」の「SQL Server 2000 の Skip 例外と Take 例外」を参照してください。 |
SQL の順序付けの制約により、LINQ to SQL は、これらのメソッドの引数の順序を、メソッドの結果に移動することを試みます。たとえば、次のような LINQ to SQL クエリがあるとします。
Dim custQuery = _
From cust In db.Customers _
Where cust.City = "London" _
Order By cust.CustomerID _
Select cust Skip 1 Take 1
var custQuery =
(from cust in db.Customers
where cust.City == "London"
orderby cust.CustomerID
select cust).Skip(1).Take(1);
このコードに対して生成される SQL では、順序が次のように末尾に移動されます。
SELECT TOP 1 [t0].[CustomerID], [t0].[CompanyName],
FROM [Customers] AS [t0]
WHERE (NOT (EXISTS(
SELECT NULL AS [EMPTY]
FROM (
SELECT TOP 1 [t1].[CustomerID]
FROM [Customers] AS [t1]
WHERE [t1].[City] = @p0
ORDER BY [t1].[CustomerID]
) AS [t2]
WHERE [t0].[CustomerID] = [t2].[CustomerID]
))) AND ([t0].[City] = @p1)
ORDER BY [t0].[CustomerID]
Take<TSource> と Skip<TSource> を連結する場合は、指定されているすべての順序が一致することが当然必要です。それ以外の場合、結果は未定義です。
Take<TSource> と Skip<TSource> はいずれも、標準クエリ演算子の仕様に基づく、負でない定数の整数引数に対して正しく定義されます。
変換されない演算子
LINQ to SQL によって変換されないメソッドを次に示します。最も一般的な理由は、順序なしのマルチセットとシーケンスの違いにあります。
演算子 |
理由 |
---|---|
SQL クエリの操作の対象は、シーケンスではなくマルチセットです。結果に対して適用する最後の句が ORDER BY であることが必要です。このため、これら 2 つのメソッドには、汎用的な変換がありません。 |
|
順序付けされたセットに対しては、このメソッドの変換が可能ですが、現在の LINQ to SQL では変換されません。 |
|
順序付けされたセットに対しては、これらのメソッドの変換が可能ですが、現在の LINQ to SQL では変換されません。 |
|
SQL クエリの操作の対象は、インデックス可能なシーケンスではなくマルチセットです。 |
|
DefaultIfEmpty (既定の引数のオーバーロード) |
一般に、任意の組に対して既定値を指定することはできません。場合によっては、外部結合を通じて、組に対する null 値の使用が可能です。 |
式の変換
null セマンティクス
LINQ to SQL は、null 比較セマンティクスを SQL に強制しません。比較演算子は、対応する SQL の演算子に構文上は変換されます。このため、セマンティクスには、サーバーまたは接続の設定で定義された SQL セマンティクスが反映されます。たとえば、既定の SQL Server 設定では、2 つの null 値は一致しないと見なされますが、この設定を変更することでセマンティクスを変更できます。LINQ to SQL は、クエリを変換するときにサーバーの設定を考慮しません。
リテラルの null による比較は適切な SQL 形式 (is null または is not null) に変換されます。
null の値の照合順序は SQL Server で定義され、LINQ to SQL では変更されません。
集計
標準クエリ演算子の集計メソッド Sum では、空のシーケンスや null のみを含むシーケンスはゼロに評価されます。LINQ to SQL では、SQL のセマンティクスは変更されず、空のシーケンスまたは null のみを含むシーケンスは、Sum による評価はゼロではなく null になります。
LINQ to SQL での集計には、中間結果に対する SQL の制限が適用されます。32 ビットの整数の Sum の計算では、64 ビットの結果は使用されません。LINQ to SQL による Sum の変換では、オーバーフローが発生することがあります。これには、標準クエリ演算子の実装で、対応するメモリ内シーケンスでオーバーフローが発生しないケースも含まれます。
同様に、LINQ to SQL が整数値の Average を変換するときには、double ではなく integer として計算されます。
エンティティ引数
LINQ to SQL では、GroupBy メソッドおよび OrderBy メソッドでエンティティ型を使用できます。これらの演算子の変換では、型の引数を使用している場合、その型のすべてのメンバを指定しているのと同等と見なされます。たとえば、次のコードは同じ意味です。
db.Customers.GroupBy(Function(c) c)
db.Customers.GroupBy(Function(c) New With {c.CustomerID, _
c.ContactName})
db.Customers.GroupBy(c => c);
db.Customers.GroupBy(c => new { c.CustomerID, c.ContactName });
引数の等値性/比較
以下のメソッドの実装では、引数が等値であることが必要です。
LINQ to SQL では、フラットな引数に対して、等値性と比較がサポートされていますが、シーケンスの引数またはシーケンスを含む引数に対してはサポートされていません。フラットな引数とは、SQL の行に対応付けられる型のものです。シーケンスを含まないと静的に決定できる 1 つまたは複数のエンティティ型の射影は、フラットな引数と見なされます。
フラットな引数の例を次に示します。
db.Customers.Select(Function(c) c)
db.Customers.Select(Function(c) New With {c.CustomerID, c.City})
db.Orders.Select(Function(o) New With {o.OrderID, o.Customer.City})
db.Orders.Select(Function(o) New With {o.OrderID, o.Customer})
db.Customers.Select(c => c);
db.Customers.Select(c => new { c.CustomerID, c.City });
db.Orders.Select(o => new { o.OrderID, o.Customer.City });
db.Orders.Select(o => new { o.OrderID, o.Customer });
フラットでない (階層構造の) 引数の例を次に示します。
' In the following line, c.Orders is a sequence.
db.Customers.Select(Function(c) New With {c.CustomerID, c.Orders})
' In the following line, the result has a sequence.
db.Customers.GroupBy(Function(c) c.City)
// In the following line, c.Orders is a sequence.
db.Customers.Select(c => new { c.CustomerID, c.Orders });
// In the following line, the result has a sequence.
db.Customers.GroupBy(c => c.City);
Visual Basic の関数の変換
Visual Basic のコンパイラが使用する以下のヘルパー関数は、対応する SQL 演算子および関数に変換されます。
CompareString
DateTime.Compare
Decimal.Compare
IIf (in Microsoft.VisualBasic.Interaction)
変換メソッド :
ToBoolean |
ToSByte |
ToByte |
ToChar |
ToCharArrayRankOne |
ToDate |
ToDecimal |
ToDouble |
ToInteger |
ToUInteger |
ToLong |
ToULong |
ToShort |
ToUShort |
ToSingle |
ToString |
継承のサポート
継承の対応付けの制限
詳細については、「方法 : 継承階層を割り当てる (LINQ to SQL)」を参照してください。
クエリでの継承
C# のキャストは射影でのみサポートされます。他の場所で使用したキャストは変換されず、無視されます。SQL 関数名を除き、SQL が実行するのは、共通言語ランタイム (CLR: Common Language Runtime) の Convert に相当する処理のみです。つまり、SQL は、ある型の値を別の型に変更することはできます。CLR のキャストに相当するものはありません。同じビット列を別の型として再解釈するという概念がないためです。したがって、C# のキャストはローカルでのみ機能します。リモート処理はされません。
演算子 is と as、および GetType メソッドは、Select 演算子に限定されません。他のクエリ演算子でも使用できます。
SQL Server 2008 のサポート
.NET Framework 3.5 SP1 以降、LINQ to SQL は SQL Server 2008 で導入された新しい日付/時刻型へのマッピングをサポートします。ただし、これらの新しい型にマッピングされた値を操作するときに使用できる LINQ to SQL のクエリ演算子にはいくつか制限があります。
サポートされていないクエリ演算子
DATETIME2、DATE、TIME、および DATETIMEOFFSET は、SQL Server の新しい日付/時刻型にマッピングされた値ではサポートされていません。
Aggregate
Average
LastOrDefault
OfType
Sum
SQL Server の日付/時刻型へのマッピングの詳細については、「SQL と CLR の型マッピング (LINQ to SQL)」を参照してください。
SQL Server 2005 のサポート
LINQ to SQL は、SQL Server 2005 の以下の機能をサポートしていません。
SQL CLR 用に作成されたストアド プロシージャ。
ユーザー定義型。
XML クエリ機能。
SQL Server 2000 のサポート
Microsoft SQL Server 2005 と比較した場合の SQL Server 2000 の以下の制限事項は、LINQ to SQL のサポートに影響します。
Cross Apply 演算子および Outer Apply 演算子
これらの演算子は SQL Server 2000 では使用できません。LINQ to SQL は、一連の書き換えを行って、これらの演算子を適切な結合に置換します。
Cross Apply および Outer Apply は、リレーションシップ ナビゲーションに対してのみ生成されます。どのようなクエリのセットに対してこのような書き換えが可能かは、正しく定義されていません。このため、SQL Server 2000 でサポートされている最小限のクエリのセットは、リレーションシップ ナビゲーションを含まないクエリのセットです。
text / ntext
データ型 text / ntext は、Microsoft SQL Server 2005 でサポートされている、varchar(max) / nvarchar(max) に対する特定のクエリ操作では使用できません。
この制限事項には、対処方法はありません。具体的には、text 列または ntext 列に割り当てられているメンバを含む結果に対して、Distinct() を使用することはできません。
入れ子になったクエリによってトリガされる動作
SQL Server 2000 (SP4 まで) のバインダには、入れ子になったクエリによってトリガされる特異な動作があります。この特異動作をトリガする SQL クエリのセットは、正しく定義されていません。このため、SQL Server の例外を発生させる可能性がある LINQ to SQL クエリのセットは定義することができません。
Skip 演算子および Take 演算子
Take<TSource> と Skip<TSource> には、SQL Server 2000 に対するクエリで使用する場合に特定の制限があります。詳細については、「トラブルシューティング (LINQ to SQL)」の「SQL Server 2000 の Skip 例外と Take 例外」を参照してください。
オブジェクトの実体化
実体化とは、1 つまたは複数の SQL クエリで返された行から CLR オブジェクトを作成することです。
以下の呼び出しは、実体化の中でローカルで実行されます。
コンストラクタ
射影での ToString メソッド
射影での型キャスト
AsEnumerable<TSource> メソッドに続くメソッドはローカルで実行されます。このメソッドでは即時実行は行われません。
struct は、クエリ結果の戻り値の型として、または結果の型のメンバとして使用できます。エンティティはクラスである必要があります。匿名型はクラス インスタンスとして実体化されますが、射影では名前付き構造体 (エンティティ以外) を使用できます。
クエリ結果の戻り値の型のメンバには、IQueryable<T> 型を使用できます。これはローカル コレクションとして実体化されます。
以下のメソッドでは、メソッドの対象シーケンスの即時実体化が発生します。
参照
処理手順
方法 : シーケンスの要素を返すまたはスキップする (LINQ to SQL)
方法 : 2 つのシーケンスを連結する (LINQ to SQL)
方法 : 2 つのシーケンスの差集合を返す (LINQ to SQL)
方法 : 2 つのシーケンスの積集合を返す (LINQ to SQL)
方法 : 2 つのシーケンスの和集合を返す (LINQ to SQL)