Как избежать ошибки деления на ноль в SQL?

У меня такое сообщение об ошибке:

Сообщение 8134, уровень 16, состояние 1, строка 1 Обнаружена ошибка деления на ноль.

Как лучше всего написать код SQL, чтобы я больше никогда не увижу это сообщение об ошибке?

Я мог сделать одно из следующих действий:

  • Добавьте предложение where, чтобы мой делитель никогда не был равен нулю

Or

  • Я мог бы добавить оператор case, чтобы для нуля была особая обработка.

Лучше всего использовать предложение NULLIF?

Есть ли лучший способ или как это можно сделать?


person Henrik Staun Poulsen    schedule 14.05.2009    source источник
comment
Возможно, потребуется некоторая проверка данных.   -  person Anthony    schedule 14.05.2009


Ответы (19)


Чтобы избежать ошибки «Деление на ноль», мы запрограммировали это так:

Select Case when divisor=0 then null
Else dividend / divisor
End ,,,

Но вот способ сделать это гораздо лучше:

Select dividend / NULLIF(divisor, 0) ...

Теперь единственная проблема - запомнить бит NullIf, если я использую клавишу «/».

person Henrik Staun Poulsen    schedule 14.05.2009
comment
Намного более приятный способ сделать это Выбрать делимое / nullif (divisor, 0) ... прерывается, если divisor равен NULL. - person Anderson; 01.12.2014
comment
@Anderson Это совсем не так. Вы уверены, что случайно не использовали IsNull вместо NullIf? Попробуй сам! SELECT Value,1/NullIf(Value,0)FROM(VALUES(0),(5.0),(NULL))x(Value); Разве что под перерывами вы имеете в виду, что возвращает NULL? Вы можете преобразовать это во что угодно с помощью IsNull или Coalesce. - person ErikE; 26.01.2017
comment
@ErikE, это правда ... попробуйте запустить ... выберите 1 / nullif (null, 0) ... вы получите Тип первого аргумента для NULLIF не может быть константой NULL, потому что тип первого аргумента должен быть быть известным. Обработайте это с помощью coalesce (FieldName, 0) ... например. выберите 1 / nullif (coalesce (null, 0), 0) - person John Joseph; 03.05.2019
comment
@JohnJoseph Я не могу сказать, согласны ли вы со мной или спорите со мной. - person ErikE; 03.05.2019
comment
@JohnJoseph Присмотритесь к полученной вами ошибке. Да, SELECT 1 / NULLIF(NULL, 0) не работает, но это потому, что NULLIF() нужно знать тип данных первого аргумента. Этот измененный пример отлично работает: SELECT 1 / NULLIF(CAST(NULL AS INT), 0). В реальной жизни вы собираетесь предоставить столбец таблицы в NULLIF(), а не в константу NULL. Поскольку столбцы таблицы имеют известные типы данных, это тоже нормально работает: SELECT 1 / NULLIF(SomeNullableColumn, 0) FROM SomeTable. - person MarredCheese; 06.06.2019
comment
Как вы можете получить делитель нуля, если вы не знаете тип данных, если вы не наберете буквально null, как в примере, предоставленном @JohnJoseph? Если он поступает из таблицы данных или переменной, он будет иметь определенный тип. Если я чего-то не упускаю, похоже, что единственный способ сломать это - сделать это намеренно. - person RPh_Coder; 05.03.2021

Если вы хотите вернуть ноль, в случае, если произойдет нулевое отклонение, вы можете использовать:

SELECT COALESCE(dividend / NULLIF(divisor,0), 0) FROM sometable

Для каждого делителя, равного нулю, вы получите ноль в результирующем наборе.

