22

option (recompile)エンティティ フレームワークからテーブル値関数を呼び出していますが、選択した実行プランが最適ではないため、テーブル値関数を追加できる必要があります。SQL Server Management Studio でクエリを実行すると、次のようになります。

select 
       * 
from dbo.fDE_myquery(0, 0, 3309, '7/1/2013', '7/1/2014', 0, 0)
option (recompile)

EF から、そのヒントを追加する方法はありません。EF 部分は次のようになります。

var query = from f in ctx.fDE_myQuery(aBool, anotherBool, StartDate, 
            EndDate, someInt, moreBool)
            select f;

私はこの質問を見ました:

エンティティ フレームワークでパラメーター スニッフィングやクエリ ヒントを制御するにはどうすればよいですか?

しかし、それは古く、承認されたソリューションは、エンティティ フレームワークを使用して提案されたソリューション (プラン ガイドを使用)を実際に実装する方法について十分な情報を提供していません。それが唯一の解決策である場合、エンティティ フレームワークでプラン ガイドを使用するにはどうすればよいでしょうか。

4

2 に答える 2

41

私はこれに出くわしました:

https://entityframework.codeplex.com/wikipage?title=傍受

そして、次のようなことができるようです:

public class HintInterceptor : DbCommandInterceptor
{
    public override void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
    {
        command.CommandText += " option (recompile)";
        base.ReaderExecuting(command, interceptionContext);
    }
}

そして、次のように登録します(私はApplication_Startof で行いましたglobal.asax.cs):

DbInterception.Add(new HintInterceptor());

そして、それはあなたが変更できるようになりますCommandText。唯一の問題は、リーダーのクエリの一部がそのヒントによって悪影響を受ける可能性があるため、問題になる可能性があるすべてのリーダー クエリに添付されていることです。ヒントが適切かどうかを判断するためにコンテキストを使用して何かを行うことができると思います。または、最悪の場合はCommandTextそれ自体を調べることもできます。

最もエレガントまたはきめの細かいソリューションとは思えません。

編集: からinterceptorContextを取得できるため、DbContexts次のようなインターフェイスを定義しました。

public interface IQueryHintContext
{
    string QueryHint { get; set; }
    bool ApplyHint { get; set; }
}

次に、元の DbContext (EF によって生成された) から派生し、上記のインターフェイスを実装するクラスを作成しました。次に、インターセプターを次のように変更しました。

public class HintInterceptor : DbCommandInterceptor
{
    public override void ReaderExecuting(System.Data.Common.DbCommand command, DbCommandInterceptionContext<System.Data.Common.DbDataReader> interceptionContext)
    {
        if (interceptionContext.DbContexts.Any(db => db is Dal.IQueryHintContext))
        {
            var ctx = interceptionContext.DbContexts.First(db => db is Dal.IQueryHintContext) as Dal.IQueryHintContext;
            if (ctx.ApplyHint)
            {
                command.CommandText += string.Format(" option ({0})", ctx.QueryHint);
            }
        }
        base.ReaderExecuting(command, interceptionContext);
    }
}

これを使用するために、元のクラスではなく派生クラスを使用してコンテキストを作成し、QueryHint希望するものに設定し (recompileこの場合) ApplyHint、コマンドを実行する直前に設定し、後で false に戻します。

これをもう少し自己完結型にするために、次のようなインターフェイスを定義することになりました。

public interface IQueryHintContext
{
    string QueryHint { get; set; }
    bool ApplyHint { get; set; }
}

そして、このように私のdbコンテキストを拡張しました(もちろん、部分クラスを使用してEF生成クラスを拡張することもできます):

public class MyEntities_Ext : MyEntities, IQueryHintContext
{
    public string QueryHint { get; set; }
    public bool ApplyHint { get; set; }
}

そして、ターンオン、ターンオフの部分を少し扱いやすくするために、次のように定義しました。

public class HintScope : IDisposable
{
    public IQueryHintContext Context { get; private set; }
    public void Dispose()
    {
        Context.ApplyHint = false;
    }

    public HintScope(IQueryHintContext context, string hint)
    {
        Context = context;
        Context.ApplyHint = true;
        Context.QueryHint = hint;
    }
}

今それを使用するために、私はこれを行うことができます:

using (var ctx = new MyEntities_Ext()) 
{
    // any code that didn't need the query hint
    // ....
    // Now we want the query hint
    using (var qh = new HintScope(ctx, "recompile"))
    {
        // query that needs the recompile hint
    }
    // back to non-hint code
}

これは少しやり過ぎかもしれませんが、さらに開発することもできます (たとえば、文字列の代わりに使用可能なヒントに列挙型を使用するか、クエリ ヒントをサブクラス化して、毎回recompile文字列を指定する必要がなく、タイプミスのリスクがないようにする)。recompile私の差し迫った問題。

于 2014-11-05T16:55:15.170 に答える
3

fDE_myquery特定の使用法以外の他の呼び出し元はありますか? そして、これはどのくらいの頻度で呼び出されますか? 問題は、最適ではないSELECT * FROM dbo.fDE_myquery();計画を取得していることではなく、内部の 1 つ以上のクエリが最適ではない計画をfDE_myquery取得していることです。したがって、その TVF 内の 1 つ以上のクエリに を追加するだけです。OPTION(RECOMPILE)

この TVF が頻繁に呼び出されるとパフォーマンスに悪影響を及ぼします。そのため、この TVF の他の用途について尋ねました。これがこの TVF の唯一の、または断然メインの用途である場合、悪い計画が頻繁に取り上げられている場合は、それだけの価値があるかもしれません。

ただし、問題が発生していないこの TVF の発信者が他に複数いる場合は、 をRECOMPILETVF に配置することは適切ではない可能性があります。ただし、その場合、 をカプセル化するラッパー TVF を作成できますSELECT * FROM dbo.fDE_myquery() OPTION (RECOMPILE);。これは、より柔軟なソリューションのように見えます:)。私が試したところ、インライン TVF はOPTION節を評価していないようですが、マルチステートメント TVF はそれで問題ありませんでした。

編集:
または、これを純粋に EF で処理する場合は、1 行のコードで再コンパイル要求を発行するだけです。

ctx.context.ExecuteStoreCommand("EXEC sp_recompile 'dbo.fDE_myquery';");

そして、次のことを行います。

var query = from f in ctx.fDE_myQuery(aBool, anotherBool, StartDate, 
            EndDate, someInt, moreBool)
            select f;
于 2014-11-05T17:41:03.140 に答える