[英]What is the fastest way to determine if a row exists using 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())
...或者是否有更快的查詢?
Count()
方法可能會做額外的工作,因為(在TSQL中) EXISTS
或TOP 1
通常要快得多; db可以優化“至少有一行”。 就個人而言,我會使用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數據庫做了一個簡單的測試,有1400萬個給予或接受。 碼:
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,其中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:FOUND:處理時間:699.622
沒有索引:
來自DB:FOUND:處理時間:2395.1988
來自DB:FOUND:處理時間:390.6334
來自DB:FOUND:處理時間:664.8581
現在有些人可能認為它只有一兩毫秒。 然而,在我給它做一個索引之前,差異要大得多; 幾秒鍾。
最后的計算是在那里,我開始認為ToLower()比LIKE更快,我是對的,直到我嘗試計數並在其上放置一個索引。 我猜Lower()使索引無關緊要。
我不同意選擇top 1總是優於所有SQL實現的select count。 你知道,這完全依賴於實現。 奇怪的是,即使存儲在特定數據庫中的數據的性質也會影響整體結果。
讓我們按照我實現它們的方式檢查它們:對於這兩種情況,投影(WHERE子句)評估是一個常見的步驟。
對於選擇前1的下一個,您將必須讀取所有字段(除非您確實選擇了前1'x',例如:select top 1 1)。 這將在功能上等同於IQueryable.Any(...)。,除非您將花費一些時間在第一個遇到的記錄的每一列的值中閃爍(如果是EXISTS)。 如果在語句中找到SELECT TOP,則如果沒有后投影過程(例如ORDER BY子句),則投影將被截斷。 這個預處理會產生很小的成本,但是如果不存在記錄則這是額外的成本,在這種情況下,仍然完成一個完整的項目。
對於選擇計數,不執行預處理。 完成投影,如果EXISTS為假,則結果是即時的。 如果EXISTS為真,則計數仍然很快,因為它只是dW_Highest_Inclusive - dW_Lowest_Exclusive。 快到500 - 26.如果存在是假的,結果會更加即時。
因此剩下的情況是:投影的速度有多快,完全投射會松動什么? 答案導致最關鍵的問題是:[NAME]字段是否已編入索引! 如果你在[NAME]上有一個索引,那么任何一個查詢的性能都會非常接近,因此它可以歸結為開發人員的偏好。
總的來說,我將簡單地編寫兩到四個linq查詢並輸出前后時間差。
在[NAME]上使用非聚集索引重復全部4;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.