person Tobias Domhan    schedule 04.01.2012
comment
Некоторые тесты показывают, что COALESCE немного медленнее, чем ISNULL. Однако COALESCE соответствует стандартам, поэтому он более портативен. - person Paul Chernoch; 12.07.2012
comment
Если кто-то еще не сразу понял, почему это работает, NULLIF (d, 0) вернет NULL, если d равно 0. В SQL деление на NULL возвращает NULL. Coalesce заменяет результирующий NULL на 0. - person GuiSim; 16.04.2014
comment
Также обратите внимание, что он возвращает 0, когда делимое или делитель равно NULL, тогда как по стандарту он возвращает NULL. - person user824276; 07.03.2015
comment
ПОЖАЛУЙСТА! Я шокирован тем, что за него проголосовали 71 человек! Деление на ноль НЕОПРЕДЕЛЕННО и не должно НИКОГДА возвращать НУЛЬ! Я очень редко голосую против, но это действительно чушь! По крайней мере, удалите объединение, тогда деление вернет NULL вместо Zero, так лучше. - person SQL Police; 26.11.2015
comment
@SQLGeorge. Хотя я согласен с вашим аргументом, обратите внимание, что есть случаи, когда человек больше заботится о том, что является статистически правильным, чем математически правильным. В некоторых случаях при использовании статистических функций приемлемым результатом является 0 или даже 1, когда делитель равен нулю. - person Athafoud; 03.02.2016
comment
Подобные взломы стоили людям денег, лишали людей работы и, возможно, подвергали опасности жизни. Такой ответ нельзя принимать ни в какой форме. Если вы хотите вернуть 0 (или 1), значит, вы делаете что-то не так или задаете неправильный вопрос. (Anything / 0) Если это на самом деле 0, значит, вы делаете что-то не так, и он возвращает ошибку, как и должно. Это означает, что вам нужно пройти по очереди и выяснить, почему вы получаете 0 или приемлемо с возвращением null. 0 никогда не является правильным в этом случае. - person James Wilson; 07.03.2016
comment
@JamesWilson Полностью с вами согласен. Этот ответ следует запретить в Stackoverflow. Это взлом, а не продуманное программирование. - person SQL Police; 06.07.2016
comment
Может кто-нибудь объяснить мне, почему это плохо? Если я пытаюсь вычислить процент, а делитель равен нулю, я определенно хочу, чтобы результат был нулевым. - person Todd Sharp; 17.08.2016
comment
Я думаю, что @George и @ James / Wilson в корне неправильно понимают задаваемый вопрос. Безусловно, существуют бизнес-приложения, в которых уместно возвращать 0, даже если это технически неверно с математической точки зрения. - person Sean Branchaw; 27.09.2016
comment
@SeanBranchaw Приведите пример, где 0 в качестве результата будет уместным. Вы непременно пишете, поэтому у вас непременно есть готовый пример. - person SQL Police; 02.10.2016
comment
@George Конкретным примером, который привел меня на эту страницу, был трекер квалификации, вычисляющий, сколько значений, соответствующих определенным критериям, было выбрано из всех выбранных значений. Прежде чем пользователь ввел какие-либо данные, начальное состояние для расчета - 0/0, поскольку значения не были выбраны. Хотя это, очевидно, технически неопределенно, рассказывать менеджеру об этом не особо полезно. Следующим выбором может быть возврат null или N / A, но это не только сбивает с толку визуальное понимание на панели инструментов, но и приложение, которое я использую, не очень гибкое. Почему вы не согласны? - person Sean Branchaw; 19.10.2016
comment
@SeanBranchaw Я согласен, если ваш пользовательский интерфейс не может обрабатывать NULL или N / A, и если вы не можете изменить пользовательский интерфейс, тогда, конечно, вы должны что-то сделать. Но это обходной путь, а не решение. Решение означало бы изменение пользовательского интерфейса таким образом, чтобы он мог правильно обрабатывать деление на ноль. Но ОП не сообщала о такой ситуации. Он только хотел, чтобы его запрос не останавливал свое выполнение при делении на ноль. - person SQL Police; 23.10.2016
comment
Если вы дадите 5 яблок 0 людям, сколько яблок у них есть? Ответ - 0, у 0 человек есть 0 из 5 яблок, которые у вас есть. Следовательно, правильный ответ - 0. С математической точки зрения, деление на 0 всегда должно возвращать 0. Мне не нравятся аргументы, говорящие, что это невозможно - это просто правило, используемое математикой, чтобы математика работала. Можно ли дать 5 яблок 0 человек? Да, но они перестали бы существовать в контексте. - person markthewizard1234; 03.03.2017
comment
@ markthewizard1234 Более подходящий способ сформулировать свой сценарий (чтобы он явно предполагал деление на 0 вместо того, чтобы обходить его стороной): 5 яблок были поровну разделены между 0 людьми. Сколько яблок у каждого человека? Вразумительного ответа на этот вопрос просто нет. Еще более абсурдно представить себе разделение объекта на 0 частей. Ясно, что позволить математике работать невозможно, несмотря на отсутствие мотивации. - person MarredCheese; 06.06.2019
comment
Есть причина, по которой x / 0 или даже 0/0 не равно 0. Бизнесмен продает товар. Цена постоянно меняется. Чтобы увидеть, какова была цена в среднем каждый месяц, он рассчитывает: доход / количество заказов в месяц. Допустим, он ничего не продавал в марте, и мы хотим рассчитать среднее значение за первый квартал. Правильно: (20, 30, null) = ›50/2 = 25. Неправильно: (20, 30, 0) =› 50/3 = 16,67. (Обратите внимание, что вычислять среднее значение в два этапа таким способом некорректно. Это не средняя цена, которую бизнесмен получил в течение этого квартала. Это скорее предположение о средней цене продукта.) - person noox; 14.06.2019

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

