標準クエリ演算子の変換

LINQ to SQL では、標準クエリ演算子から SQL コマンドへの変換が行われます。 SQL 変換の実行のセマンティクスは、データベースのクエリ プロセッサによって決まります。

標準クエリ演算子は "シーケンス" に対して定義されています。 シーケンスは "順序付き" で、シーケンスの各要素の参照 ID に基づいています。 詳しくは、「標準クエリ演算子の概要 (C#)」または「標準クエリ演算子の概要 (Visual Basic)」を参照してください。

SQL で扱われるのは主に、"順序なしの値のセット" です。 通常、順序付けは、明示的な後処理の操作として、クエリの中間結果ではなく最終結果に対して適用されます。 ID は値で定義されます。 このため、SQL クエリは、"セット" ではなくマルチセット ("バッグ") を扱うものとして理解されます。

以下、標準クエリ演算子とその SQL 変換との違いを、LINQ to SQL 用 SQL Server プロバイダーの場合について説明します。

演算子のサポート

Concat

Concat メソッドは、受信側と引数の順序が同じである、順序付けされたマルチセットに対して定義されます。 Concat は、共通の順序に従ったマルチセットに対する UNION ALL として機能します。

最後の手順は、結果を生成する前の SQL での順序付けです。 Concat は、引数の順序を維持しません。 適切な順序にするには、Concat の結果を明示的に順序付けする必要があります。

Intersect、Except、Union

Intersect メソッドと Except メソッドは、セットに対してのみ正しく定義されます。 マルチセットのセマンティクスは未定義です。

Union メソッドは、マルチセットの順序なし連結メソッドとして、マルチセットに対して定義されます (事実上、SQL の UNION ALL 句の結果)。

Take、Skip

Take メソッドと Skip メソッドは、"順序付けされたセット" に対してのみ正しく定義されています。 順序付けされていないセットまたはマルチセットのセマンティクスは未定義です。

Note

TakeSkip を SQL Server 2000 に対するクエリで使用する場合は、いくつかの制限があります。 詳しくは、「トラブルシューティング」の「SQL Server 2000 の Skip 例外と Take 例外」をご覧ください。

SQL での順序付けに対する制限のため、LINQ to SQL では、これらのメソッドの引数の順序を、メソッドの結果に移動することが試みられます。 たとえば、次のような LINQ to SQL クエリがあるとします。

var custQuery =
    (from cust in db.Customers
    where cust.City == "London"
    orderby cust.CustomerID
    select cust).Skip(1).Take(1);
Dim custQuery = _
    From cust In db.Customers _
    Where cust.City = "London" _
    Order By 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]

TakeSkip を連結する場合は、指定されているすべての順序が一致することが当然必要です。 それ以外の場合、結果は未定義です。

TakeSkip はいずれも、標準クエリ演算子の仕様に基づく、負でない定数の整数引数に対して正しく定義されます。

変換されない演算子

LINQ to SQL によって変換されないメソッドを次に示します。 最も一般的な理由は、順序なしのマルチセットとシーケンスの違いにあります。

演算子 理由
TakeWhileSkipWhile SQL クエリの操作の対象は、シーケンスではなく、マルチセットです。 結果に対して適用する最後の句が ORDER BY であることが必要です。 このため、これら 2 つのメソッドには、汎用的な変換がありません。
Reverse 順序付けされたセットに対しては、このメソッドの変換が可能ですが、現在の LINQ to SQL では変換されません。
LastLastOrDefault 順序付けされたセットに対しては、これらのメソッドの変換が可能ですが、現在の LINQ to SQL では変換されません。
ElementAtElementAtOrDefault 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(c => c);
db.Customers.GroupBy(c => new { c.CustomerID, c.ContactName });
db.Customers.GroupBy(Function(c) c)
db.Customers.GroupBy(Function(c) New With {c.CustomerID, _
    c.ContactName})

引数の等値性/比較

