2

基本的にタグの辞書を持つドキュメントであるデータ構造があります。「Last Name」という名前のタグと「Smith」のタグ値を持つ特定のフォームタイプのすべてのドキュメントを取り戻そうとしています。ドキュメントに関連付けられた 0..N 個の「姓」タグがある場合があります。

次のlinqクエリを使用して、ソースドキュメントを一致するタグを持つ子に一致させようとしています:

DB.Documents
    .Where(doc => doc.FormID == pd.IndexForm.FormID)
    .Where(doc => doc.Document_StringIndex_ReadOnly
                .Join(Fields,
                        dsi => new { FieldName = dsi.FieldName, FieldValue = dsi.StringValue },
                        dsi2 => new { FieldName = dsi2.FieldName, FieldValue = dsi2.StringValue },
                        (dsi, dsi2) => dsi.Document).Count() > 0);

.ToTraceString() を使用して出力すると、次のクエリが生成されます

SELECT 
[Project1].*
FROM ( SELECT 
    [Extent1].*
    (SELECT 
        COUNT(cast(1 as bit)) AS [A1]
        FROM   [dbo].[Document_StringIndex_ReadOnly] AS [Extent2]
        INNER JOIN  (SELECT [Extent3].*
            FROM  [dbo].[Document] AS [Extent3]
            INNER JOIN [dbo].[Document_StringIndex_ReadOnly] AS [Extent4] ON [Extent3].[DocumentID] = [Extent4].[DocumentID] ) AS [Join1] ON (([Extent2].[FieldName] = [Join1].[FieldName]) OR (([Extent2].[FieldName] IS NULL) AND ([Join1].[FieldName] IS NULL))) AND (([Extent2].[StringValue] = [Join1].[StringValue]) OR (([Extent2].[StringValue] IS NULL) AND ([Join1].[StringValue] IS NULL)))
        LEFT OUTER JOIN [dbo].[Document] AS [Extent5] ON [Extent2].[DocumentID] = [Extent5].[DocumentID]
        WHERE ([Extent1].[DocumentID] = [Extent2].[DocumentID]) AND ([Join1].[DocumentID1] = @p__linq__7) AND ([Join1].[FieldName] = @p__linq__8)) AS [C1]
    FROM [dbo].[Document] AS [Extent1]
    WHERE [Extent1].[FormID] = @p__linq__5
)  AS [Project1]
WHERE [Project1].[C1] > 0

パラメータを定数で直接置換すると (以下に示すように)、クエリは非常に高速に実行されます。ただし、パラメーターをそのままにしておくと、クエリに数分かかります。

SELECT 
[Project1].*
FROM ( SELECT 
    [Extent1].*
    (SELECT 
        COUNT(cast(1 as bit)) AS [A1]
        FROM   [dbo].[Document_StringIndex_ReadOnly] AS [Extent2]
        INNER JOIN  (SELECT [Extent3].*
            FROM  [dbo].[Document] AS [Extent3]
            INNER JOIN [dbo].[Document_StringIndex_ReadOnly] AS [Extent4] ON [Extent3].[DocumentID] = [Extent4].[DocumentID] ) AS [Join1] ON (([Extent2].[FieldName] = [Join1].[FieldName]) OR (([Extent2].[FieldName] IS NULL) AND ([Join1].[FieldName] IS NULL))) AND (([Extent2].[StringValue] = [Join1].[StringValue]) OR (([Extent2].[StringValue] IS NULL) AND ([Join1].[StringValue] IS NULL)))
        LEFT OUTER JOIN [dbo].[Document] AS [Extent5] ON [Extent2].[DocumentID] = [Extent5].[DocumentID]
        WHERE ([Extent1].[DocumentID] = [Extent2].[DocumentID]) AND ([Join1].[DocumentID1] = 1015) AND ([Join1].[FieldName] = 'DDKey')) AS [C1]
    FROM [dbo].[Document] AS [Extent1]
    WHERE [Extent1].[FormID] = 22
)  AS [Project1]
WHERE [Project1].[C1] > 0

実行計画を生成した後、パラメーター値を直接代入すると、SQL Server がインデックス シークを実行し、クエリが高速になることがわかりました。パラメータをそのままにしておくとすぐに、SQL Server がインデックス スキャンを実行し、クエリがタイムアウトします。常にシークするように SQL サーバーを起動する方法はありますか? エンティティ フレームワークでパラメーター化されたクエリを使用しないようにすることはできますか?

4

1 に答える 1

4

生成された SQL では、この行

[Join1].[FieldName] = @p__linq__8

問題かもしれません。

FieldNameisvarchar(...)@p__linq__8isの場合nvarchar(...)、パラメーターの型がインデックスの型と一致しないため、この句によってテーブル スキャンが発生します。

「DDKey」を直接置き換えると、タイプが一致するため、インデックスシークが取得されます。N'DDkey' を使用してクエリを実行し、テーブル スキャンが実行されるかどうかを確認してください。

これは、Linq to Sql および Linq to Entities のさまざまなバージョンの問題ですが、今後のリリースで修正される可能性があります。

最新バージョンに更新できない場合に問題を回避する 1 つの方法は、に変更FieldNameすることnvarchar(...)です。

于 2012-06-01T19:24:25.947 に答える