Предположим, вы хотите рассчитать соотношение мужчин и женщин для различных школьных клубов, но обнаруживаете, что следующий запрос не работает и выдает ошибку деления на ноль при попытке вычислить соотношение для клуба "Властелин колец", в котором нет женщин. :

SELECT club_id, males, females, males/females AS ratio
  FROM school_clubs;

Вы можете использовать функцию NULLIF, чтобы избежать деления на ноль. NULLIF сравнивает два выражения и возвращает null, если они равны, или первое выражение в противном случае.

Перепишите запрос как:

SELECT club_id, males, females, males/NULLIF(females, 0) AS ratio
  FROM school_clubs;

Любое число, разделенное на NULL, дает NULL, и никаких ошибок не возникает.

person frank    schedule 17.12.2013
comment
Да, действительно, это НАМНОГО ЛУЧШЕ, чем тот другой ответ, за который так много положительных голосов. В вашем решении у вас есть как минимум NULL, что означает, что вы не можете предоставить правильный результат. Но если вы конвертируете результат из NULL в Zero, вы просто получите неправильные и вводящие в заблуждение результаты. - person SQL Police; 26.11.2015
comment
Кстати, если вы хотите рассчитать соотношение мужчин и женщин, я предлагаю лучше сравнить его с общим итогом, например: select males/(males+females), females/(males+females). Это даст вам процентное распределение мужчин и женщин в клубе: 31% мужчин и 69% женщин. - person SQL Police; 26.11.2015

Вы также можете сделать это в начале запроса:

SET ARITHABORT OFF 
SET ANSI_WARNINGS OFF

Поэтому, если у вас есть что-то вроде 100/0, он вернет NULL. Я сделал это только для простых запросов, поэтому не знаю, как это повлияет на более длинные / сложные.

person Taz    schedule 30.06.2011
comment
Работает для меня. В моем случае я должен использовать операцию разделения в предложении WHERE. Я уверен, что нулевого делителя нет, потому что, когда я комментирую WHERE out, в результатах нет нулевых значений. Но почему-то оптимизатор запросов при фильтрации делит на ноль. SET ARITHABORT OFF SET и ANSI_WARNINGS OFF работают - после 2 дней борьбы с делением на ноль в предложении WHERE. Спасибо! - person huhu78; 27.09.2012
comment
Это кажется таким грязным, но мне это нравится! Это было необходимо в запросе, который выполняет агрегирование, и использование оператора CASE не было вариантом, потому что тогда мне пришлось добавить этот столбец в GROUP BY, что полностью изменило результаты. Выполнение начального запроса как подзапроса, а затем выполнение GROUP BY для внешнего запроса также изменяет результаты, потому что есть разделение. - person Andrew Steitz; 02.03.2013
comment
Хорошо, мне все еще нравится это решение, но, как многие из вас, вероятно, чувствовали, я чувствовал, что должен быть более чистый способ. Что делать, если я забыл снова включить предупреждения? Или кто-то закрыл мой код (этого никогда не бывает, правда?) И не подумал о предупреждениях? Во всяком случае, видел другие ответы о NULLIF (). Я знал о NULLIF (), но не понимал, что деление на NULL возвращает NULL (я думал, что это будет ошибка). Итак ... Я выбрал следующее: ISNULL ((SUM (foo) / NULLIF (SUM (bar), 0)), 0) AS Avg - person Andrew Steitz; 02.03.2013
comment
Я не знал этого решения. Не уверен, что мне это нравится, но когда-нибудь, возможно, будет полезно узнать об этом. Большое тебе спасибо. - person Henrik Staun Poulsen; 18.12.2013
comment
Это самое простое решение, но учтите, что оно снизит производительность. Из docs.microsoft.com / en-us / sql / t-sql / applications /: установка для ARITHABORT значения OFF может негативно повлиять на оптимизацию запросов, что приведет к проблемам с производительностью. - person mono blaine; 31.10.2017

