Какой самый быстрый способ определить, существует ли строка с использованием Linq to SQL? - PullRequest
43 голосов
/ 16 марта 2009

Меня не интересует содержание строки, я просто хочу узнать, существует ли строка. Столбец 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 ]

85 голосов
/ 16 марта 2009

Подход Count() может выполнять дополнительную работу, поскольку (в TSQL) EXISTS или TOP 1 часто намного быстрее; БД может оптимизировать "есть хотя бы одна строка". Лично я бы использовал перегрузку any / Предикат:

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

Конечно, вы можете сравнить то, что делает каждый, посмотрев TSQL:

dc.Log = Console.Out;
11 голосов
/ 18 июня 2013

Конечно

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

это лучше, и если несколько условий для проверки, то очень просто записать как

Скажем, вы хотите проверить пользователя для компании, тогда

if (dc.Users.Where(u => u.ID== Id && u.Company==company).Any())
4 голосов
/ 16 марта 2009

Я думаю:

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

- лучший подход.

1 голос
/ 19 февраля 2018

Для тех, кто заявляет, что Any () - это путь вперед, я провел простой тест в LinqPad с базой данных SQL CommonPasswords, 14 миллионов «давай или бери». Код:

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]

Вы можете видеть, что ЛЮБОЙ упаковывает запрос в другой слой кода, чтобы выполнить CASE, где существует, тогда 1, где Count () просто добавляет команду Count. Проблема с обоими из них в том, что вы не можете сделать Top (1), но я не вижу лучшего способа, используя Top (1)

Результаты:

из БД: НАЙДЕНО: время обработки: 13.3962

из БД: НАЙДЕНО: время обработки: 12.0933

из БД: НАЙДЕНО: время обработки: 787.8801

Еще раз:

из БД: НАЙДЕНО: время обработки: 13.3878

из БД: НАЙДЕНО: время обработки: 12.6881

из БД: НАЙДЕНО: время обработки: 780.2686

Еще раз:

из БД: НАЙДЕНО: время обработки: 24.7081

из БД: НАЙДЕНО: время обработки: 23.6654

из БД: НАЙДЕНО: время обработки: 699.622

без индекса:

из БД: НАЙДЕНО: время обработки: 2395.1988

из БД: НАЙДЕНО: время обработки: 390.6334

из БД: НАЙДЕНО: время обработки: 664.8581

Теперь некоторые из вас могут думать, что это всего лишь миллисекунда или две. Однако различие было намного больше, прежде чем я поместил в него индекс; на несколько секунд.

Последнее вычисление началось с того, что я начал с представления о том, что ToLower () будет быстрее, чем LIKE, и я был прав, пока не попробовал считать и не поместил в него индекс. Я полагаю, что Lower () делает индекс бесполезным.

0 голосов
/ 16 марта 2009

Я не согласен с тем, что выбор топ 1 всегда будет превосходить счетчик выбора для всех реализаций SQL. Знаете, все зависит от реализации. Любопытно, что даже характер данных, хранящихся в конкретной базе данных, также влияет на общий результат.

Давайте рассмотрим их оба, как я бы их реализовал, если бы сделал это: для обоих случаев оценка проекции (предложение WHERE) является обычным шагом.

Далее для выбора top 1 вам нужно будет прочитать все поля (если вы не выбрали top 1 'x', например: выберите top 1 1). Это будет функционально эквивалентно IQueryable.Any (...)., За исключением того, что вы потратите некоторое время на мигание значения для каждого столбца первой обнаруженной записи, если EXISTS. Если в выражении найден SELECT TOP, проекция является усечением, если нет процедуры постпроекции (например, предложение ORDER BY). Этот препроцесс требует небольших затрат, но это дополнительные расходы, если записи не существует, и в этом случае полный проект все еще выполняется.

Для выбора количества предварительная обработка не завершена. Проекция выполнена, и если EXISTS ложно, результат мгновенен. Если EXISTS равен true, счетчик все еще быстр, потому что это будет просто dW_Highest_Inclusive - dW_Lowest_Exclusive. Быстро, как 500 - 26. Если существует ложь, результат еще более мгновенный.

Таким образом, оставшийся случай: какова скорость проекции и что вы теряете, выполняя полную проекцию? И ответ приводит к самой важной проблеме: индексируется ли поле [ИМЯ] или нет! Если у вас есть индекс [NAME], производительность любого запроса будет настолько близка, что сводится к предпочтениям разработчика.

В общем, я просто напишу от двух до четырех запросов linq и выведу разницу во времени до и после.

  1. выберите количество
  2. выберите топ 1
  3. выберите топ 1 1
  4. выберите любой

Повторите все 4 с некластеризованным индексом для [ИМЯ];

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...