Самый быстрый способ определить, существует ли запись

152

Как следует из названия ... Я пытаюсь найти самый быстрый способ с наименьшими накладными расходами, чтобы определить, существует ли запись в таблице или нет.

Пример запроса:

SELECT COUNT(*) FROM products WHERE products.id = ?;

    vs

SELECT COUNT(products.id) FROM products WHERE products.id = ?;

    vs

SELECT products.id FROM products WHERE products.id = ?;

Скажем, ?заменяется на 'TB100'... и первый, и второй запросы вернут один и тот же результат (скажем ... 1для этого разговора). Последний запрос вернет, 'TB100'как ожидалось, или ничего, если idв таблице нет.

Цель состоит в том, чтобы выяснить, есть ли idв таблице или нет. Если нет, программа вставит следующую запись, если это так, программа пропустит ее или выполнит запрос UPDATE на основе другой логики программы, выходящей за рамки этого вопроса.

Что быстрее и с меньшими накладными расходами? (Это будет повторяться десятки тысяч раз за один запуск программы и будет выполняться много раз в день).

(Выполнение этого запроса к M $ SQL Server из Java через предоставленный M $ драйвер JDBC)

SnakeDoc
источник
1
Это может зависеть от базы данных. Например, рассчитывать на Postgres довольно медленно.
Майк Кристенсен,
Извините, это Java, разговаривающая с M $ SQL через драйвер jdbc. Я обновлю свой OP.
SnakeDoc 07
2
Также существует .
Никола Марковинович
@Nikola Markovinović: как бы вы использовали его в этом случае?
zerkms 07
5
@zerkms Зависит от контекста. Если бы в хранимой процедуре было бы if exists(select null from products where id = @id); если в запросе, вызванном непосредственно клиентом select case when exists (...) then 1 else 0 end.
Никола Марковинович

Ответы:

174

SELECT TOP 1 products.id FROM products WHERE products.id = ?; превзойдет все ваши предложения, так как прекратит выполнение после того, как найдет первую запись.

Declan_K
источник
5
Разве оптимизатор сам не учитывает это при поиске по PK (или любому другому уникальному ключу)?
zerkms
3
Он сказал, что это ПК, но если это так, то да, оптимизатор примет это во внимание.
Declan_K
3
@Declan_K: похоже, что моя волшебная сфера в этом случае не удалась, а столбец с названием idне является PK. Так что +1 к вашему совету.
zerkms
4
Если это не ПК, я бы также предложил убедиться, что в этом столбце есть индекс. В противном случае запрос должен будет выполнять сканирование таблицы вместо более быстрого поиска по таблице.
CD Jorgensen
4
Я думаю, мы должны рассмотреть ответ @ nenad-zivkovic, а не этот.
Джулио Каччин 07
204

EXISTS(или NOT EXISTS) специально разработан для проверки, существует ли что-то и поэтому должен быть (и остается) лучшим вариантом. Он остановится на первой совпадающей строке, поэтому не требует TOPпредложения и фактически не выбирает какие-либо данные, поэтому нет накладных расходов на размер столбцов. Вы можете смело использовать SELECT *здесь - ничем не отличается от SELECT 1, SELECT NULLили SELECT AnyColumn... (вы даже можете использовать недопустимое выражение, например, SELECT 1/0и оно не сломается) .

IF EXISTS (SELECT * FROM Products WHERE id = ?)
BEGIN
--do what you need if exists
END
ELSE
BEGIN
--do what needs to be done if not
END
Ненад Живкович
источник
разве это не должно сначала выполнить оператор SELECT, а затем выполнить оператор IF EXISTS ... вызывая дополнительные накладные расходы и, следовательно, большее время обработки?
SnakeDoc
8
@SnakeDoc No. Existsработает selectтаким образом, что закрывается, как только будет найдена одна строка. Кроме того, существует просто отмечает наличие записи, а не фактических значений в записи, избавляя от необходимости загружать строку с диска (конечно, при условии, что критерии поиска индексируются). Что касается накладных расходов if- вам все равно придется потратить это ничтожное время.
Никола Марковинович
1
@ NikolaMarkovinović интересный момент. Я не уверен, существует ли Индекс в этом поле, и мой новичок SQL не знает, как это узнать. Я работаю с этой БД с Java через JDBC, и база данных удаленно расположена где-то в коло. Мне предоставили только «сводку базы данных», в которой просто подробно описаны поля, существующие в каждой таблице, их тип и любые FK или PK. Это что-нибудь меняет?
SnakeDoc 08
3
@SnakeDoc Чтобы узнать о структуре таблицы, включая внешние ключи и индексы, запустите процедуру sp_help table_name . Индексы важны, когда дело доходит до извлечения нескольких строк из многих, используя select topили exists; если их нет, sql Engine должен будет выполнить сканирование таблицы. Это наименее желательный вариант поиска по таблице. Если у вас нет полномочий на создание индексов, вам придется связаться с техническим персоналом на другой стороне, чтобы узнать, корректируют ли они их автоматически или ожидают, что вы предложите индексы.
Никола Марковинович
1
@Konstantin Вы можете сделать что-то вродеSELECT CASE WHEN EXISTS(..) THEN 1 ELSE 0 END;
Ненад Живкович
23