Вы можете хотя бы предотвратить прерывание запроса с ошибкой и вернуть NULL, если есть деление на ноль:

SELECT a / NULLIF(b, 0) FROM t 

Однако я бы НИКОГДА не преобразовал это значение в ноль с помощью coalesce, как показано в том другом ответе, который получил много голосов. Это совершенно неверно с математической точки зрения и даже опасно, поскольку ваше приложение, скорее всего, вернет неправильные и вводящие в заблуждение результаты.

person SQL Police    schedule 26.11.2015

РЕДАКТИРОВАТЬ: Я получаю много отрицательных голосов по этому поводу в последнее время ... поэтому я подумал, что просто добавлю примечание, что этот ответ был написан до того, как вопрос подвергся самому последнему редактированию, где возвращение null было выделено как вариант .. ., что кажется очень приемлемым. Часть моего ответа была адресована таким опасениям, как Эдвардо в комментариях, который, казалось, выступал за возврат 0. Это тот случай, против которого я выступал.

ОТВЕТ: Я думаю, здесь есть основная проблема: деление на 0 незаконно. Это признак того, что что-то не так. Если вы делите на ноль, вы пытаетесь сделать что-то, что не имеет математического смысла, поэтому никакой числовой ответ, который вы можете получить, не будет действительным. (Использование null в этом случае является разумным, поскольку это значение не будет использоваться в более поздних математических вычислениях).

Итак, Эдвардо спрашивает в комментариях: «А что, если пользователь поставит 0?», И выступает за то, чтобы получить 0 взамен было нормально. Если пользователь ставит ноль в сумму, и вы хотите, чтобы 0 возвращался, когда они это делают, тогда вам следует ввести код на уровне бизнес-правил, чтобы поймать это значение и вернуть 0 ... нет какого-то особого случая, когда деление на 0 = 0.

Это тонкая разница, но она важна ... потому что в следующий раз, когда кто-то вызовет вашу функцию и ожидает, что она будет делать правильные вещи, и она сделает что-то необычное, что не является математически правильным, а просто обработает конкретный крайний случай, он получит хороший шанс укусить кого-нибудь позже. На самом деле вы не делите на 0 ... вы просто возвращаете плохой ответ на плохой вопрос.

Представьте, что я что-то кодирую, и я облажался. Я должен был прочитать масштабное значение измерения радиации, но в странном крайнем случае, которого я не ожидал, я прочитал 0. Затем я опускаю свое значение в вашу функцию ... вы возвращаете мне 0! Ура, без радиации! За исключением того, что это действительно так, и я просто передавал плохую ценность ... но я понятия не имею. Я хочу, чтобы деление выдавало ошибку, потому что это признак того, что что-то не так.

