Найшвидший спосіб визначити, чи існує запис


143

Як підказує заголовок ... Я намагаюся розібратися в найшвидшому способі з найменшими накладними витратами, щоб визначити, чи існує запис у таблиці чи ні.

Зразок запиту:

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)


1
Це може залежати від бази даних. Наприклад, розраховувати на Postgres досить повільно.
Майк Крістенсен

Вибачте, це Java, яка розмовляє з M $ SQL через драйвер jdbc. Я оновлю свій ОП.
SnakeDoc


@Nikola Markovinovic: як би ти використовував це в цьому випадку?
zerkms

5
@zerkms Залежить від контексту. Якби у збереженій процедурі це було б if exists(select null from products where id = @id); якщо у запиті, викликаному безпосередньо клієнтом select case when exists (...) then 1 else 0 end.
Микола Марковинович

Відповіді:


170

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


5
Чи оптимізатор не враховує це сам під час пошуку через ПК (чи будь-який інший унікальний ключ)?
zerkms

3
Він нвер заявив, що це ПК, але якщо так, то оптимізатор врахує це.
Declan_K

3
@Declan_K: начебто моя магічна сфера в цьому випадку не вдалася, і стовпець під назвою як idне ПК. Тож +1 до вашої поради.
zerkms

4
Якщо це не ПК, я б також запропонував переконатися, що в цьому стовпці є індекс. В іншому випадку запит повинен буде виконати сканування таблиці замість швидшого пошуку таблиці.
CD Jorgensen

4
Я думаю, що нам слід розглянути відповідь @ nenad-zivkovic над цим.
Джуліо Каччін

193

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

7
@SnakeDoc No. Existsпрацює з selectтаким способом, що він закривається, як тільки знайдеться один ряд. Крім того, існує лише зауваження про наявність запису, а не фактичних значень у записі, що врятує необхідність завантаження рядка з диска (якщо, звичайно, припускати, що критерії пошуку індексуються). Щодо накладних витрат if- вам все одно доведеться витратити цей мізерний час.
Микола Марковинович

1
@ NikolaMarkovinović цікавий момент. Я не впевнений, чи існує індекс у цьому полі, і мій newbish SQL не знає, як це дізнатися. Я працюю з цією БД від Java через JDBC, і база даних десь віддалено розташована в колоні. Мені було надано лише "резюме бази даних", в якому детально описуються, які поля існують у кожній таблиці, їх тип та будь-які FK чи PK. Це щось змінює?
SnakeDoc

3
@SnakeDoc Щоб дізнатися про структуру таблиці, включаючи зовнішні ключі та індекси, запустіть sp_help table_name . Індекси є важливими, коли йдеться про вилучення кількох рядків з багатьох, незалежно від використання select topабо exists; якщо їх немає, двигун sql повинен буде виконати сканування таблиці. Це найменш бажаний варіант пошуку таблиці. Якщо ви не маєте права створювати індекси, вам доведеться повідомити технічному персоналу з іншого боку, щоб дізнатися, чи вони їх автоматично коригують, чи вони очікують, що ви запропонуєте індекси.
Микола Марковинович

1
@Konstantin Ви можете зробити щось на кшталтSELECT CASE WHEN EXISTS(..) THEN 1 ELSE 0 END;
Ненад Живкович,

21

Ніщо не може перемогти -

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

Вам не потрібно рахувати, щоб знати, чи є дані в таблиці. І не використовуйте псевдоніми, коли це не потрібно.


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 вийде, як тільки він знайде запис, так щось подібне: ВИБІР СЛУЧА КОГО ВИНАГАЄТЬСЯ (ВИБІР 1 З dbo. [YourTable] WHERE [YourColumn] = [YourValue]) THEN CAST (1 AS BIT) ELSE CAST (0 AS BIT) END
Стефан Звонар

У цій пропозиції не згадується, чому це було б швидше за вбудовані оператори існує / не існує в SQL Server. Без будь-якого бенчмаркінгу мені важко буде повірити, що випадок справи принесе більший результат, ніж негайний істинний / хибний відповідь.
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
Однак ви змушуєте db перебирати всі записи, дуже повільно на великих таблицях
amd

@amd піклується пояснити чому?
UmNyobe

@amd ваш коментар має повний сенс. Цей запит швидше ЗНАЙДАЄТЬСЯ ВСЕ, ніж ЗНАЙТИ БУДЬ-ЯКОГО.
UmNyobe

1

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

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

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             

0

Для тих, хто стикається з цим на фоні MySQL або Oracle - MySQL підтримує пункт LIMIT для вибору обмеженої кількості записів, тоді як Oracle використовує ROWNUM.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.