LINQ to SQL (および LINQ to Entities) のパフォーマンスの改善 (Tim Ng)

ここ数か月間、VB チームと Data Programmability チームは LINQ to SQL (LINQ to Entities のマニフェスト内にもあります) のパフォーマンス上の問題の解決に取り組んできました。問題というのは、VB LINQ クエリに Null 許容型の列を対象とするフィルタが含まれる場合、LINQ to SQL で生成される T-SQL クエリが最適化されないことです。

たとえば、次のクエリを例とします。

 Dim q = From o In db.Orders Where o.EmployeeID = 123 Select o.CustomerID 

このシナリオでは、Orders.EmployeeID フィールドは Null 許容型のフィールド (Integer?) です。VB では、論理演算子 (<、<=、= など) は 3 値論理演算子と見なされるため、等式の比較結果は "Boolean?" 型の値となります。しかし、LINQ 演算子は "Boolean" 型の値が返されることを予想し、"Boolean?" 型ではないため、VB では合体演算子を使用して "Boolean?" を "Boolean" に変換する必要があります (詳細については、式ツリーと合体演算子 (英語) に関するブログの記事を参照してください)。

LINQ to SQL は合体演算子を最適化できますが、VS 2008 RTM では、述語論理から 3 値論理 (およびその逆) に変換されるため、列の該当するインデックスが飛んでしまうことがあります。

VS 2008 RTM で生成される SQL の例を次に挙げます。

 

SELECT [t0].[CustomerID]
FROM [dbo].[Orders] AS [t0]
WHERE (COALESCE(
(CASE
WHEN [t0].[EmployeeID] = @p0 THEN 1
WHEN NOT ([t0].[EmployeeID] = @p0) THEN 0
ELSE NULL
END),@p1)) = 1
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [123]
-- @p1: Input Int (Size = 0; Prec = 0; Scale = 0) [0]

朗報

この問題に関し、LINQ to SQL チームは、このパターンに該当する LINQ to SQL のコード生成の修正方法を見つけ出し、予想されるとおりに、次の SQL コードが生成されるようになりました。

SELECT [t0].[CustomerID]
FROM [dbo].[Orders] AS [t0]
WHERE [t0].[EmployeeID] = @p0
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [123]

つまり、LINQ to SQL (および同様の修正を加えた LINQ to Entities) は VB から SQL に対し、適切な 3 値論理を渡すようになったのです。

この具体例では、VB と SQL はどちらも 3 値のブール論理を使用しますが、中間層である LINQ 演算子が 2 値のブール論理を使用し、層の間の変換が行われなかったため、最適化されないコードが VS2008 RTM で生成される結果となっていました。この修正については、後日年内にリリースされる更新プログラムをお待ちください。

VB チーム

投稿 : 2008 年 3 月 28 日 8:58 AM

分類 : LINQ/VB9Timothy Ng

VB チームの Web ログ - https://blogs.msdn.com/vbteam/archive/2008/03/28/linq-to-sql-and-linq-to-entities-performance-improvements-tim-ng.aspx (英語) より