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

时间:2009-03-15 23:23:44

标签: c# linq-to-sql lambda

我对一行的内容不感兴趣,我只是想知道一行是否存在。 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())

...还是有更快的查询?

5 个答案:

答案 0 :(得分:85)

Count()方法可能会做额外的工作,因为(在TSQL中)EXISTSTOP 1通常要快得多; db可以优化“至少有一行”。就个人而言,我会使用any / predicate重载:

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

当然,你可以通过观察TSQL来比较每个人做的事情:

dc.Log = Console.Out;

答案 1 :(得分:11)

当然

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

这是最好的,如果要检查多个条件,那么写为

非常简单

假设您要检查公司的用户

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

答案 2 :(得分:4)

我想:

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

是最好的方法。

答案 3 :(得分:1)

对于那些声称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: 发现:处理时间:13.3962

来自DB: 发现:处理时间:12.0933

来自DB: 发现:处理时间:787.8801

再次:

来自DB: 完成:处理时间:13.3878

来自DB: 发现:处理时间:12.6881

来自DB: 发现:处理时间:780.2686

再次:

来自DB: 发现:处理时间:24.7081

来自DB: 完成:处理时间:23.6654

来自DB: 发现:处理时间:699.622

没有索引:

来自DB: 发现:处理时间:2395.1988

来自DB: 发现:处理时间:390.6334

来自DB: 发现:处理时间:664.8581

现在有些人可能认为它只有一两毫秒。然而,在我给它做一个索引之前,差异要大得多;几秒钟。

最后一次计算是因为我开始认为ToLower()比LIKE更快,我是对的,直到我尝试计数并在其上放置一个索引。我猜Lower()使索引无关紧要。

答案 4 :(得分:0)

我不同意选择top 1总是优于所有SQL实现的select count。你知道,这完全依赖于实现。奇怪的是,即使存储在特定数据库中的数据的性质也会影响整体结果。

如果我这样做,让我们检查它们的实现方式:对于这两种情况,投影(WHERE子句)评估是一个常见的步骤。

接下来选择top 1,你必须读取所有字段(除非你确实选择了top 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查询并输出前后时间差异。

  1. 选择计数
  2. 选择前1名
  3. 选择热门1 1
  4. 选择任何
  5. 在[NAME];

    上使用非聚集索引重复全部4