以下のメソッドの実装では、引数が等値であることが必要です。

LINQ to SQL では、"フラット" な引数に対して、等値性と比較がサポートされていますが、シーケンスの引数またはシーケンスを含む引数に対してはサポートされていません。 フラットな引数とは、SQL の行に対応付けられる型のものです。 シーケンスを含まないと静的に決定できる 1 つまたは複数のエンティティ型の射影は、フラットな引数と見なされます。

フラットな引数の例を次に示します。

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 });	
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})

フラットでない (階層構造の) 引数の例を次に示します。

// 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);
' 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)

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

継承のサポート

継承の対応付けの制限

詳細については、「方法: 継承階層を割り当てる」を参照してください。

クエリでの継承

C# のキャストは射影でのみサポートされます。 他の場所で使用したキャストは変換されず、無視されます。 SQL 関数名を除き、SQL が実行するのは、共通言語ランタイム (CLR: Common Language Runtime) の Convert に相当する処理のみです。 つまり、SQL は、ある型の値を別の型に変更することはできます。 CLR のキャストに相当するものはありません。同じビット列を別の型として再解釈するという概念がないためです。 したがって、C# のキャストはローカルでのみ機能します。 リモート処理はされません。

演算子 isas、および GetType メソッドは、Select 演算子に限定されません。 他のクエリ演算子でも使用できます。

SQL Server 2008 のサポート

.NET Framework 3.5 SP1 以降、LINQ to SQL は SQL Server 2008 で導入された新しい日付/時刻型へのマッピングをサポートします。 ただし、これらの新しい型にマッピングされた値を操作するときに使用できる LINQ to SQL のクエリ演算子にはいくつか制限があります。

サポートされていないクエリ演算子

DATETIME2DATETIME、および DATETIMEOFFSET は、SQL Server の新しい日付/時刻型にマッピングされた値ではサポートされていません。

  • Aggregate

  • Average

  • LastOrDefault

  • OfType

  • Sum

SQL Server の日付/時刻型へのマッピングについて詳しくは、「SQL と CLR の型マッピング」をご覧ください。

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) に対する特定のクエリ操作では使用できません。

この制限事項には、対処方法はありません。 具体的には、Distinct() 列または text 列に割り当てられているメンバーを含む結果に対して、ntext を使用することはできません。

入れ子になったクエリによってトリガーされる動作

SQL Server 2000 (SP4 まで) のバインダーには、入れ子になったクエリによってトリガーされる特異な動作があります。 この特異動作をトリガーする SQL クエリのセットは、正しく定義されていません。 このため、SQL Server の例外を発生させる可能性がある LINQ to SQL クエリのセットは定義することができません。

Skip 演算子および Take 演算子

TakeSkip を SQL Server 2000 に対するクエリで使用する場合は、いくつかの制限があります。 詳しくは、「トラブルシューティング」の「SQL Server 2000 の Skip 例外と Take 例外」をご覧ください。

オブジェクトの具体化

実体化とは、1 つまたは複数の SQL クエリで返された行から CLR オブジェクトを作成することです。

  • 以下の呼び出しは、実体化の一部として "ローカルで実行" されます。

    • コンストラクター

    • 射影での ToString メソッド

    • 射影での型キャスト

  • AsEnumerable メソッドに続くメソッドは "ローカルで実行" されます。 このメソッドでは即時実行は行われません。

  • struct は、クエリ結果の戻り値の型として、または結果の型のメンバーとして使用できます。 エンティティはクラスである必要があります。 匿名型はクラス インスタンスとして実体化されますが、射影では名前付き構造体 (エンティティ以外) を使用できます。

  • クエリ結果の戻り値の型のメンバーには、IQueryable<T> 型を使用できます。 これはローカル コレクションとして実体化されます。

  • 以下のメソッドでは、メソッドが適用されるシーケンスが "すぐに実体化" されます。

関連項目