web-dev-qa-db-ja.com

Linq to SQLを使用して行が存在するかどうかを判断する最速の方法は何ですか?

行の内容には興味がありません。行が存在するかどうかを知りたいだけです。 Name列は主キーであるため、一致する行は0または1です。現在、私は使用しています:

if ((from u in dc.Users where u.Name == name select u).Count() > 0)
    // row exists
else
    // row doesn't exist

上記は機能しますが、行のすべてのコンテンツを選択することにより(存在する場合)、多くの不必要な作業を行います。以下は、より高速なクエリを作成しますか?

if (dc.Users.Where(u => u.Name == name).Any())

...またはさらに速いクエリがありますか?

45
Protagonist

Count()アプローチは、(TSQLで)EXISTSまたはTOP 1は、多くの場合、はるかに高速です。データベースは「少なくとも1行あります」を最適化できます。個人的には、any/predicateオーバーロードを使用します。

if (dc.Users.Any(u => u.Name == name)) {...}

もちろん、TSQLを見ると、それぞれの動作を比較できます。

dc.Log = Console.Out;
90
Marc Gravell

もちろん

if (dc.Users.Where(u => u.Name == name).Any())

これが最良であり、複数の条件を確認する場合は、次のように記述するのが非常に簡単です。

会社のユーザーを確認するとします

if (dc.Users.Where(u => u.ID== Id && u.Company==company).Any())
11
Raju

おもう:

if (dc.Users.Any(u => u.Name == name)) {...}

最適なアプローチです。

4
MRFerocius

Any()を主張する人々にとって、私はLinqPadでCommonPasswordsのSQLデータベースに対して1,400万回のギブまたはテイクを行う簡単なテストを行ったのです。コード:

var password = "qwertyuiop123";

var startTime = DateTime.Now;
"From DB:".Dump();
startTime = DateTime.Now;

if (CommonPasswords.Any(c => System.Data.Linq.SqlClient.SqlMethods.Like(c.Word, password)))
{
    $"FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump();
}
else
{
    $"NOT FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump();
}

"From DB:".Dump();
startTime = DateTime.Now;
if (CommonPasswords.Where(c => System.Data.Linq.SqlClient.SqlMethods.Like(c.Word, password)).Count() > 0)
{
    $"FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump();
}
else
{
    $"NOT FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump();
}

"From DB:".Dump();
startTime = DateTime.Now;
if (CommonPasswords.Where(c => c.Word.ToLower() == password).Take(1).Any())
{
    $"FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump();
}
else
{
    $"NOT FOUND: processing time: {(DateTime.Now - startTime).TotalMilliseconds}\r\n".Dump();
}

これが翻訳されたSQLです:

-- Region Parameters
DECLARE @p0 NVarChar(1000) = 'qwertyuiop123'
-- EndRegion
SELECT 
    (CASE 
        WHEN EXISTS(
            SELECT NULL AS [EMPTY]
            FROM [Security].[CommonPasswords] AS [t0]
            WHERE [t0].[Word] LIKE @p0
            ) THEN 1
        ELSE 0
     END) AS [value]
GO

-- Region Parameters
DECLARE @p0 NVarChar(1000) = 'qwertyuiop123'
-- EndRegion
SELECT COUNT(*) AS [value]
FROM [Security].[CommonPasswords] AS [t0]
WHERE [t0].[Word] LIKE @p0
GO

-- Region Parameters
DECLARE @p0 NVarChar(1000) = 'qwertyuiop123'
-- EndRegion
SELECT 
    (CASE 
        WHEN EXISTS(
            SELECT NULL AS [EMPTY]
            FROM (
                SELECT TOP (1) NULL AS [EMPTY]
                FROM [Security].[CommonPasswords] AS [t0]
                WHERE LOWER([t0].[Word]) = @p0
                ) AS [t1]
            ) THEN 1
        ELSE 0
     END) AS [value]

ANYはクエリを別のコードレイヤーにラップして、Case Where Exists Then 1 where as Count()がCountコマンドを追加するだけであることがわかります。これらの両方の問題は、Top(1)を実行できないことですが、Top(1)を使用するより良い方法がわかりません。

結果:

DBから:FOUND:処理時間:13.3962

DBから:FOUND:処理時間:12.0933

DBから:FOUND:処理時間:787.8801

再び:

DBから:FOUND:処理時間:13.3878

DBから:FOUND:処理時間:12.6881

DBから:FOUND:処理時間:780.2686

再び:

DBから:FOUND:処理時間:24.7081

DBから:FOUND:処理時間:23.6654

DBから:検出:処理時間:699.622

インデックスなし:

DBから:FOUND:処理時間:2395.1988

DBから:FOUND:処理時間:390.6334

DBから:FOUND:処理時間:664.8581

今、あなたの一部はそれがほんの1、2ミリ秒だと思っているかもしれません。ただし、インデックスを付ける前の差異ははるかに大きかった。数秒で。

最後の計算は、ToLower()がLIKEよりも高速であるという概念から始めたのであり、カウントしてインデックスを配置するまでは正しかった。 Lower()はインデックスを無関係にします。

1
Tod

トップ1を選択すると、すべてのSQL実装で常に選択カウントを上回ることに同意しません。それはすべて実装に依存しているのです。奇妙なことに、特定のデータベースに保存されているデータの性質も全体的な結果に影響します。

両方のケースを実装する方法で調べてみましょう。どちらの場合も、プロジェクション(WHERE句)の評価は一般的なステップです。

次に、トップ1を選択するために、すべてのフィールドの読み取りを行う必要があります(トップ1 'x'を選択しなかった場合:例:トップ1 1を選択)これは機能的にはIQueryable.Any(...)と同等です。ただし、EXISTSの場合、最初に検出されたレコードの各列の値をフラッシュするのに時間がかかる点が異なります。ステートメントでSELECT TOPが見つかった場合、投影後のプロシージャ(ORDER BY句など)がない場合、投影は切り捨てになります。この前処理には少額の費用がかかりますが、レコードが存在しない場合は追加費用になります。その場合、プロジェクト全体が実行されます。

選択カウントの場合、前処理は行われません。投影が行われ、EXISTSがfalseの場合、結果は即座に得られます。 EXISTSがtrueの場合、カウントは単なるdW_Highest_Inclusive-dW_Lowest_Exclusiveであるため、カウントは高速です。 500〜26の速さです。存在がfalseの場合、結果はさらに瞬時になります。

したがって、残りのケースは次のとおりです。投影はどのくらいの速さで、完全な投影を行うことで何を失いますかそして、答えはここで最も重要な問題につながります:[NAME]フィールドがインデックス化されているかどうかです! [NAME]にインデックスがある場合、いずれかのクエリのパフォーマンスは非常に近く、開発者の好みに合わせて決まります。

概して、2〜4つのlinqクエリを記述し、前後の時間差を出力します。

  1. カウントを選択
  2. トップ1を選択
  3. トップ1を選択1
  4. いずれかを選択

[NAME]の非クラスター化インデックスで4つすべてを繰り返します。

0
Pita.O