11

Skip(1).Any()いつになるかはわかりませんが、Entity Framework を使用する場合は思いやりよりも の使用が優れていることを示す記事を読みましたCount()(間違っていることを覚えているかもしれません)。生成された T-SQL コードを見た後では、これについてはよくわかりません。

最初のオプションは次のとおりです。

int userConnectionCount = _dbContext.HubConnections.Count(conn => conn.UserId == user.Id);
bool isAtSingleConnection = (userConnectionCount == 1);

これにより、妥当な次の T-SQL コードが生成されます。

SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
  COUNT(1) AS [A1]
    FROM [dbo].[HubConnections] AS [Extent1]
    WHERE [Extent1].[UserId] = @p__linq__0
)  AS [GroupBy1]

私が覚えている限り、提案されたクエリである他のオプションは次のとおりです。

bool isAtSingleConnection = !_dbContext
    .HubConnections.OrderBy(conn => conn.Id)
    .Skip(1).Any(conn => conn.UserId == user.Id);

上記の LINQ クエリに対して生成された T-SQL は次のとおりです。

SELECT 
CASE WHEN ( EXISTS (SELECT 
    1 AS [C1]
    FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[UserId] AS [UserId]
        FROM ( SELECT [Extent1].[Id] AS [Id], [Extent1].[UserId] AS [UserId], row_number() OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number]
            FROM [dbo].[HubConnections] AS [Extent1]
        )  AS [Extent1]
        WHERE [Extent1].[row_number] > 1
    )  AS [Skip1]
    WHERE [Skip1].[UserId] = @p__linq__0
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT 
    1 AS [C1]
    FROM ( SELECT [Extent2].[Id] AS [Id], [Extent2].[UserId] AS [UserId]
        FROM ( SELECT [Extent2].[Id] AS [Id], [Extent2].[UserId] AS [UserId], row_number() OVER (ORDER BY [Extent2].[Id] ASC) AS [row_number]
            FROM [dbo].[HubConnections] AS [Extent2]
        )  AS [Extent2]
        WHERE [Extent2].[row_number] > 1
    )  AS [Skip2]
    WHERE [Skip2].[UserId] = @p__linq__0
)) THEN cast(0 as bit) END AS [C1]
FROM  ( SELECT 1 AS X ) AS [SingleRowTable1];

ここで適切な方法はどれですか。これら2つの間に大きなパフォーマンスの違いはありますか?

4

3 に答える 3

10

クエリのパフォーマンスは、存在するインデックス、実際のデータ、存在するデータに関する統計がどれだけ古いかなど、多くのことに依存します。SQL クエリ プラン オプティマイザーは、これらのさまざまなメトリックを調べて、効率的なクエリ プランを考え出します。したがって、クエリ 1 が常にクエリ 2 よりも優れている、またはその逆であるという単純な答えは正しくありません。

Skip(1).Any()とはいえ、以下の私の答えは、記事のスタンスと、 Count() > 1. 2 番目のクエリは、サイズが大きく、ほとんど判読できませんが、効率的な方法で解釈できるように見えます。繰り返しますが、これは前述の事柄に依存します。データベースが結果を把握するために調べなければならない行の数は、Count(). count の場合、必要なインデックスが存在すると仮定すると (2 番目のケースでは OrderBy を効率的にするための Id のクラスター化インデックス)、db は count 個の行を通過する必要があります。2 番目のケースでは、答えにたどり着くまでに最大 2 行を通過する必要があります。

私たちの分析をより科学的にして、私の上記の理論が何らかの根拠を保持しているかどうかを見てみましょう. このために、ダミーの顧客データベースを作成しています。Customer タイプは次のようになります。

public class Customer
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

このコードを使用して、データベースに 100K のランダムな行をシードしています (これを証明する必要があります)。

    for (int j = 0; j < 100; j++)
    {
        using (CustomersContext db = new CustomersContext())
        {
            Random r = new Random();
            for (int i = 0; i < 1000; i++)
            {
                Customer c = new Customer
                {
                    Name = Guid.NewGuid().ToString(),
                    Age = r.Next(0, 100)
                };
                db.Customers.Add(c);
            }
            db.SaveChanges();
        }
    }

サンプルコードはこちら

今、私が使用しようとしているクエリは次のとおりです。

db.Customers.Where(c => c.Age == 26).Count() > 1; // scenario 1

db.Customers.Where(c => c.Age == 26).OrderBy(c => c.ID).Skip(1).Any() // scenario 2

クエリプランをキャッチするために SQL プロファイラーを開始しました。キャプチャされた計画は次のようになります。

シナリオ 1:

上記の画像で、シナリオ 1 の推定コストと実際の行数を確認してください。 シナリオ 1 - 推定コスト シナリオ 1 - 実際の行数

シナリオ 2:

以下の画像で、シナリオ 2 の推定コストと実際の行数を確認してください。 シナリオ 2 - 推定コスト シナリオ 2 - 実際の行数

最初の推測によると、推定コストと行数は、Count の場合と比較して、Skip および any の場合の方が少なくなります。

結論:

この分析はさておき、他の多くの人が以前にコメントしたように、これらはコードで行うべきパフォーマンスの最適化ではありません。これらのようなものは、非常に最小限の (存在しないと言えます) パフォーマンスの利点で読みやすさを損ないます。Count()私は楽しみのためにこの分析を行っただけで、シナリオ 2 を選択するための基礎としてこれを使用することは決してありませんSkip().Any()

于 2013-04-24T19:18:17.183 に答える
6

Skip(1).Any()の使用法が よりも優れていることを示す、これに関する記事を読みましたCount()

そのステートメントは、LINQ to objects クエリに非常に当てはまります。LINQ to objects クエリSkip(1).Any()では、シーケンスの最初の 2 つの項目のみを取得する必要があり、その後に続くすべての項目を無視できます。シーケンスにかなりコストのかかる操作が含まれる (そして適切に実行を延期する) 場合、またはさらに重要なことに、シーケンスが無限である場合、これは大きな問題になる可能性があります。ほとんどのクエリでは少し重要ですが、多くの場合、それほど重要ではありません。

代わりに、クエリ プロバイダーに基づく LINQ クエリの場合、大きな違いはほとんどありません。特に EF では、ご覧のとおり、生成されたクエリは目立った違いはありません。確かに、違いがある可能性はありますか。あるケースは、クエリ プロバイダーによって他のケースよりも適切に処理される可能性があり、特定のクエリは、どちらかが使用する特定のリファクタリングにより最適化される可能性が高くなります。

これら 2 つの EF クエリに大きな違いがあることを誰かが示唆している場合、LINQ to objects クエリにのみ適用するように設計されたガイドラインを誤って適用している可能性があります。

于 2013-04-24T19:21:18.113 に答える
0

テーブル/データセットのレコード数に間違いなく依存します。多数のレコードがある場合、ID はインデックス化されているため、ID のカウントは非常に高速ですが、1 つのレコードをスキップして次のレコードを取得する方が高速です。

確かに、このプロセスはどちらの場合もサブミリ秒で実行できます。10,000 以上のレコードを超えるレコード数がない限り、特定のしきい値を下回る必要がない限り、それは問題になりません。SQL Server はクエリ実行プランをキャッシュすることを忘れないでください。同じクエリを再実行した場合、データがその下でかなり大幅に変更されない限り、最初に実行した後に違いが見られない場合があります。

于 2013-04-24T19:12:37.707 に答える