Мы столкнулись с интересной проблемой с SQL Server. Рассмотрим следующий пример repro:
CREATE TABLE #test (s_guid uniqueidentifier PRIMARY KEY);
INSERT INTO #test (s_guid) VALUES ('7E28EFF8-A80A-45E4-BFE0-C13989D69618');
SELECT s_guid FROM #test
WHERE s_guid = '7E28EFF8-A80A-45E4-BFE0-C13989D69618'
AND s_guid <> NEWID();
DROP TABLE #test;
Пожалуйста, забудьте на мгновение, что s_guid <> NEWID()
условие кажется совершенно бесполезным - это всего лишь минимальный пример воспроизведения. Поскольку вероятность NEWID()
совпадения некоторого заданного значения константы чрезвычайно мала, она должна каждый раз оцениваться как ИСТИНА.
Но это не так. Выполнение этого запроса обычно возвращает 1 строку, но иногда (довольно часто, более 1 раза из 10) возвращает 0 строк. Я воспроизвел его с помощью SQL Server 2008 в моей системе, и вы можете воспроизвести его в режиме онлайн с помощью скрипта, указанного выше (SQL Server 2014).
Изучение плана выполнения показывает, что анализатор запросов, по-видимому, разбивает условие на s_guid < NEWID() OR s_guid > NEWID()
:
... что полностью объясняет, почему иногда происходит сбой (если первый сгенерированный идентификатор меньше, а второй больше указанного идентификатора).
Можно ли оценивать SQL Server A <> B
как A < B OR A > B
, даже если одно из выражений недетерминировано? Если да, где это задокументировано? Или мы нашли ошибку?
Интересно, что AND NOT (s_guid = NEWID())
дает тот же план выполнения (и тот же случайный результат).
Мы обнаружили эту проблему, когда разработчик хотел по желанию исключить конкретную строку и использовал:
s_guid <> ISNULL(@someParameter, NEWID())
как «ярлык» для:
(@someParameter IS NULL OR s_guid <> @someParameter)
Я ищу документацию и / или подтверждение ошибки. Код не так уж важен, поэтому обходные пути не требуются.
источник
Ответы:
Это несколько спорный момент, и ответом является квалифицированное «да».
Лучшее обсуждение, о котором я знаю, было дано в ответе на сообщение об ошибке Connect Ицик Бен-Гана «Ошибка с NEWID» и «Выражения таблиц» , которое было закрыто, так как не будет исправлено. С тех пор Connect был удален, поэтому есть ссылка на веб-архив. К сожалению, много полезного материала было потеряно (или стало труднее найти) после кончины Connect. Во всяком случае, самые полезные цитаты из Джима Хогга из Microsoft:
Одним из примеров изменения поведения в этом отношении во времени является то, что NULLIF некорректно работает с недетерминированными функциями, такими как RAND () . Существуют также другие подобные случаи, использующие, например,
COALESCE
подзапрос, который может привести к неожиданным результатам и который также решается постепенно.Джим продолжает:
Это является следствием нормализации, которая происходит очень рано во время компиляции запроса. Оба выражения компилируются в одну и ту же нормализованную форму, поэтому создается один и тот же план выполнения.
источник
Это задокументировано (вроде) здесь:
Пользовательские функции
Это не единственная форма запроса, в которой план запроса будет выполнять NEWID () несколько раз и изменять результат. Это сбивает с толку, но на самом деле очень важно, чтобы NEWID () был полезен для генерации ключей и случайной сортировки.
Больше всего сбивает с толку то, что не все недетерминированные функции на самом деле ведут себя так. Например, RAND () и GETDATE () будут выполняться только один раз за запрос.
источник
=
,<
и>
может быть эффективно оценена с BTree.Для чего стоит, если вы посмотрите на этот старый стандартный документ SQL 92 , требования к неравенству описаны в разделе «
8.2 <comparison predicate>
» следующим образом:Примечание: я включил 7b и 7h для полноты, так как они говорят о
<>
сравнении - я не думаю, что сравнение конструкторов значений строк с несколькими значениями реализовано в T-SQL, если только я не совсем неправильно понимаю, что это говорит - что вполне возможноЭто куча непонятного мусора. Но если вы хотите продолжить погружение в мусорный контейнер ...
Я думаю, что 1.ii - это элемент, который применяется в этом сценарии, поскольку мы сравниваем значения «элементов конструктора значений строк».
В основном это говорит, что
X <> Y
истина, если значения, представленные X и Y, не равны. ПосколькуX < Y OR X > Y
это логически эквивалентное переписывание этого предиката, оптимизатор может использовать его.Стандарт не накладывает никаких ограничений на это определение, связанное с детерминированностью (или чем бы то ни было, вы получаете это) элементов конструктора значения строки по обе стороны от
<>
оператора сравнения. Пользовательский код отвечает за то, что выражение значения на одной стороне может быть недетерминированным.источник