简体   繁体   English

使用Linq to SQL确定行是否存在的最快方法是什么?

[英]What is the fastest way to determine if a row exists using Linq to SQL?

I am not interested in the contents of a row, I just want to know if a row exists. 我对行的内容不感兴趣,我只想知道是否存在行。 The Name column is a primary key, so there will either be 0 or 1 matching rows. Name列是主键,因此将有0或1个匹配的行。 Currently, I am using: 目前,我正在使用:

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

While the above works, it does a lot of unnecessary work by selecting all the contents of the row (if it exists). 虽然上述工作,但通过选择行的所有内容(如果存在),它会做很多不必要的工作。 Does the following create a faster query: 以下是否创建更快的查询:

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

...or is there an even faster query? ...或者是否有更快的查询?

The Count() approach may do extra work, as (in TSQL) EXISTS or TOP 1 are often much quicker; Count()方法可能会做额外的工作,因为(在TSQL中) EXISTSTOP 1通常要快得多; the db can optimise "is there at least one row". db可以优化“至少有一行”。 Personally, I would use the any/predicate overload: 就个人而言,我会使用any / predicate重载:

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

Of course, you can compare what each one does by watching the TSQL: 当然,你可以通过观察TSQL来比较每个人做的事情:

dc.Log = Console.Out;

Of Course 当然

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

this is best and if multiple conditions to check then it is very simple to write as 这是最好的,如果要检查多个条件,那么写起来非常简单

Say you want to check the user for company then 假设您要检查公司的用户

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

I think: 我认为:

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

is the best approach. 是最好的方法。

For those people claiming Any() is the way forward I've done a simple test in LinqPad against a SQL database of CommonPasswords, 14 million give or take. 对于那些声称Any()是前进的人,我在LinqPad中对CommonPasswords的SQL数据库做了一个简单的测试,有1400万个给予或接受。 Code: 码:

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();
}

Here is the translated SQL: 这是翻译的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]

You can see that ANY wraps the query up in another layer of code to do a CASE Where Exists Then 1 where as Count() just adds in a Count command. 您可以看到ANY将查询包装在另一层代码中以执行CASE Where Exists Then 1,其中Count()只是在Count命令中添加。 Problem with both of these is you can't do a Top(1) but I can't see a better way using Top(1) 这两个问题都是你不能做Top(1)但我看不到更好的方法使用Top(1)

Results: 结果:

From DB: FOUND: processing time: 13.3962 来自DB:FOUND:处理时间:13.3962

From DB: FOUND: processing time: 12.0933 来自DB:FOUND:处理时间:12.0933

From DB: FOUND: processing time: 787.8801 来自DB:FOUND:处理时间:787.8801

Again: 再次:

From DB: FOUND: processing time: 13.3878 来自DB:FOUND:处理时间:13.3878

From DB: FOUND: processing time: 12.6881 来自DB:FOUND:处理时间:12.6881

From DB: FOUND: processing time: 780.2686 来自DB:FOUND:处理时间:780.2686

Again: 再次:

From DB: FOUND: processing time: 24.7081 来自DB:FOUND:处理时间:24.7081

From DB: FOUND: processing time: 23.6654 来自DB:FOUND:处理时间:23.6654

From DB: FOUND: processing time: 699.622 来自DB:FOUND:处理时间:699.622

Without Index: 没有索引:

From DB: FOUND: processing time: 2395.1988 来自DB:FOUND:处理时间:2395.1988

From DB: FOUND: processing time: 390.6334 来自DB:FOUND:处理时间:390.6334

From DB: FOUND: processing time: 664.8581 来自DB:FOUND:处理时间:664.8581

Now some of you may be thinking it's only a millisecond or two. 现在有些人可能认为它只有一两毫秒。 However the varience was much greater before I put an index on it; 然而,在我给它做一个索引之前,差异要大得多; by a few seconds. 几秒钟。

The last calculation is there as I started with the notion that ToLower() would be faster than LIKE, and I was right, until I tried count and put an Index on it. 最后的计算是在那里,我开始认为ToLower()比LIKE更快,我是对的,直到我尝试计数并在其上放置一个索引。 I guess the Lower() makes the index irrelavent. 我猜Lower()使索引无关紧要。

I do not agree that selecting top 1 will always outperform select count for all SQL implementations. 我不同意选择top 1总是优于所有SQL实现的select count。 It's all implementation dependent, you know. 你知道,这完全依赖于实现。 Curiously, even the nature of data stored in a particular database also affects the overall outcome. 奇怪的是,即使存储在特定数据库中的数据的性质也会影响整体结果。

Let's examine both of them the way I would implement them if I were to do so: For both cases, the projection (WHERE clause) evaluation is a common step. 让我们按照我实现它们的方式检查它们:对于这两种情况,投影(WHERE子句)评估是一个常见的步骤。

Next for select top 1, you will have to do a read of all fields (unless you did select top 1 'x' eg: select top 1 1). 对于选择前1的下一个,您将必须读取所有字段(除非您确实选择了前1'x',例如:select top 1 1)。 This will be functionally equivalent to IQueryable.Any(...)., except that you will spend some time flashing in the value for each column of the first encountered record if EXISTS. 这将在功能上等同于IQueryable.Any(...)。,除非您将花费一些时间在第一个遇到的记录的每一列的值中闪烁(如果是EXISTS)。 If SELECT TOP is found in the statement, the projection is truncation if there's no post-projection proc (eg ORDER BY clause). 如果在语句中找到SELECT TOP,则如果没有后投影过程(例如ORDER BY子句),则投影将被截断。 This preprocess incurs a small cost but this is extra cost if no record does exist, in which case, a full project is still done. 这个预处理会产生很小的成本,但是如果不存在记录则这是额外的成本,在这种情况下,仍然完成一个完整的项目。

For select count, the preprocess is not done. 对于选择计数,不执行预处理。 A projection is done and if EXISTS is false, the result is instant. 完成投影,如果EXISTS为假,则结果是即时的。 If EXISTS is true, the count is still fast because it will be a mere dW_Highest_Inclusive - dW_Lowest_Exclusive. 如果EXISTS为真,则计数仍然很快,因为它只是dW_Highest_Inclusive - dW_Lowest_Exclusive。 As quick as 500 - 26. If exists is false, the result is even more instant. 快到500 - 26.如果存在是假的,结果会更加即时。

The remaining case therefore is: How fast is the projection and what do you loose by doing full projection? 因此剩下的情况是:投影的速度有多快,完全投射会松动什么? And the answer leads to the most crucial issue here which is: is the [NAME] field indexed or not! 答案导致最关键的问题是:[NAME]字段是否已编入索引! If you have an index on [NAME] the performance of either query will be so close that it boils down to developer's preference. 如果你在[NAME]上有一个索引,那么任何一个查询的性能都会非常接近,因此它可以归结为开发人员的偏好。

By and large, I will simply write two to four linq queries and output difference in time before and after. 总的来说,我将简单地编写两到四个linq查询并输出前后时间差。

  1. select count 选择计数
  2. select top 1 选择前1名
  3. select top 1 1 选择前1 1
  4. select any 选择任何

Repeat all 4 with an nonclustered index on [NAME]; 在[NAME]上使用非聚集索引重复全部4;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM