行の内容には興味がありません。行が存在するかどうかを知りたいだけです。 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())
...またはさらに速いクエリがありますか?
Count()
アプローチは、(TSQLで)EXISTS
またはTOP 1
は、多くの場合、はるかに高速です。データベースは「少なくとも1行あります」を最適化できます。個人的には、any/predicateオーバーロードを使用します。
if (dc.Users.Any(u => u.Name == name)) {...}
もちろん、TSQLを見ると、それぞれの動作を比較できます。
dc.Log = Console.Out;
もちろん
if (dc.Users.Where(u => u.Name == name).Any())
これが最良であり、複数の条件を確認する場合は、次のように記述するのが非常に簡単です。
会社のユーザーを確認するとします
if (dc.Users.Where(u => u.ID== Id && u.Company==company).Any())
おもう:
if (dc.Users.Any(u => u.Name == name)) {...}
最適なアプローチです。
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を選択すると、すべての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クエリを記述し、前後の時間差を出力します。
[NAME]の非クラスター化インデックスで4つすべてを繰り返します。