person Beska    schedule 14.05.2009
comment
Я не согласен. Ваши бизнес-правила никогда не должны приводить к неправильной математике. Если вы в конечном итоге сделаете что-то подобное, скорее всего, ваша модель данных неверна. Всякий раз, когда вы сталкиваетесь с делением на 0, вы должны задуматься, должны ли данные быть NULL вместо 0. - person Remus Rusanu; 14.05.2009
comment
Я не могу поверить, что меня отверг кто-то, кто спрашивает, занимаюсь ли я когда-либо настоящим программированием? потому что я говорю делать это правильно, а не лениться. вздох - person Beska; 14.05.2009
comment
Прости, я не хотел тебя обидеть. Но этот вопрос совершенно справедлив во многих распространенных LOB-приложениях, и ответ на него с делением на 0 незаконен, не добавляет ценности ИМХО. - person Eduardo Molteni; 15.05.2009
comment
как лучше всего гарантировать, что мы всегда учитываем ноль при написании символа косой черты? - person Henrik Staun Poulsen; 18.05.2009
comment
@ Эдуардо ... но это моя точка зрения. Там тоже не действует. Иногда может случиться так, что есть крайний случай, или неправильный ввод пользователя, или что-то в этом роде. В этом нет никаких сомнений ... и с этим нужно разобраться. Но моя точка зрения (которую, я полагаю, я не очень хорошо понял) заключается в том, что это должно обрабатываться бизнес-логикой заранее ... захватывать эти плохие / крайние / любые данные и отмечать их. Потому что вы действительно не хотите делить на 0, что невозможно; вы просто хотите, чтобы эти лишние дела обрабатывались элегантно. Я не думаю, что этого не следует делать на уровне SQL. - person Beska; 18.05.2009
comment
Хенрикстаунпульсен: Я думаю, что лучше всего в целом попытаться справиться с этим вне SQL. Если вы передаете 0, значит, у вас должен быть странный крайний случай, или у вас плохие данные, или что-то в этом роде ... и поэтому в любом случае нет смысла переходить на уровень SQL. Просто пусть логика кода вернет код ошибки, или ноль, или что-то еще. Затем вы можете оставить свой код SQL как можно более чистым, и если вы когда-нибудь действительно увидите исходящую оттуда ошибку / 0, это будет хорошим предупреждением о том, что вы не поймаете случай, который вам следует будет ... это будет скорее хорошо, чем плохо. - person Beska; 18.05.2009
comment
@Remus Я часто сталкиваюсь с этим, сообщая среднюю продажную цену транзакций. Возможны отрицательные транзакции, и если продажа +1 имеет цену продажи, отличную от -1, и они единственные 2, ASP математически не определен. Однако бизнес определяет результат в этом случае как «показать пустое место для ASP». Я не уверен, что это противоречит вашим бизнес-правилам. Ваши бизнес-правила никогда не должны приводить к неправильному математическому правилу, но мне оно кажется достаточно верным? - person ; 06.12.2012
comment
@JackDouglas Верно. Это тот случай, когда вы хотите, чтобы бизнес-правила обрабатывали особый случай особым образом ... но это не должно быть математическое значение, возвращающее пустое значение. Это должно быть бизнес-правилом. Принятый ответ возвращает значение null, что является нормальным способом его обработки. Также можно выбросить исключение. Пожалуй, идеальным вариантом было бы обнаружить его и обработать до того, как он перейдет в SQL. Предоставление какой-либо функции, которую могут вызывать другие объекты, возвращающей математически неверное значение, - это не путь, потому что этот особый случай может не применяться к другим вызывающим объектам. - person Beska; 06.12.2012
comment
@Beska Я бы сказал, что единственный нормальный способ справиться с этим - это тот способ, который соответствует вашим бизнес-правилам, и, учитывая вопрос, это не обязательно включает возврат нуля, поскольку OP не предоставляет большого контекста! Конечно, я согласен с тем, что непонимание проблемы, помимо сообщения об ошибке, от которого мне нужно избавиться, - это помешательство :) - person ; 06.12.2012
comment
@JackDouglas Да, это хорошее резюме, с которым я согласен. Первоначально вопрос, казалось, был сформулирован так: что я могу сделать, чтобы просто скрыть эту ошибку. С тех пор он эволюционировал. Возвращение нуля, ответ, к которому он в конце концов приходит, кажется одним разумным ответом. (Я настоятельно рекомендовал не возвращать 0 или другое число.) - person Beska; 14.03.2013
comment
Разве вы не ненавидите движущиеся цели :) Я заметил, что вы подписаны на большинство сайтов SE, но не на dba.se: некоторые из ваших вопросов по базе данных SO также будут там по теме, мы будем рады, если вы присоединитесь нас ;) - person ; 14.03.2013
comment
Спасибо! Наконец-то здесь кто-то говорит правду. Деление на ноль никогда не должно возвращать ноль. Это просто неправильно и приводит к ошибочным результатам. Как вы говорите, если вы используете нулевой результат для дальнейшей обработки в другой функции, вы распространите ошибку и получите больше неправильных функций. - person SQL Police; 26.11.2015
comment
@EduardoMolteni Золотое правило программирования: ВСЕГДА ДОСТАВЛЯЙТЕ ПРАВИЛЬНЫЕ РЕЗУЛЬТАТЫ. Я занимаюсь этим бизнесом почти 30 лет и могу вам сказать: если вы немного небрежны, это вернется к вам бумерангом. И это сильно ударит по тебе. Поэтому всегда делайте все возможное и старайтесь писать каждую функцию как можно точнее и надежнее. - person SQL Police; 26.11.2015
comment
@Beska Я вижу твой вздох. Я также не могу понять, как этот другой ответ, который дает 0 в результате, когда-либо получил 70 голосов. это меня шокирует, потому что программисты должны стремиться писать правильные программы, а не ленивый и небрежный код. - person SQL Police; 26.11.2015
comment
@ Джордж. Вы совершенно правы, когда требуется разделение. Однако в некоторых случаях деление применяется неправильно. Например,% отклонения x от y. Если и x, и y равны 0, то отклонения нет, поэтому правильным результатом будет 0%. Этот результат достигается без разделения. Итак, вызов для всех, кто считает, что 0/0 = 0 в их ситуации: объясните, что это означает, и посмотрите, имеет ли смысл исключить разделение. Если вы не можете, тогда undefined, NaN или NULL - правильный ответ. - person Tom Blodget; 27.01.2017
comment
@TomBlodget Если отклонение между 0 и 0 составляет 0%, каково тогда отклонение между 800 и 0? 800 $ Продажи в 2016 г., 0 $ Продажи в 2015 г. Какое отклонение? - person SQL Police; 01.02.2017
comment
Отклонение составляет 800 долларов. % Отклонения не определено. Мой пример похож на упрощение алгебраических уравнений; Если разделить на коэффициент, который при определенных условиях может быть равен 0, полученное уравнение не может быть применено к этим условиям. Это не значит, что в этих условиях нет решения. Это не значит, что есть и один, что является серьезной ошибкой, которую совершают люди, избегающие деления на ноль. - person Tom Blodget; 01.02.2017