Ничто не может победить -

SELECT TOP 1 1 FROM products WHERE id = 'some value';

Вам не нужно считать, чтобы узнать, есть ли данные в таблице. И не используйте псевдоним, если он не нужен.

AgentSQL
источник
5
Несмотря на название, idэто не первичный ключ. Таким образом, даже если вы не считая вам все еще нужно , чтобы найти все записи , соответствующие, возможно , тысячи из них. По поводу алиасинга - код постоянно дорабатывается. Никогда не знаешь, когда тебе придется вернуться. Псевдонимы помогают предотвратить глупые ошибки времени выполнения; например, уникальное имя столбца, которому не нужен псевдоним, больше не является уникальным, поскольку кто-то создал столбец с таким же именем в другой объединенной таблице.
Никола Марковинович
Да, вы абсолютно правы. Сглаживание очень помогает, но я не думаю, что это имеет значение, когда не используются объединения. Итак, я сказал, не используйте его, если в этом нет необходимости. :) И вы можете найти долгое обсуждение здесь о проверке существования. :)
AgentSQL
3
Не знаю, почему я принял этот термин aliasing. Правильный термин есть qualifying. Вот более подробное объяснение Алекса Кузнецова . О запросах одной таблицы - теперь это одна таблица . Но позже, когда обнаруживается ошибка, и вы пытаетесь сдержать флуд, клиент нервничает, вы присоединяетесь к другому столу, чтобы увидеть сообщение об ошибке - легко исправляемое сообщение, но не в этот потный момент, наносится небольшой штрих - и вы исправляете ошибка при запоминании никогда не покидать колонну ...
Никола Марковинович
1
Не могу игнорировать это сейчас. Благодарность!! :)
AgentSQL
15
SELECT CASE WHEN EXISTS (SELECT TOP 1 *
                         FROM dbo.[YourTable] 
                         WHERE [YourColumn] = [YourValue]) 
            THEN CAST (1 AS BIT) 
            ELSE CAST (0 AS BIT) END

Этот подход возвращает вам логическое значение.

Крис Коулман
источник
1
Вероятно, можно опустить оператор Top и оператор *, чтобы сделать его немного быстрее, поскольку Exist выйдет, как только найдет запись, поэтому что-то вроде этого: SELECT CASE WHEN EXISTS (SELECT 1 FROM dbo. [YourTable] WHERE [YourColumn] = [YourValue]) THEN CAST (1 AS BIT) ELSE CAST (0 AS BIT) END
Стефан
В этом предложении не упоминается, почему это будет быстрее, чем встроенные операторы существует / не существует в SQL Server. Без какого-либо бенчмаркинга мне было бы трудно поверить, что оператор case даст более быстрый результат, чем немедленный ответ true / false.
Bonez024
8

Вы также можете использовать

 If EXISTS (SELECT 1 FROM dbo.T1 WHERE T1.Name='Scot')
    BEGIN
         --<Do something>
    END 

ELSE    
     BEGIN
       --<Do something>
     END
Атик Саркер
источник
7

Не думайте, что кто-то еще упомянул об этом, но если вы уверены, что данные под вами не изменятся, вы можете также применить подсказку NoLock, чтобы убедиться, что они не блокируются при чтении.

SELECT CASE WHEN EXISTS (SELECT 1 
                     FROM dbo.[YourTable] WITH (NOLOCK)
                     WHERE [YourColumn] = [YourValue]) 
        THEN CAST (1 AS BIT) 
        ELSE CAST (0 AS BIT) END
Стефан Звонар
источник
3
SELECT COUNT(*) FROM products WHERE products.id = ?;

Это решение кросс-реляционной базы данных, которое работает во всех базах данных.

мошенник
источник
7
Однако вы заставляете базу данных перебирать все записи, что очень медленно для больших таблиц
amd
@amd не могу объяснить почему?
UmNyobe
@amd ваш комментарий имеет смысл. Этот запрос больше относится к НАЙТИ ВСЕ, чем НАЙТИ ЛЮБОЙ.
UmNyobe
1

Ниже приведен самый простой и быстрый способ определить, существует ли запись в базе данных или нет. Хорошо, что она работает во всех реляционных БД.

SELECT distinct 1 products.id FROM products WHERE products.id = ?;
маниш прасад
источник
1

Для тех, кто наткнулся на это из опыта MySQL или Oracle - MySQL поддерживает предложение LIMIT для выбора ограниченного количества записей, в то время как Oracle использует ROWNUM.

Вернер
источник
0
create or replace procedure ex(j in number) as
i number;
begin
select id into i from student where id=j;
if i is not null then
dbms_output.put_line('exists');
end if;
exception
   when no_data_found then
        dbms_output.put_line(i||' does not exists');

end;
Киран
источник
2
Возможно, ваш код отлично работает, но было бы лучше, если вы добавите дополнительную информацию, чтобы она была лучше понятна.
idmean
0

Я использовал это в прошлом, и это не требует полного сканирования таблицы, чтобы увидеть, существует ли что-то. Это супер быстро ...

UPDATE TableName SET column=value WHERE column=value
IF @@ROWCOUNT=0
BEGIN
     --Do work
END             
Эрик Парсонс
источник