SQL: Как правильно проверить, существует ли запись


207

Читая некоторую документацию по настройке SQL, я нашел это:

SELECT COUNT(*) :

  • Подсчитывает количество строк.
  • Часто неправильно используется для проверки существования записи.

Это SELECT COUNT(*)действительно так плохо?

Как правильно проверить наличие записи?

Ответы:


255

Лучше использовать одно из следующих:

-- Method 1.
SELECT 1
FROM table_name
WHERE unique_key = value;

-- Method 2.
SELECT COUNT(1)
FROM table_name
WHERE unique_key = value;

Первый вариант не должен давать вам никакого результата или один результат, второй счет должен быть равен нулю или единице.

Сколько лет документации вы используете? Несмотря на то, что вы прочитали хороший совет, большинство оптимизаторов запросов в последних СУБД все SELECT COUNT(*)равно оптимизируют , поэтому, хотя теоретическая разница (и более старые базы данных) существует, вы не должны заметить никакой разницы на практике.


1
Я поясню, что я имел в виду «уникальный ключ» с условием «ключ = значение», но кроме этого я все еще за своим ответом.
Мартин Шапендонк,

1
ХОРОШО. При такой предпосылке запрос действительно вернул бы только одну или нулевую запись. НО: вопрос не ограничивается уникальным столбцом. Также: 2-й запрос count (1) эквивалентен count (*) из практического POV.
Мартин Ба,

1
Вопрос говорит: «Как правильно проверить наличие записи A». Я интерпретировал это как единственное число, как в: 1 записи. Разница между count (*) и count (1) уже покрыта моим ответом. Я предпочитаю count (1), потому что он не зависит от конкретной реализации RDBMS.
Мартин Шапендонк,

192

Я бы предпочел вообще не использовать функцию Count:

IF [NOT] EXISTS ( SELECT 1 FROM MyTable WHERE ... )
     <do smth>

Например, если вы хотите проверить, существует ли пользователь, прежде чем вставить его в базу данных, запрос может выглядеть следующим образом:

IF NOT EXISTS ( SELECT 1 FROM Users WHERE FirstName = 'John' AND LastName = 'Smith' )
BEGIN
    INSERT INTO Users (FirstName, LastName) VALUES ('John', 'Smith')
END

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

Хорошо отметить, что с помощью T-SQL
Бронек

20

Ты можешь использовать:

SELECT 1 FROM MyTable WHERE <MyCondition>

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


Вы имели в виду ТОП 1? -> (ВЫБЕРИТЕ ТОП 1 ИЗ MyTable WHERE <MyCondition>)
Джейкоб

6
Нет, я имел в виду именно «1»
Cătălin Pitiș

1
чтобы оптимизатор запросов мог даже знать, что вам не нужно читать / не нужны оставшиеся наборы данных, вы должны указать SELECT TOP 1 1 FROM ... WHERE ... (или использовать соответствующие подсказки запроса для вашей RDBS)
eFloh

3
Сам оператор Exists пытается получить только абсолютный минимум информации, поэтому добавление TOP 1 ничего не делает, кроме добавления 5 символов к размеру запроса. - sqlservercentral.com/blogs/sqlinthewild/2011/04/05/…
AquaAlex

13