SELECT Dividend / ISNULL(NULLIF(Divisor,0), 1) AS Result from table

Поймав ноль с помощью nullif (), а затем получившийся нуль с помощью isnull (), вы можете обойти свою ошибку деления на ноль.

person Community    schedule 25.08.2009
comment
Ваш ответ рекомендуется удалить из-за его длины. Обратите внимание, что всегда лучше добавлять небольшое объяснение того, что вы предлагаете, даже если это кажется очень простым;) - person Trinimon; 16.09.2015

Замена «делить на ноль» на ноль спорна, но это также не единственный вариант. В некоторых случаях замена на 1 (разумно) уместна. Я часто использую

 ISNULL(Numerator/NULLIF(Divisor,0),1)

когда я смотрю на сдвиги в счетах / счетах и ​​хочу установить по умолчанию 1, если у меня нет данных. Например

NewScore = OldScore *  ISNULL(NewSampleScore/NULLIF(OldSampleScore,0),1) 

Чаще всего я фактически вычислял это соотношение где-то еще (не в последнюю очередь потому, что он может привести к очень большим поправочным коэффициентам для низких знаменателей. В этом случае я бы обычно проверял, что OldSampleScore больше порогового значения; что затем исключает нулевое значение). Но иногда уместно «взломать».

person N Mason    schedule 24.04.2018
comment
@N Mason; да, иногда вариант 1. Но как запомнить часть ISNULL при вводе обратной косой черты? - person Henrik Staun Poulsen; 24.04.2018
comment
Извини, Хенрик - я не уверен, что понимаю вопрос. - person N Mason; 24.04.2018

Некоторое время назад я написал функцию для ее обработки для моих хранимых процедур:

print 'Creating safeDivide Stored Proc ...'
go

if exists (select * from dbo.sysobjects where  name = 'safeDivide') drop function safeDivide;
go

create function dbo.safeDivide( @Numerator decimal(38,19), @divisor decimal(39,19))
   returns decimal(38,19)
begin
 -- **************************************************************************
 --  Procedure: safeDivide()
 --     Author: Ron Savage, Central, ex: 1282
 --       Date: 06/22/2004
 --
 --  Description:
 --  This function divides the first argument by the second argument after
 --  checking for NULL or 0 divisors to avoid "divide by zero" errors.
 -- Change History:
 --
 -- Date        Init. Description
 -- 05/14/2009  RS    Updated to handle really freaking big numbers, just in
 --                   case. :-)
 -- 05/14/2009  RS    Updated to handle negative divisors.
 -- **************************************************************************
   declare @p_product    decimal(38,19);

   select @p_product = null;

   if ( @divisor is not null and @divisor <> 0 and @Numerator is not null )
      select @p_product = @Numerator / @divisor;

   return(@p_product)
