¿Cuál es la forma más rápida de determinar si existe una fila usando Linq to SQL?


No estoy interesado en el contenido de una fila, solo quiero saber si existe una fila. La columna Name es una clave primaria, por lo que habrá 0 o 1 filas coincidentes. Actualmente, estoy usando:

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

Mientras lo anterior funciona, hace mucho trabajo innecesario seleccionando todo el contenido de la fila (si existe). Hace lo siguiente para crear una consulta más rápida:

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

...¿o hay una consulta aún más rápida?

Author: Protagonist, 2009-03-16

5 answers

El enfoque Count() puede hacer un trabajo extra, ya que (en TSQL) EXISTS o TOP 1 son a menudo mucho más rápidos; la base de datos puede optimizar "hay al menos una fila". Personalmente, usaría la sobrecarga de cualquier / predicado:

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

Por supuesto, puede comparar lo que hace cada uno observando el TSQL:

dc.Log = Console.Out;
 79
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-15 23:25:38

Por supuesto

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

Esto es lo mejor y si hay varias condiciones para verificar, entonces es muy simple escribir como

Digamos que desea comprobar el usuario para la empresa entonces

if (dc.Users.Where(u => u.ID== Id && u.Company==company).Any())
 9
Author: Raju,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-18 08:57:56

Creo que:

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

Es el mejor enfoque.

 3
Author: MRFerocius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-24 22:04:41

Para aquellas personas que reclaman Any() es el camino a seguir He hecho una prueba simple en LINQPad contra una base de datos SQL de CommonPasswords, 14 millones más o menos. Código:

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

Aquí está el SQL traducido:

-- 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]

Puedes ver que ANY envuelve la consulta en otra capa de código para hacer un CASO Donde Existe y luego 1 donde as Count() solo agrega un comando Count. El problema con ambos es que no puedes hacer un Top (1) pero no puedo ver una mejor manera de usar Top (1)

Resultados:

Desde DB: ENCONTRADO: tiempo de procesamiento: 13.3962

Desde DB: ENCONTRADO: tiempo de procesamiento: 12.0933

Desde DB: ENCONTRADO: tiempo de procesamiento: 787.8801

De nuevo:

Desde DB: ENCONTRADO: tiempo de procesamiento: 13.3878

Desde DB: ENCONTRADO: tiempo de procesamiento: 12.6881

Desde DB: ENCONTRADO: tiempo de procesamiento: 780.2686

De nuevo:

Desde DB: ENCONTRADO: tiempo de procesamiento: 24.7081

Desde DB: ENCONTRADO: tiempo de procesamiento: 23.6654

De DB: ENCONTRADO: tiempo de procesamiento: 699.622

Sin Índice:

Desde DB: ENCONTRADO: tiempo de procesamiento: 2395.1988

Desde DB: ENCONTRADO: tiempo de procesamiento: 390.6334

Desde DB: ENCONTRADO: tiempo de procesamiento: 664.8581

Ahora algunos de ustedes pueden estar pensando que es solo un milisegundo o dos. Sin embargo, la variación era mucho mayor antes de poner un índice en él; por unos pocos segundos.

El último cálculo está ahí ya que empecé con la noción de que ToLower () sería más rápido que LIKE, y Tenía razón, hasta que intenté contar y le puse un índice. Supongo que la más Baja() hace que el índice de irrelavent.

 2
Author: Tod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-19 15:42:18

No estoy de acuerdo en que la selección de top 1 siempre superará a select count para todas las implementaciones SQL. Todo depende de la implementación, ya sabes. Curiosamente, incluso la naturaleza de los datos almacenados en una base de datos en particular también afecta el resultado general.

Examinemos ambos de la manera en que los implementaría si lo hiciera: Para ambos casos, la evaluación de la proyección (cláusula WHERE) es un paso común.

A continuación, para seleccionar el top 1, tendrá que hacer una lectura de todos los campos (a menos que haya seleccionado top 1' x ' por ejemplo: seleccione top 1 1). Esto será funcionalmente equivalente a IQueryable.Cualquier(...)., excepto que pasará algún tiempo parpadeando en el valor para cada columna del primer registro encontrado si EXISTE. Si SELECT TOP se encuentra en la instrucción, la proyección es truncamiento si no hay proc posterior a la proyección (por ejemplo, la cláusula ORDER BY). Este preproceso incurre en un pequeño costo, pero esto es un costo adicional si no existe ningún registro, en cuyo caso, un proyecto completo todavía está Terminado.

Para select count, el preproceso no se realiza. Se realiza una proyección y si EXISTE es false, el resultado es instantáneo. Si EXISTS es true, el conteo sigue siendo rápido porque será un mero dW_Highest_Inclusive-dW_Lowest_Exclusive. Tan rápido como 500 - 26. Si existe es false, el resultado es aún más instantáneo.

El caso restante por lo tanto es: ¿Qué tan rápido es la proyección y qué se pierde al hacer la proyección completa? Y la respuesta lleva a la cuestión más crucial aquí que is: es el campo [NOMBRE] indexado o no! Si tiene un índice en [NOMBRE], el rendimiento de cualquiera de las consultas será tan cercano que se reduce a la preferencia del desarrollador.

En general, simplemente escribiré de dos a cuatro consultas linq y generaré diferencias en el tiempo antes y después.

  1. seleccione count
  2. seleccione top 1
  3. seleccione top 1 1
  4. seleccione cualquier

Repita los 4 con un índice no agrupado en [NOMBRE];

 0
Author: Pita.O,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-16 00:04:42