Подтвердить что ты не робот

Каков самый быстрый способ определить, существует ли строка с использованием 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())

... или есть еще более быстрый запрос?

4b9b3361

Ответ 1

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

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

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

dc.Log = Console.Out;

Ответ 2

Конечно

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

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

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

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

Ответ 3

Я думаю:

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

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

Ответ 4

Для тех, кто заявляет, что 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() делает индекс нерелевантным.

Ответ 5

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

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

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

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

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

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

  • выберите счетчик
  • выберите верхнюю часть 1
  • выберите верхнюю часть 1 1
  • выберите любой

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