2013-08-07 3 views
92

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

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

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 довольно медленно. –

+0

Извините, это Java, который разговаривает с M $ SQL через драйвер jdbc. Я обновлю свой OP. – SnakeDoc

+1

Существует [существует] (http://technet.microsoft.com/en-us/library/ms188336.aspx). –

ответ

111

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

+4

Неужели оптимизатор не учитывает его при поиске через PK (или любой другой уникальный ключ)? – zerkms

+1

В моем случае 'products.id' не является ПК ... это просто нормальное поле. – SnakeDoc

+3

Он сказал, что это был ПК, но если да, то оптимизатор учтет это. –

15

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

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

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

+5

Несмотря на свое имя, 'id' не является первичным ключом. Поэтому, даже если вы не считаете *, вам все равно нужно найти все соответствующие записи, возможно, тысячи из них. О aliasing - код - постоянная работа. Вы никогда не знаете, когда вам придется вернуться. Сглаживание помогает предотвратить глупые ошибки времени выполнения; например, уникальное имя столбца *, которому не нужен псевдоним *, больше не уникален, потому что кто-то создал столбец с тем же именем в другой объединенной таблице. –

+0

Да, вы абсолютно правы. Aliasing помогает много, но я не думаю, что это имеет какое-то значение, когда не используется объединение. Итак, я сказал, не используйте его, если не нужно. :) И вы можете найти длинную дискуссию [здесь] (http://blog.sqlauthority.com/2010/02/21/sql-server-if-existsselect-null-from-table-vs-if-existsselect-1 -from-table /) при проверке существования. :) – AgentSQL

+3

Я не знаю, почему я воспринял термин «сглаживание». Правильный термин - «квалификационный». Вот [более подробное объяснение Алекса Кузнецова] (http://sqlblog.com/blogs/alexander_kuznetsov/archive/2008/10/25/defensive-database-programming-qualifying-column-names.aspx). О одиночных табличных запросах - это одна таблица * сейчас *.Но позже, когда обнаруживается ошибка, и вы пытаетесь провести наводнение, клиент нервничает, вы присоединяетесь к другой таблице, чтобы встретить сообщение об ошибке - легко исправляемое сообщение, но не в этот потный момент, небольшой удар поражает - и вы исправляете ошибка запоминания никогда не оставлять столбец ... –

126

EXISTS (или NOT EXISTS) специально разработан для проверки того, существует ли что-то и поэтому должен быть (и является) лучшим вариантом. Он будет останавливаться на первой строке, которая соответствует, поэтому для нее не требуется предложение TOP, и на самом деле он не выбирает никаких данных, поэтому накладные расходы по размеру столбцов отсутствуют. Вы можете безопасно использовать SELECT * здесь - не за исключением SELECT 1, SELECT NULL или SELECT AnyColumn ... (you can even use invalid expression like SELECT 1/0 and it will not break).

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 
+0

не нужно сначала выполнять инструкцию SELECT, а затем выполнять инструкцию IF EXISTS ... вызывая дополнительные накладные расходы и, следовательно, больше времени обработки? – SnakeDoc

+6

@SnakeDoc № 'Exists' работает с' select' таким образом, что он выходит, как только найден один ряд. Кроме того, существует только замечание о существовании записи, а не фактических значений в записи, сохраняя необходимость загрузки строки с диска (при условии, что критерии поиска индексируются, конечно). Что касается накладных расходов 'if' - вам все равно придется потратить это минимальное время. –

+1

@ NikolaMarkovinović интересный момент. Я не уверен, существует ли указатель в этом поле, и мой новичок SQL не знает, как это выяснить. Я работаю с этой БД с Java через JDBC, и база данных находится в удаленном месте где-то в colo. Мне было предоставлено только «сводку базы данных», в которой просто указаны поля, которые существуют в каждой таблице, их тип и любые FK или PK. Это что-то меняет? – SnakeDoc

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

+2

Этот код не будет работать с SQL Server –

0

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

UPDATE TableName SET column=value WHERE column=value 
IF @@ROWCOUNT=0 
BEGIN 
    --Do work 
END    
2
SELECT COUNT(*) FROM products WHERE products.id = ?; 

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

+0

Однако вы вынуждаете db циклически перебирать все записи, очень медленно на больших таблицах. – amd

7
SELECT CASE WHEN EXISTS (SELECT TOP 1 * 
         FROM dbo.[YourTable] 
         WHERE [YourColumn] = [YourValue]) 
      THEN CAST (1 AS BIT) 
      ELSE CAST (0 AS BIT) END 

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

+0

Возможно, можно опустить оператор Top и оператор *, чтобы сделать его более быстрым, поскольку Exist выйдет после того, как он найдет запись, так что примерно так: SELECT CASE КОГДА СУЩЕСТВУЕТ (SELECT 1 FROM dbo. [YourTable] WHERE [YourColumn] = [YourValue]) \t THEN CAST (1 AS BIT) ELSE CAST (0 AS BIT) КОНЕЦ –

-2
DECLARE @USER TABLE(SM_Tbl_Name nvarchar(100),SM_Col_Namenvarchar(100),SM_Disp_Name nvarchar(200),SM_Data_Type nvarchar(100),SM_Is_Required BIT, 
    SM_UI_Tag nvarchar(100),SM_Master_Table nvarchar(100),SM_Text_Field,nvarchar(100),SM_Value_Field,nvarchar(100),SM_Where_Cond nvarchar(400),SM_Is_Readonly BIT,SM_Isactive BIT,SM_Status VARCHAR(1)) 


    INSERT INTO @USER(SM_Tbl_Name,SM_Col_Name,SM_Disp_Name,SM_Data_Type,SM_Is_Required,SM_UI_Tag,SM_Master_Table,SM_Text_Field,SM_Value_Field,SM_Where_Cond,SM_Is_Readonly,SM_Isactive,SM_Status) 

    SELECT 'SADM_Users' as SM_Tbl_Name,'Flex_3'as SM_Col_Name,'Salesman Status Principal' as SM_Disp_Name,'Int' as SM_Data_Type,'0' as SM_Is_Required,'DropDown' as SM_UI_Tag,'ADM_LOVs' as SM_Master_Table,'ALOV_Name' as SM_Text_Field,'ALOV_Id' as SM_Value_Field, 'ALOV_Type=''SALESMAN_STATUS_PRINCIPAL''' as SM_Where_Cond,'0' as SM_Is_Readonly,'1' as SM_Isactive,'I' as SM_Status 
    UNION 
    SELECT 'SADM_Users' as SM_Tbl_Name,'Flex_4'as SM_Col_Name,'Salesman Status Channel' as SM_Disp_Name,'Int' as SM_Data_Type,'0' as SM_Is_Required,'DropDown' as SM_UI_Tag,'ADM_LOVs' as SM_Master_Table,'ALOV_Name' as SM_Text_Field,'ALOV_Id' as SM_Value_Field, 'ALOV_Type=''SALESMAN_STATUS_CHANNEL''' as SM_Where_Cond,'0' as SM_Is_Readonly,'1' as SM_Isactive,'I' as SM_Status 
    UNION 
    SELECT 'SADM_Users' as SM_Tbl_Name,'Flex_5'as SM_Col_Name,'Supervisor Status Principal' as SM_Disp_Name,'Int' as SM_Data_Type,'0' as SM_Is_Required,'DropDown' as SM_UI_Tag,'ADM_LOVs' as SM_Master_Table,'ALOV_Name' as SM_Text_Field,'ALOV_Id' as SM_Value_Field, 'ALOV_Type=''SUPERVISOR_STATUS_PRINCIPAL''' as SM_Where_Cond,'0' as SM_Is_Readonly,'1' as SM_Isactive,'I' as SM_Status 
    UNION 
    SELECT 'SADM_Users' as SM_Tbl_Name,'Flex_6'as SM_Col_Name,'Supervisor Status Channel' as SM_Disp_Name,'Int' as SM_Data_Type,'0' as SM_Is_Required,'DropDown' as SM_UI_Tag,'ADM_LOVs' as SM_Master_Table,'ALOV_Name' as SM_Text_Field,'ALOV_Id' as SM_Value_Field, 'ALOV_Type=''SUPERVISOR_STATUS_CHANNEL''' as SM_Where_Cond,'0' as SM_Is_Readonly,'1' as SM_Isactive,'I' as SM_Status 

INSERT INTO [dbo].[SADM_Flex_Ext_Def](SM_Tenant_Id,SM_Bu_Id,SM_Tbl_Name,SM_Col_Name,SM_Disp_Name,SM_Data_Type,SM_Is_Required,SM_UI_Tag,SM_Master_Table,SM_Text_Field,SM_Value_Field,SM_Where_Cond,SM_Is_Readonly,SM_Isactive,SM_Status) 
SELECT 1 as SM_Tenant_Id,1 as SM_Bu_Id,T.SM_Tbl_Name,T.SM_Col_Name,T.SM_Disp_Name,T.SM_Data_Type,T.SM_Is_Required,T.SM_UI_Tag,T.SM_Master_Table,T.SM_Text_Field,T.SM_Value_Field,T.SM_Where_Cond,T.SM_Is_Readonly,T.SM_Isactive,T.SM_Status FROM @USER as T 
left join SADM_Flex_Ext_Def as TT on TT.SM_Tenant_Id=97 and TT.SM_Bu_Id=41 and TT.SM_Col_Name=T.SM_Col_Name and TT.SM_Tbl_Name=T.SM_Tbl_Name 
WHERE TT.SM_Id IS NULL 
PRINT 'SADM_Flex_Ext_Def - Insert: ' + CONVERT(VARCHAR, @@ROWCOUNT) 
GO 
4

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

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

ELSE  
    BEGIN 
     --<Do something> 
    END 
0

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

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

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

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 
0

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

Смежные вопросы