Раньше я писал свои EXISTS чеки так:
IF EXISTS (SELECT * FROM TABLE WHERE Columns=@Filters)
BEGIN
UPDATE TABLE SET ColumnsX=ValuesX WHERE Where Columns=@Filters
END
Один из администраторов баз данных в предыдущей жизни сказал мне, что когда я делаю EXISTS
предложение, SELECT 1
вместоSELECT *
IF EXISTS (SELECT 1 FROM TABLE WHERE Columns=@Filters)
BEGIN
UPDATE TABLE SET ColumnsX=ValuesX WHERE Columns=@Filters
END
Это действительно имеет значение?
sql
sql-server
tsql
Радж Мор
источник
источник
Ответы:
Нет, SQL Server умен и знает, что он используется для EXISTS, и не возвращает системе НИКАКИХ ДАННЫХ.
Ответ Microsoft: http://technet.microsoft.com/en-us/library/ms189259.aspx?ppud=4
Чтобы проверить себя, попробуйте запустить следующее:
SELECT whatever FROM yourtable WHERE EXISTS( SELECT 1/0 FROM someothertable WHERE a_valid_clause )
Если бы он действительно что-то делал со списком SELECT, он выдал бы div с нулевой ошибкой. Это не так.
РЕДАКТИРОВАТЬ: Обратите внимание, что стандарт SQL действительно говорит об этом.
Стандарт ANSI SQL 1992, стр. 191 http://www.contrib.andrew.cmu.edu/~shadow/sql/sql1992.txt
источник
EXISTS
трюк с 1/0 может быть даже расширен на этоSELECT 1 WHERE EXISTS(SELECT 1/0)
... кажется , шаг более абстрактно , то , как второйSELECT
не имеетFROM
пунктаSELECT COUNT(*) WHERE EXISTS(SELECT 1/0)
. ASELECT
без aFROM
в SQL Server обрабатывается так, как если бы он обращался к таблице с одной строкой (например, аналогично выбору изdual
таблицы в других СУБД)SELECT
создается таблица с 1 строкой, прежде чем она сделает что-либо еще, так что даже если1/0
таблица с 1 строкой все еще мусорEXISTS
?Причина этого заблуждения, по-видимому, связана с убеждением, что в конечном итоге он прочитает все столбцы. Легко понять, что это не так.
CREATE TABLE T ( X INT PRIMARY KEY, Y INT, Z CHAR(8000) ) CREATE NONCLUSTERED INDEX NarrowIndex ON T(Y) IF EXISTS (SELECT * FROM T) PRINT 'Y'
Дает план
Это показывает, что SQL Server смог использовать самый узкий доступный индекс для проверки результата, несмотря на то, что индекс не включает все столбцы. Доступ к индексу осуществляется с помощью оператора полусоединения, что означает, что он может остановить сканирование, как только будет возвращена первая строка.
Итак, ясно, что это мнение ошибочно.
Однако Конор Каннингем из группы оптимизатора запросов объясняет здесь, что он обычно использует
SELECT 1
в этом случае, поскольку это может незначительно повлиять на производительность при компиляции запроса.Я протестировал четыре возможных способа выражения этого запроса на пустой таблице с различным количеством столбцов.
SELECT 1
противSELECT *
противSELECT Primary_Key
противSELECT Other_Not_Null_Column
.Я выполнял запросы в цикле, используя
OPTION (RECOMPILE)
и измеряя среднее количество выполнений в секунду. Результаты ниже+-------------+----------+---------+---------+--------------+ | Num of Cols | * | 1 | PK | Not Null col | +-------------+----------+---------+---------+--------------+ | 2 | 2043.5 | 2043.25 | 2073.5 | 2067.5 | | 4 | 2038.75 | 2041.25 | 2067.5 | 2067.5 | | 8 | 2015.75 | 2017 | 2059.75 | 2059 | | 16 | 2005.75 | 2005.25 | 2025.25 | 2035.75 | | 32 | 1963.25 | 1967.25 | 2001.25 | 1992.75 | | 64 | 1903 | 1904 | 1936.25 | 1939.75 | | 128 | 1778.75 | 1779.75 | 1799 | 1806.75 | | 256 | 1530.75 | 1526.5 | 1542.75 | 1541.25 | | 512 | 1195 | 1189.75 | 1203.75 | 1198.5 | | 1024 | 694.75 | 697 | 699 | 699.25 | +-------------+----------+---------+---------+--------------+ | Total | 17169.25 | 17171 | 17408 | 17408 | +-------------+----------+---------+---------+--------------+
Как можно видеть, нет последовательного победителя между
SELECT 1
и,SELECT *
а разница между двумя подходами незначительна.SELECT Not Null col
ИSELECT PK
появляются немного быстрее , хотя.Производительность всех четырех запросов снижается по мере увеличения количества столбцов в таблице.
Поскольку таблица пуста, эта связь кажется объяснимой только количеством метаданных столбца. Поскольку
COUNT(1)
легко увидеть, что этоCOUNT(*)
в какой-то момент переписывается из приведенного ниже.SET SHOWPLAN_TEXT ON; GO SELECT COUNT(1) FROM master..spt_values
Что дает следующий план
|--Compute Scalar(DEFINE:([Expr1003]=CONVERT_IMPLICIT(int,[Expr1004],0))) |--Stream Aggregate(DEFINE:([Expr1004]=Count(*))) |--Index Scan(OBJECT:([master].[dbo].[spt_values].[ix2_spt_values_nu_nc]))
Присоединение отладчика к процессу SQL Server и случайное прерывание при выполнении нижеприведенного
DECLARE @V int WHILE (1=1) SELECT @V=1 WHERE EXISTS (SELECT 1 FROM ##T) OPTION(RECOMPILE)
Я обнаружил, что в тех случаях, когда таблица имеет 1024 столбца большую часть времени, стек вызовов выглядит примерно так, как показано ниже, что указывает на то, что он действительно тратит большую часть времени на загрузку метаданных столбца, даже когда
SELECT 1
он используется (для случая, когда таблица имеет 1 столбец, который случайным образом разбивается, не попал в этот бит стека вызовов за 10 попыток)Эта попытка ручного профилирования поддерживается профилировщиком кода VS 2012, который показывает совсем другой набор функций, потребляющих время компиляции, для двух случаев ( первые 15 функций - 1024 столбца против лучших 15 функций - 1 столбец ).
Обе версии
SELECT 1
иSELECT *
версии завершают проверку разрешений столбцов и терпят неудачу, если пользователю не предоставлен доступ ко всем столбцам в таблице.Пример я скопировал из разговора на кучу
CREATE USER blat WITHOUT LOGIN; GO CREATE TABLE dbo.T ( X INT PRIMARY KEY, Y INT, Z CHAR(8000) ) GO GRANT SELECT ON dbo.T TO blat; DENY SELECT ON dbo.T(Z) TO blat; GO EXECUTE AS USER = 'blat'; GO SELECT 1 WHERE EXISTS (SELECT 1 FROM T); /* ↑↑↑↑ Fails unexpectedly with The SELECT permission was denied on the column 'Z' of the object 'T', database 'tempdb', schema 'dbo'.*/ GO REVERT; DROP USER blat DROP TABLE T
Таким образом, можно предположить, что незначительная очевидная разница при использовании
SELECT some_not_null_col
заключается в том, что он завершает проверку разрешений только для этого конкретного столбца (хотя по-прежнему загружает метаданные для всех). Однако это, похоже, не согласуется с фактами, поскольку процентная разница между двумя подходами, если что-то становится меньше по мере увеличения количества столбцов в базовой таблице.В любом случае я не буду спешить и менять все свои запросы на эту форму, поскольку разница очень незначительна и проявляется только во время компиляции запроса. Удаление,
OPTION (RECOMPILE)
чтобы последующие исполнения могли использовать кэшированный план, дало следующее.+-------------+-----------+------------+-----------+--------------+ | Num of Cols | * | 1 | PK | Not Null col | +-------------+-----------+------------+-----------+--------------+ | 2 | 144933.25 | 145292 | 146029.25 | 143973.5 | | 4 | 146084 | 146633.5 | 146018.75 | 146581.25 | | 8 | 143145.25 | 144393.25 | 145723.5 | 144790.25 | | 16 | 145191.75 | 145174 | 144755.5 | 146666.75 | | 32 | 144624 | 145483.75 | 143531 | 145366.25 | | 64 | 145459.25 | 146175.75 | 147174.25 | 146622.5 | | 128 | 145625.75 | 143823.25 | 144132 | 144739.25 | | 256 | 145380.75 | 147224 | 146203.25 | 147078.75 | | 512 | 146045 | 145609.25 | 145149.25 | 144335.5 | | 1024 | 148280 | 148076 | 145593.25 | 146534.75 | +-------------+-----------+------------+-----------+--------------+ | Total | 1454769 | 1457884.75 | 1454310 | 1456688.75 | +-------------+-----------+------------+-----------+--------------+
Тестовый сценарий, который я использовал, можно найти здесь
источник
Лучший способ узнать - проверить производительность обеих версий и проверить план выполнения для обеих версий. Выберите таблицу с большим количеством столбцов.
источник
В SQL Server нет разницы, и в SQL Server это никогда не было проблемой. Оптимизатор знает, что они одинаковы. Если вы посмотрите на планы выполнения, вы увидите, что они идентичны.
источник
Лично мне очень и очень трудно поверить, что они не оптимизируются под один и тот же план запроса. Но единственный способ узнать это в вашей конкретной ситуации - это протестировать. Если да, сообщите об этом!
источник
Никакой реальной разницы, но может быть очень небольшое снижение производительности. Как показывает практика, вы не должны запрашивать больше данных, чем вам нужно.
источник