標準查詢運算子轉譯
LINQ to SQL 會將標準查詢運算子轉譯為 SQL 命令。 資料庫的查詢處理器可決定 SQL 轉譯的執行語意。
標準查詢運算子是針對「序列」所定義。 序列是「已排序」,而且依賴序列中每個元素的參考身分識別。 如需詳細資訊,請參閱標準查詢運算子概觀 (C#) 或標準查詢運算子概觀 (Visual Basic)。
SQL 主要處理「未排序的值集合」。 排序通常是一項明確陳述的後置處理作業,會套用至最終查詢結果 (而非中繼結果)。 識別是以值來定義。 因此,了解 SQL 查詢將處理多重集合 (「集合包」) 而非「集合」。
下列段落描述標準查詢運算子和其因為 LINQ to SQL 針對 SQL Server 提供者進行的 SQL 轉譯之間的差異。
運算子支援
Concat
Concat方法在定義上適用於已排序的多重集,也就是說接收器的順序和引數的順序相同。 Concat 的運作原理,是對採用共通順序的多重集執行 UNION ALL
。
最後一步就是在產生結果前於 SQL 中排序。 Concat 不會保留其引數的順序。 若要確保適當的排序,您必須明確排序 Concat 的結果。
Intersect、Except、Union
Intersect 和 Except 方法在定義上僅適用於集合。 尚未定義多重集 (Multiset) 的語意 (Semantics)。
Union 方法在定義上適用於多重集,可對多重集做未排序的串連 (其實就是 SQL 中 UNION ALL 子句的結果)。
Take、Skip
Take 和 Skip 方法只針對「已排序的集合」妥善定義。 未定義適用於未排序集合或多重集的語意 (Semantics)。
注意
Take 和 Skip 在用於對 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]
很明顯地,當 Take 和 Skip 鏈結在一起時,所有指定的排序都必須一致。 否則,會有未定義的結果。
根據標準查詢運算子的規格,Take 和 Skip 在定義上適用於非負數的固定整數引數。
不轉譯的運算子
LINQ to SQL 不會轉譯下列方法。 最常見的原因在於未排序的多重集與序列之間有差異。
操作員 | 解題說明 |
---|---|
TakeWhile、SkipWhile | SQL 查詢可用於多重集而非序列上。 ORDER BY 必須是最後一個對結果套用的子句。 因此,這兩個方法不需要普遍受到轉譯。 |
Reverse | 對於未排序的集合,轉譯這個方法是可行的,但 LINQ to SQL 目前不會將其轉譯。 |
Last、LastOrDefault | 對於未排序的集合,轉譯這些方法是可行的,但 LINQ to SQL 目前不會將其轉譯。 |
ElementAt、ElementAtOrDefault | SQL 查詢是用於多重集,而非可建立索引的序列上。 |
DefaultIfEmpty (以預設引數多載) | 一般而言,如果是任意 Tuple,就不能指定預設值。 在某些情況下,可以透過外部聯結 (Outer Join) 指定 Null 值給 Tuple。 |
運算式轉譯
Null 語意
LINQ to SQL 不會對 SQL 加上 null 比較語意。 比較運算子會轉譯為語法上的 SQL 對等用法。 基於這個原因,語意會反映由伺服器或連線設定所定義的 SQL 語意。 例如,在預設的 SQL Server 設定下,兩個 null 值會視為不相等的值,但您可以變更設定來變更語意。 LINQ to SQL 在轉譯查詢時不會考慮伺服器設定。
與常值 null 的比較會轉譯為適當的 SQL 版本 (is null
或 is not null
)。
定序 (Collation) 中的值 null
是由 SQL Server 所定義。 LINQ to SQL 不會變更定序。
彙總
標準查詢運算子彙總方法 Sum 會將空序列或只包含 null 的序列評估為零。 在 LINQ to SQL 中,SQL 的語意維持不變,而且 Sum 會將空序列或只包含 null 的序列估為 null
而非零。
SQL 對中繼結果的限制會套用至 LINQ to SQL 中的彙總。 32 位元整數量值的 Sum 不會用 64 位元的結果來計算。 即使標準查詢運算子實作並未造成對應的記憶體中序列發生溢位,Sum 進行 LINQ to SQL 轉譯仍可能會發生溢位。
同樣地,整數值的 Average 進行 LINQ to SQL 轉譯時,會以 integer
而非 double
計算。
實體引數
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 資料列的型別。 如果一個或多個實體型別的投影可以透過靜態方式判斷為不含序列,則這個投影即為扁平引數。
下列是扁平引數的範例:
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 編譯器 (Compiler) 所用的下列 Helper 函式會轉譯為對應的 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# 轉換 (Cast)。 其他地方使用的轉換 (Cast) 不會受到轉譯而且會予以忽略。 事實上,除了 SQL 函式名稱之外,SQL 只會執行 Common Language Runtime (CLR) Convert 的對等用法。 也就是說,SQL 可以將某個型別的值變更為另一個值。 因為沒有將相同位元的型別重新解譯為另一個型別的概念,CLR 轉換 (Cast) 沒有對等用法。 這就是 C# 轉換只能在區域執行的原因。 無法遠端處理。
運算子 (is
和 as
) 與 GetType
方法不僅能用於 Select
運算子中, 也可用於其他查詢運算子中。
SQL Server 2008 支援
從 .NET Framework 3.5 SP1 開始,LINQ to SQL 便支援對應至 SQL Server 2008 所導入的新日期和時間型別。 但是,針對對應至這些新型別的值進行作業時,您可以使用的 LINQ to SQL 查詢運算子有一些限制。
不支援的查詢運算子
對應至新 SQL Server 日期和時間型別的值不支援下列查詢運算子:DATETIME2
、DATE
、TIME
和 DATETIMEOFFSET
。
Aggregate
Average
LastOrDefault
OfType
Sum
如需對應至這些 SQL Server 日期和時間類型的詳細資訊,請參閱 SQL-CLR 類型對應。
SQL Server 2005 支援
LINQ to SQL 不支援下列 SQL Server 2005 功能:
針對 SQL CLR 撰寫的預存程序 (Stored Procedure)。
使用者定義型別。
XML 查詢功能。
SQL Server 2000 支援
下列 SQL Server 2000 限制 (相較於 Microsoft SQL Server 2005) 會影響 LINQ to SQL 支援。
Cross Apply 和 Outer Apply 運算子
這些運算子不適用於 SQL Server 2000。 LINQ to SQL 會嘗試進行一連串重寫作業,以將其取代為適當的聯結。
Cross Apply
和 Outer Apply
是為了關聯性 (Relationship) 巡覽而產生的。 會進行這類重寫的查詢集還沒整理出來。 因此,對於 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 運算子
Take 和 Skip 在用於對 SQL Server 2000 進行的查詢中時會有一些限制。 如需詳細資訊,請參閱疑難排解中的<SQL Server 2000 中的 Skip 和 Take 例外狀況>一節。
物件具體化
Materialization 作業會針對一個或多個 SQL 查詢傳回的資料列建立 CLR 物件。
下列呼叫會在「本機執行」,作為具體化的一部分:
建構函式
投影中的
ToString
方法投影中的型別轉換
跟在 AsEnumerable 方法之後的方法會在「本機執行」。 這個方法不會造成立即執行。
您可以使用
struct
做為查詢結果的傳回型別或是結果型別的成員。 實體必須為類別。 匿名型別會具體化成為類別執行個體,但具名 struct (非實體) 則可以用於投影中。查詢結果所傳回型別的成員可以屬於型別 IQueryable<T>。 它會具體化成為本機集合。
下列方法導致套用這些方法的序列發生「立即具體化」: