Почему это оконное выражение не приводит к ошибке деления на ноль? - PullRequest
9 голосов
/ 11 марта 2019

Я сталкивался с этим ответом по программированию головоломок и Code Golf. В нем автор использует выражение (хотя с тех пор ответ был отредактирован для использования другого решения):

row_number()over(order by 1/0)

Я бы ожидал, что 1/0 приведет к исключению деления на ноль, но это не так.

Когда я спросил автора о PPCG, они ответили, «потому что 1/0 не рассчитывается. where exists(select 1/0) будет иметь тот же эффект». Это оставляет меня немного в замешательстве, потому что where exists(select 1) является допустимым синтаксисом, а row_number()over(order by 1) - нет. Так почему же выражение в order by не рассчитано? Результатом выражения является целое число, и целое число не допускается в order by. Как SQL Server обрабатывает order by в этом случае? Я предполагаю, что эффект такой же, как row_number()over(order by (SELECT NULL)), но если мы дадим выражение, я ожидаю, что это выражение будет оценено.

По совпадению, если кто-то использует что-то вроде:

SELECT  ROW_NUMBER() OVER ( ORDER BY A.x )
FROM    (
            SELECT  *
            ,       1 / 0 x
            FROM    master..spt_values
        ) A

Опять же, об ошибке деления на ноль не сообщается (естественно, если столбец x не выбран). Итак, почему это разрешено, когда целое число не является?

Ответы [ 4 ]

6 голосов
/ 11 марта 2019

Вам не разрешено целое число литералы в этом ORDER BY, но вы можете выражений возвращать целые числа - в противном случае вы не сможете использовать, например, CASE выражений.

Так вот почему вам не разрешено OVER (ORDER BY 1), но, по-видимому, разрешено OVER (ORDER BY 1/0).Я не думаю, что это преднамеренная возможность, чтобы позволить вам написать это.Вы, конечно, не можете делить на ноль, если ваше выражение на самом деле зависит от каких-либо столбцов из строк - это приводит к ошибке:

select name,object_id,ROW_NUMBER() OVER (ORDER BY 1/(object_id-3))
from sys.objects

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

4 голосов
/ 12 марта 2019

Давайте попробуем еще несколько примеров ...


ROW_NUMBER() OVER (ORDER BY 2/1)

Оконные функции и функции NEXT VALUE FOR не поддерживают целое число индексы как выражения предложения ORDER BY.

Проблема с 2/1 заключается в том, что он рано сворачивается до 2 в начале процесса оптимизации, поэтому обрабатывается так же, как ROW_NUMBER() OVER (ORDER BY 2), что недопустимо.


ROW_NUMBER() OVER (ORDER BY LOG(1))

Оконные функции и функции NEXT VALUE FOR не поддерживают константы как выражения предложения ORDER BY.

Снова включается постоянное свертывание - на этот раз результат не является целочисленным индексом, но SQL Server все равно не разрешает константы.


ROW_NUMBER() OVER (ORDER BY LOG(-1))

Это успешно выполняется в последних версиях SQL Server - в старых версиях, таких как SQL Server 2008, вы увидите An invalid floating point operation occurred.. Этот конкретный случай упоминается в контексте CASE здесь . Свертывание постоянной времени компиляции нарушило семантику CASE, и это было исправлено в более поздних версиях.

Подавление постоянного свертывания в этих случаях ошибок (LOG(-1) и 1/0) достаточно, чтобы обойти проверки, которые выдают сообщения об ошибках выше. Однако SQL Server по-прежнему признает, что выражение на самом деле является константой и может быть оптимизировано позже (поэтому вы не получите операцию сортировки, чтобы упорядочить строки по результату этого выражения).

Во время фазы упрощения ROW_NUMBER ORDER BY non_folded_const_expression упрощается до ROW_NUMBER и non_folded_const_expression удаляется из дерева, поскольку на него больше нет ссылок. Таким образом, это не вызывает проблем во время выполнения, поскольку даже не существует в окончательном плане выполнения.

1 голос
/ 11 марта 2019

Это обоснованное предположение.

SQL Server оптимизирует постоянные выражения на этапе компиляции запроса.При некоторых обстоятельствах ошибки в выражениях, такие как деление на ноль, игнорируются, поскольку выражения могут не понадобиться в конечном результате.По любой причине ошибка не помечается как «постоянная».

Ошибки деления на ноль, генерируемые на этапе выполнения, не игнорируются.

Лично,Я никогда не использовал бы бессмысленное выражение, подобное этому, в любом реальном коде (только демонстрационный код).Это возвращает деление на ноль в большинстве других баз данных.

Я бы написал первый запрос как:

row_number() over (order by (select null))

Для exists я использую select 1.

1 голос
/ 11 марта 2019

SQL Server преобразует порядок по значению выражения в константу, а не в фактическую оценку выражения.

Так что в случае целочисленной константы это может быть как положительное, так и отрицательное значение.Следовательно, это не дает ошибки.

Вы также можете проверить это.

Select 'a' as [Test] order by 1/0
...