end
go
person Ron Savage    schedule 14.05.2009
comment
Привет, Рон! Хорошее решение, за исключением того, что оно имеет ограниченный тип данных (4 десятичных знака), и наши @divisors также могут быть отрицательными. И как заставить его использовать? TIA Хенрик Стаун Поульсен - person Henrik Staun Poulsen; 14.05.2009
comment
Я довольно быстро набросился на него, чтобы справиться с конкретным сценарием проблемы в то время. Приложение от одного разработчика, поэтому исполнение не так уж и сложно, если не считать моей памяти. :-) - person Ron Savage; 14.05.2009
comment
Несмотря на оператор печати, это не хранимая процедура, это скалярная UDF. Это убьет вас в MS-SQL, если он является частью запроса. - person Mark Sowul; 08.05.2012
comment
Я согласился с утверждением Марка Совула о том, что скалярная функция вызовет боль. Это ужасное предложение в T-SQL, не делайте этого! Скалярные функции разрушают производительность! Встроенные табличные функции - единственные хорошие пользовательские функции в SQL Server (возможно, за исключением функций CLR, которые могут работать хорошо). - person Davos; 26.02.2014
comment
Вы можете заменить всю свою функцию выражением @numerator / NULLIF(@divisor, 0). Он делает то же самое, но намного быстрее. - person SQL Police; 02.09.2020

  1. Добавьте ограничение CHECK, которое заставляет Divisor быть ненулевым
  2. Добавьте в форму валидатор, чтобы пользователь не мог вводить в это поле нулевые значения.
person finnw    schedule 25.08.2009
comment
Мне все больше и больше нравятся ограничения CHECK. - person Henrik Staun Poulsen; 16.08.2010

Для обновления SQL:

update Table1 set Col1 = Col2 / ISNULL(NULLIF(Col3,0),1)
person Vijay Bansal    schedule 16.09.2015
comment
привет Виджай, Да, это сработает, но ... Я был бы осторожен с частью ISNULL, где вы в конечном итоге делите на NULL. Я бы предпочел сигнализировать пользователю, что результат неизвестен, потому что делитель равен нулю. - person Henrik Staun Poulsen; 17.09.2015
comment
Это спасло меня от сложного подзапроса, спасибо. - person QMaster; 20.02.2017

Нет никакой волшебной глобальной настройки «выключить деление на 0 исключений». Операция должна быть выбрана, поскольку математическое значение x / 0 отличается от значения NULL, поэтому она не может вернуть NULL. Я предполагаю, что вы заботитесь об очевидном, и в ваших запросах есть условия, которые должны исключать записи с делителем 0 и никогда не оценивать деление. Обычная «ошибка» - это то, что большинство разработчиков ожидают от SQL, чтобы вести себя как процедурные языки и предлагать короткое замыкание логического оператора, но это НЕ. Я рекомендую вам прочитать эту статью: http://www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html

person Remus Rusanu    schedule 14.05.2009
comment
Есть такая глобальная настройка Magic; ВЫКЛЮЧИТЕ ARITHABORT. - person David Manheim; 01.05.2012

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

Я собираюсь подсчитать количество оборачиваемости запасов за трехмесячный период. Я подсчитал, что у меня стоимость проданных товаров за трехмесячный период составляет 1000 долларов. Годовая норма продаж составляет 4000 долларов США (1000 долларов США / 3 шт.) * 12. Начальный запас равен 0. Конечный запас равен 0. Мой средний запас сейчас равен 0. У меня продажи составляют 4000 долларов в год, а запасов нет. Это дает бесконечное количество оборотов. Это означает, что весь мой инвентарь конвертируется и покупается покупателями.

Это бизнес-правило расчета оборачиваемости запасов.

person Jimmy    schedule 19.04.2010
comment
Да, тогда у вас есть бесконечное количество поворотов. В этом случае, если у вас есть деление на ноль, вы должны показать что-то вроде '#INF'. - person SQL Police; 26.11.2015
comment
Начальный запас равен 0. Конечный запас равен 0. Мой средний запас сейчас равен 0. Ваш расчет является приблизительным. В какой-то момент инвентарь положительный или вы не можете ничего отправить / продать. Если в результате вас не устраивает + ∞, используйте лучшую оценку среднего запаса. - person Tom Blodget; 27.01.2017

Отфильтруйте данные с помощью предложения where, чтобы не получить 0 значений.

person nunespascal    schedule 14.05.2009

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

Например, очень мелко продаваемый предмет, проданный с 2 ​​до 4, и очень крупный предмет, который продается с 1 000 000 до 2 000 000 проданных, могут означать совсем разные вещи для аналитика или руководства, но и то, и другое будет реализовано как 100% или 1. изменение.

Может быть проще изолировать значения NULL, чем просматривать кучу 0% или 100% строк, смешанных с допустимыми данными. Часто 0 в знаменателе может указывать на ошибку или отсутствующее значение, и вы можете не захотеть просто заполнить произвольное значение только для того, чтобы ваш набор данных выглядел аккуратно.