Другие ответы довольно хороши, но было бы также полезно добавить LIMIT 1(или эквивалентный , чтобы предотвратить проверку ненужных строк.


3
Если какой-либо запрос «проверки на существование» возвращает более одной строки, я думаю, что было бы более полезно дважды проверить предложение WHERE, а не ОГРАНИЧИТЬ количество результатов.
Мартин Шапендонк

2
Я думаю, что предел используется в Oracle, а не в SQL Server
Шантану Гупта

7
Я рассматриваю случай, когда они могут законно состоять из нескольких строк - где вопрос: «Есть ли (одна или несколько) строк, которые удовлетворяют этому условию?» В этом случае вы не хотите смотреть на все из них, только один.
JesseW

1
@Shantanu - я знаю, поэтому я связался с (очень сквозной) статьей en.wikipedia, объясняющей другие формы.
JesseW

11
SELECT COUNT(1) FROM MyTable WHERE ...

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

я хотел бы использовать

SELECT TOP 1 * FROM MyTable WHERE ...

Найдя 1 запись, он завершит цикл.


В случае, SELECT TOP 1если он действительно прекратит работу после нахождения одного из них, или он продолжит находить все, чтобы сказать, какой из них ТОП?
Эйрик Х

3
PS: Конечно, я всегдаIF EXISTS (SELECT TOP 1 1 FROM ... WHERE ..)
Eirik H

оператор Star заставит СУБД получить доступ к кластерному индексу, а не только к индексу (ам), которые будут необходимы для вашего условия соединения. так что лучше использовать постоянное значение в качестве результата, то есть выбрать top 1 1 .... Это вернет 1 или DB-Null, в зависимости от того, является ли условие совпадением или нет.
eFloh

мило. Мне нравится первый.
isxaker

10

Ты можешь использовать:

SELECT COUNT(1) FROM MyTable WHERE ... 

или

WHERE [NOT] EXISTS 
( SELECT 1 FROM MyTable WHERE ... )

Это будет более эффективно, чем SELECT *если вы просто выбираете значение 1 для каждой строки, а не для всех полей.

Существует также небольшая разница между COUNT (*) и COUNT (имя столбца):

  • COUNT(*) будет считать все строки, включая нули
  • COUNT(column name)будет считать только ненулевые вхождения имени столбца

2
Вы делаете ошибочное предположение, что СУБД как-то проверит все эти столбцы. Разница в производительности между count(1)и count(*)будет отличаться только в самых мертвых СУБД.
paxdiablo

2
Нет, я говорю, что вы действительно полагаетесь на детали реализации, когда заявляете, что это будет более эффективно. Если вы действительно хотите добиться максимальной производительности, вам следует профилировать ее для конкретной реализации, используя репрезентативные данные, или просто полностью об этом забыть. Все остальное потенциально вводит в заблуждение и может резко измениться, например, при переходе с DB2 на MySQL.
paxdiablo

1
Я хочу дать понять, что я не оспариваю ваш ответ. Это является полезным. Единственное бит я беру вопрос с этим требованием эффективности , так как мы сделали оценку в DB2 / г и обнаружил , что нет никакой разницы между count(*)и count(1). Так ли это для других СУБД », я не могу сказать.
paxdiablo

3
«Все остальное может вводить в заблуждение и может кардинально измениться, например, при переходе (например) с DB2 на MySQL». Скорее всего, вас укусит снижение производительности SELECT COUNT (*) при перемещении СУБД, чем разница в реализации в SELECT 1 или COUNT (1). Я твердо верю в написание кода, который наиболее четко выражает именно то, чего вы хотите достичь, вместо того, чтобы полагаться на оптимизаторы или компиляторы по умолчанию для вашего желаемого поведения.
Уинстон Смит

1
Вводящее в заблуждение утверждение «COUNT (*)» означает «остановку строк». Это не требует доступа к какой-либо конкретной колонке. И в большинстве случаев даже не потребуется доступ к самой строке, поскольку для подсчета достаточно уникального индекса.
Джеймс Андерсон

9

Ты можешь использовать:

SELECT 1 FROM MyTable WHERE... LIMIT 1

Используйте select 1для предотвращения проверки ненужных полей.

Используйте LIMIT 1 для предотвращения проверки ненужных строк.


3
Хороший вопрос, но Limit работает на MySQL и PostgreSQL, вершина работает на SQL Server, вы должны отметить это в своем ответе
Leo Gurdian

0

Я использую этот способ:

IIF(EXISTS (SELECT TOP 1 1 
                FROM Users 
                WHERE FirstName = 'John'), 1, 0) AS DoesJohnExist

0

Другой вариант:

SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [MyTable] AS [MyRecord])
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.