CASE
     WHEN [Denominator] = 0
     THEN NULL --or any value or sub case
     ELSE [Numerator]/[Denominator]
END as DivisionProblem
person Joeyslaptop    schedule 27.11.2018
comment
Я считаю, что проблема в том, чтобы не забывать делать что-то всякий раз, когда вы хотите сделать разделение. Если вы не помните, что добавили CASE или NULLIF, вы получите запрос в службу поддержки через x недель, в понедельник утром. Я ненавижу это. - person Henrik Staun Poulsen; 28.11.2018

Вот как я это исправил:

IIF(ValueA != 0, Total / ValueA, 0)

Его можно завернуть в апдейт:

SET Pct = IIF(ValueA != 0, Total / ValueA, 0)

Или в избранном:

SELECT IIF(ValueA != 0, Total / ValueA, 0) AS Pct FROM Tablename;

Мысли?

person CorvetteGuru    schedule 16.07.2019
comment
Я и другие находим, что замена unknown на ноль - опасное решение. Я предпочитаю NULL. Но сложнее всего не забыть добавить iif или nullif для всех разделов! - person Henrik Staun Poulsen; 16.07.2019

Вы можете обработать ошибку соответствующим образом, когда она передается обратно в вызывающую программу (или игнорировать ее, если вы этого хотите). В C # любые ошибки, возникающие в SQL, вызовут исключение, которое я могу перехватить и затем обработать в своем коде, как и любую другую ошибку.

Я согласен с Beska в том, что вы не хотите скрывать ошибку. Возможно, вы имеете дело не с ядерным реактором, но в целом скрывать ошибки - плохая практика программирования. Это одна из причин, по которой большинство современных языков программирования реализуют структурированную обработку исключений, чтобы отделить фактическое возвращаемое значение от кода ошибки / состояния. Это особенно верно, когда вы занимаетесь математикой. Самая большая проблема заключается в том, что вы не можете различить правильно вычисленный 0, возвращаемый в результате ошибки. Вместо этого любое возвращаемое значение является вычисленным значением, и если что-то пойдет не так, выдается исключение. Это, конечно, будет отличаться в зависимости от того, как вы обращаетесь к базе данных и какой язык вы используете, но вы всегда должны иметь возможность получить сообщение об ошибке, с которым вы можете справиться.

try
{
    Database.ComputePercentage();
}
catch (SqlException e)
{
    // now you can handle the exception or at least log that the exception was thrown if you choose not to handle it
    // Exception Details: System.Data.SqlClient.SqlException: Divide by zero error encountered.
}
person Despertar    schedule 29.06.2012
comment
Я думаю, мы все согласны с тем, что скрытие ошибки с помощью 0 - не решение. Я предлагаю написать наш код так, чтобы за каждым / следовало NULLIF. Таким образом, мой отчет / ядерный реактор не остается в покое, а показывает NULL вместо этой надоедливой ошибки Msg 8134. Конечно, если мне нужен другой процесс, когда делитель равен 0, то мы с Беской соглашаемся. Нам нужно это закодировать. Это просто сложно делать каждый раз, когда вы пишете /. / NULLIF невозможно делать каждый раз. - person Henrik Staun Poulsen; 27.01.2017

Используйте NULLIF(exp,0), но так - NULLIF(ISNULL(exp,0),0)

NULLIF(exp,0) прерывается, если exp равно null, но NULLIF(ISNULL(exp,0),0) не прерывается

person Johnny Kancharla    schedule 26.01.2017
comment
Я не могу получить NULLIF (exp, 0), если 0 - это ноль, а не o. Пытаться; ОБЪЯВИТЕ @i INT SELECT 1 / NULLIF (@i, 0), чтобы узнать, можно ли заставить его сломаться. - person Henrik Staun Poulsen; 27.01.2017

person    schedule
comment
Мне не нравится ваше решение, потому что использование UDF заставляет запрос выполняться в однопоточном режиме. Мне нравится ваша тестовая установка. Я бы хотел, чтобы это было во всех наших UDF. - person Henrik Staun Poulsen; 09.09.2016
comment
Для меня это решение идеально и элегантно - person Payedimaunt; 26.01.2017
comment
@Payedimaunt; да, UDF приводят к очень элегантному коду. Но он не работает. Это курсоры на снотворное. :-) Также сложно не забыть написать dbo.Divide вместо обычного / - person Henrik Staun Poulsen; 27.01.2017