Divide by zero error encountered перевод

Divide by zero error encountered. 1С УТ Доброго времени суток, уважаемые Мастера. Проблема немного не понятная. В УТ 10.3.12.3, при проведении документа «Поступление товаров и услуг» возникает ошибка: Ошибка при выполнении обработчика — ‘ОбработкаПроведения’ по причине: : Ошибка при вызове метода контекста (Выполнить) по причине: Ошибка выполнения запроса по причине: Ошибка при выполнении операции […]

Содержание

  1. Divide by zero error encountered. 1С УТ
  2. Divide by zero error encountered
  3. About the “Divide by zero error encountered” error message
  4. Different Approaches for Handling the Issue
  5. Approach 1 – Using the “SET ANSI_WARNINGS OFF” Command
  6. Approach 2 – Using the CASE Statement
  7. Approach 3 – Using the NULLIF Function
  8. Concluding Remarks
  9. Featured Online Courses:
  10. Check Some of Our Other SQL Server Articles:
  11. SQL Server divide by zero error encountered – How to fix
  12. Cause for the error ‘SQL Server divide by zero error encountered’
  13. How to solve the error ‘SQL Server divide by zero error encountered’?
  14. Method 1: SQL NULLIF Function
  15. Method 2: Using CASE statement to avoid divide by zero error
  16. Method 3: SET ARITHABORT OFF
  17. Conclusion
  18. Are you using Docker based apps?
  19. Как убрать ошибку 0xC0000094 Integer Divide by Zero?
  20. Как исправить ошибку 0xC0000094?

Divide by zero error encountered. 1С УТ

Доброго времени суток, уважаемые Мастера.
Проблема немного не понятная. В УТ 10.3.12.3, при проведении документа «Поступление товаров и услуг» возникает ошибка:
Ошибка при выполнении обработчика — ‘ОбработкаПроведения’
по причине:
<ОбщийМодуль.ОбщегоНазначения(4191)>: Ошибка при вызове метода контекста (Выполнить)

по причине:
Ошибка выполнения запроса
по причине:
Ошибка при выполнении операции над данными:
Microsoft SQL Server Native Client 10.0: Divide by zero error encountered.
HRESULT=80040E14, SQLSrvr: SQLSTATE=22012, state=1, Severity=10, native=8134, line=1

Знаю, что программа делит на «0», но вот где . Ошибка ссылается на следующую функцию.

Функция СформироватьЗапросПоТабличнойЧасти(ДокументОбъект, ИмяТабличнойЧасти, СтруктураПолей,
СтруктураСложныхПолей = Неопределено) Экспорт

Для Каждого Реквизит Из СтруктураПолей Цикл

ТекстЗапроса = ТекстЗапроса + «,
|Док.» + Реквизит.Значение +
» КАК » + СокрЛП(Реквизит.Ключ);

Запрос = Новый Запрос;

Если ТипЗнч(СтруктураСложныхПолей) = Тип(«Структура») Тогда // Добавим к запросу конструкции.

Для Каждого Элемент Из СтруктураСложныхПолей Цикл

ТекстЗапроса = ТекстЗапроса + «,
| » + Элемент.Значение +
» КАК » + СокрЛП(Элемент.Ключ);

Если СтруктураСложныхПолей.Свойство(«Проект») Тогда

Запрос.УстановитьПараметр(«ДатаДок»,ДокументОбъект.Дата);
КонецЕсли;
КонецЕсли;

Запрос.Текст = «ВЫБРАТЬ
| Док.НомерСтроки » + ТекстЗапроса + »
| ИЗ
| Документ.» + ДокументМетаданные.Имя + «.»+ СокрЛП(ИмяТабличнойЧасти) +
» КАК Док»+ТекстСоединение+»
| ГДЕ Док.Ссылка = &ДокументСсылка»;

// Установим параметры запроса.
Запрос.УстановитьПараметр(«ДокументСсылка» , ДокументОбъект.Ссылка);

Если ДокументОбъект[ИмяТабличнойЧасти].Количество() = 0 Тогда
Запрос.Текст = СтрЗаменить(Запрос.Текст, «ГДЕ Док.Ссылка = &ДокументСсылка», «ГДЕ ЛОЖЬ»);
КонецЕсли;

Источник

Divide by zero error encountered

In this article, we will be discussing about the “Divide by zero error encountered” error message in SQL Server.

About the “Divide by zero error encountered” error message

Well, as the error message says, whenever we have a division within a SQL query and the denominator has the value of zero (0) we get this error.

The exact error we get in SQL Server is:

Msg 8134, Level 16, State 1, Line [Here goes the line of the denominator in your query]
Divide by zero error encountered.

So, which is the best way of addressing this issue? As it is data-related we should be precautious anyway when having a division within a query and effectively control the denominator values for handling the case where a zero might be produced.

Just for reproducing the divide by zero error, consider the following example query:

Different Approaches for Handling the Issue

There are a few approaches of handling such problem. Here I present three.

Approach 1 – Using the “SET ANSI_WARNINGS OFF” Command

By using the “SET ANSI_WARNINGS OFF” command just right before the rest of the queries, it will allow your query that produces the error not to stop the execution of the rest of the queries.

Approach 2 – Using the CASE Statement

By using the CASE statement it is possible to check the denominator value for a zero and if it is so you can use 1 in order for the division not to fail.

Alternatively, you can create a custom scalar-valued function that given an input parameter, it can check for a zero and if it is encountered it can return 1 else it should return the input:

Then you can use the above function as follows:

Strengthen you SQL Server Development Skills – Enroll to our Online Course!

Check our online course titled “ Essential SQL Server Development Tips for SQL Developers
(special limited-time discount included in link).

Via the course, you will sharpen your SQL Server database programming skills via a large set of tips on T-SQL and database development techniques. The course, among other, features over than 30 live demonstrations!

( Lifetime Access / Live Demos / Downloadable Resources and more!)

Approach 3 – Using the NULLIF Function

Yep, by using the NULLIF function it is possible to handle the issue of a zero denominator.
But how? 🙂

The NULLIF function takes two arguments and if they have equal values it then returns a NULL.

The idea here is to compare the denominator value with a zero via NULLIF and if it returns a NULL then to handle it with the ISNULL function (by placing the number 1)!

Which of the above three approaches is the best one? Well, this is up to you 🙂

Personally I do not prefer Approach 1 as it does not solve the problem but rather “says” to SQL Server to ignore it.

So we have Approach 2 and 3 left. Approach 2 looks appealing but still I would only use it with a function.

My personal opinion is that Approach 3 is the best one; it just uses two built-in SQL Server functions and you do not need to write much additional code for handling a zero denominator!

Tip: Also, whenever you have a division in your query keep in mind that if you use only integer variables (like in this example 🙂 and the calculated denominator value is below zero it will return a zero so be careful with that as well (you can use float or decimal instead)!

Featured Online Courses:

  • Introduction to Azure SQL Database for Beginners
  • SQL Server 2019: What’s New – New and Enhanced Features
  • SQL Server Fundamentals – SQL Database for Beginners
  • Essential SQL Server Administration Tips
  • Boost SQL Server Database Performance with In-Memory OLTP
  • Essential SQL Server Development Tips for SQL Developers
  • Working with Python on Windows and SQL Server Databases
  • Introduction to Computer Programming for Beginners
  • .NET Programming for Beginners – Windows Forms with C#
  • Introduction to SQL Server Machine Learning Services
  • Entity Framework: Getting Started – Complete Beginners Guide
  • How to Import and Export Data in SQL Server Databases
  • Learn How to Install and Start Using SQL Server in 30 Mins
  • A Guide on How to Start and Monetize a Successful Blog

Check Some of Our Other SQL Server Articles:

Rate this article: (1 votes, average: 5.00 out of 5)

Artemakis Artemiou is a Senior SQL Server Architect, Author, a 9 Times Microsoft Data Platform MVP (2009-2018). He has over 20 years of experience in the IT industry in various roles. Artemakis is the founder of SQLNetHub and . Artemakis is the creator of the well-known software tools Snippets Generator and DBA Security Advisor. Also, he is the author of many eBooks on SQL Server. Artemakis currently serves as the President of the Cyprus .NET User Group (CDNUG) and the International .NET Association Country Leader for Cyprus (INETA). Moreover, Artemakis teaches on Udemy, you can check his courses here.

Источник

SQL Server divide by zero error encountered – How to fix

by Linsa SV | Apr 20, 2021

Stuck with ‘SQL server divide by zero error encountered’? We can help you.

Recently, one of our customer came across this error as it is not possible to divide a number by zero. It leads to infinity. We perform data calculations in SQL Server for various considerations.

As part of your Server Management Services, we assist our customers with several SQL queries.

Today, let us see how to fix this error.

Cause for the error ‘SQL Server divide by zero error encountered’

Let us see what could cause the error ‘SQL Server divide by zero error encountered’.

To start with, If the product2 quantity goes out of stock and that means we do not have any quantity for product2.

We get SQL divide by zero error messages (message id 8134, level 16):

How to solve the error ‘SQL Server divide by zero error encountered’?

Always, it is a best practice to write code in such a way that it does not give divide by zero message. It should have a mechanism to deal proactively with such conditions.

Moving ahead, let us see an effective methods followed by our Support Techs employ in order to solve this error.

Method 1: SQL NULLIF Function

Initially, we use NULLIF function to avoid divide by zero error message.

The syntax of NULLIF function:

It accepts two arguments.

  • Firstly, If both the arguments are equal, it returns a null value

For example, suppose that the value of both arguments is 10.

SELECT NULLIF(10, 10) result;

In this case, the output will be null.

  • Secondly, If both the arguments are not equal, it returns the value of the first argument.

In this example, both argument values differ. It returns the output as value of first argument 10.

SELECT NULLIF(10, 5) result;

We can modify our initial query using the SQL NULLIF statement. We place the following logic using NULLIF function for eliminating SQL divide by zero error:

  • Use NULLIF function in the denominator with second argument value zero
  • If the value of the first argument is also zero, this function returns a null value. In SQL Server, if we divide a number with null, the output is null as well.
  • If the value of the first argument is not zero, it returns the first argument value and division takes place as standard values.

Execute this modified query. We will get the output as NULL because denominator contains value zero.

If we do not want null value in the output, we can use SQL ISNULL function to avoid null values in the output and display a definite value. This function replaces the null value in the expression1 and returns expression2 value as output.

Method 2: Using CASE statement to avoid divide by zero error

Secondly, you can use a CASE statement in SQL to return values based on specific conditions. The Case statement checks for the value of @Product2 parameter:

  • If the @Product2 value is zero, it returns null.
  • If the above condition is not satisfied, it does the arithmetic operation (@Product1/@Product2) and returns the output.

We will get output as NULL.

Method 3: SET ARITHABORT OFF

By default, SQL Server has a default value of SET ARITHABORT is ON. We get SQL divide by zero error in the output using the default behavior.

The T-SQL syntax for controlling the ARITHABORT option is shown below:

  • Using ARITHABORT ON, the query will terminate with divide by zero message. It is the default behavior.

We get the SQL divide by zero error messages.

  • Using ARITHABORT OFF, the batch will terminate and returns a null value. We need to use ARITHABORT in combination with SET ANSI_WARNINGS OFF to avoid the error message:

We will get the output as NULL.

Finally, you can use the following query to check the current setting for the ARITHABORT parameter:

The default ARITHABORT setting for SQL Server Management Studio (SSMS) is ON. We can view it using SSMS Tools properties. Navigate to Tools -> Options -> Advanced.

We should not modify the value of ARITHABORT unless required. It might create performance issues, as well. It is better to use other methods for avoiding SQL divide by zero error.

[Need assistance? We can help you]

Conclusion

In short, we saw how our Support Techs resolve error ‘SQL Server divide by zero error encountered’

Are you using Docker based apps?

There are proven ways to get even more out of your Docker containers! Let us help you.

Spend your time in growing business and we will take care of Docker Infrastructure for you.

Источник

Как убрать ошибку 0xC0000094 Integer Divide by Zero?

Встречается немало жалоб на ошибку 0xC0000094 и в подавляющем большинстве случаев она связана с Deathloop. Не исключено, что она будет появляться и с другими играми или программами точно по тем же самым причинам. Перед нами относительно новый код сбоя, который мало исследован. Однако на разных площадках есть много упоминаний об одном интересном и рабочем решении ошибки.

Сообщение об ошибке содержит следующий текст:

Crash at launch : Error 0xC0000094 (Integer Divide by Zero)

Еще один вариант:

Crash summary : Error 0xC0000094 (Integer Divide by Zero) occurred in thread ‘DEATHLOOP’ at instruction location 0x00007FF6BC819FCE.

Как исправить ошибку 0xC0000094?

Похоже, что ошибка является следствием недоработок Bethesda в Deathloop. Так как система опирается на некую технологию AES (Advanced Encryption Standard). Речь о технологии шифрования, встроенной в Intel ЦПУ. Как раз неактивность функции и приводит к ошибке.

Как убрать ошибку 0xC0000094:

  1. Перезагружаем компьютер и входим в BIOS. Для этого при старте системы нажимаем Del, F2, F9 или другие клавиши, в зависимости от вашей материнской платы.
  2. В разделе Advanced или Security (может быть и в другом) ищем параметр AES или его словосочетание.
  3. Переводим AES в положение Enabled.
  4. Сохраняем изменения и перезагружаем компьютер.

Есть еще альтернативное решение, хотя это скорее костыль, но подтверждено, что он часто работает. Суть в том, чтобы переключиться на использование встроенного графического адаптера. Корректировки можно внести как в BIOS, так и в «Панели управления NVidia» (а также на аналогичной панели для AMD). В настройках 3D-графики выбираем Deathloop и устанавливаем вариант по умолчанию «Встроенный графический адаптер». При запуске появится предупреждение о малой мощности видеоядра, но игра должна запуститься. Если ЦП оснащен достаточно мощным штатным адаптером, игра будет работать стабильно. Только проверьте, что подключились к HDMI выходу непосредственно от графического процессора, а не через материнскую плату.

Все остальные решения ошибки 0xC0000094 не были подтверждены пользователями, как эффективные. Пока это вся информация, которую нам удалось найти. Как и всегда, последним решением является обращение в техподдержку.

Источник

Ошибка — Warning: Division by zero in, как исправить

Ошибка Division by zero, как исправить ошибку Warning: Division by zero in

division by zero это не ошибка при установке игры — это обычное деление на ноль…

  1. Что означает ошибка Division by zero
  2. Как исправить ошибку Warning: Division by zero in
  1. Что означает ошибка Division by zero

    У нас встретилась вот такая ошибка :

    Warning: Division by zero in адрес on line 18

    Что означает ошибка Division by zero

    Прежде чем приступать к нашей ошибке — давайте просто попробуем перевести!

    Warning: Division by zero — перевод

    Переведем данную ошибку с английского и нам сразу станет все ясно!

    Warning — предупреждение, сигнал сущ

    Division — деление, отдел, раздел, распределение сущ

    by — по, посредством предл

    zero — ноль сущ

    Итого, если мы соберем данную фразу «Warning: Division by zero» на русском, то получим :

    Внимание! Ошибка деления на ноль!

  2. как исправить ошибку Warning: Division by zero in

    Дл ятого, чтобы исправить ошибку «Warning: Division by zero in» нам понадобится скриншот ошибки, который мы приводили раньше. смотрим на него и видим последнее слово и число, line — это линия в файле, 18 — это номер линии где ошибка.

    line 18

    И та часть, которая размыта — это адрес, по которому находится файл с ошибкой Warning: Division by zero in
    как исправить ошибку Warning: Division by zero in

    Далее находим данный файл и удаляем ошибку!

Можете не благодарить, лучше помогите!

COMMENTS+

 
BBcode


vetal_x

20 / 20 / 5

Регистрация: 23.01.2013

Сообщений: 371

1

07.02.2014, 00:53. Показов 11529. Ответов 2

Метки нет (Все метки)


SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SELECT
 
    (SELECT (tbl_OfferingInOpportunity1.TotalAmount / tbl_OfferingInOpportunity1.Quantity) AS [Price]
    FROM    [dbo].[tbl_OfferingInOpportunity] AS [tbl_OfferingInOpportunity1]
    WHERE([tbl_Opportunity].[ID] = [tbl_OfferingInOpportunity1].[OpportunityID] AND
        [tbl_OfferingInOpportunity1].[ModifiedOn] = 
        (SELECT
            top 1 ([tbl_OfferingInOpportunity2].[ModifiedOn]) AS [ModifiedOn]
        FROM
            [dbo].[tbl_OfferingInOpportunity] AS [tbl_OfferingInOpportunity2]
        WHERE([tbl_OfferingInOpportunity1].[OpportunityID] = [tbl_OfferingInOpportunity2].[OpportunityID])))) AS [Price]
FROM
    [dbo].[tbl_Opportunity] AS [tbl_Opportunity]
LEFT OUTER JOIN
    [dbo].[tbl_Contact] AS [Contact] ON [Contact].[ID] = [tbl_Opportunity].[ContactID]
    WHERE (SELECT COUNT(*) FROM tbl_OfferingInOpportunity WHERE tbl_OfferingInOpportunity.OpportunityID=tbl_Opportunity.ID)>0

как с єтого может вылезти такая ошибка:

SQL
1
Divide BY zero error encountered.

tbl_OfferingInOpportunity1.Quantity нету записей с значением 0

__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь



0



3318 / 2027 / 723

Регистрация: 02.06.2013

Сообщений: 4,976

07.02.2014, 09:48

2

Цитата
Сообщение от vetal_x
Посмотреть сообщение

tbl_OfferingInOpportunity1.Quantity нету записей с значением 0

Чудес не бывает. Если есть деление на 0, значит таки есть. Как проверяли, что нет?
Либо в какой-то таблице есть вычисляемый столбец, и ошибка происходит при вычислении его значения.



0



ZlobnyG

07.02.2014, 15:13

3

SQL
1
tbl_OfferingInOpportunity1.TotalAmount / NULLIF(tbl_OfferingInOpportunity1.Quantity,0)

как вариант

Время прочтения
16 мин

Просмотры 35K

Привет, Хабр! Представляю вашему вниманию перевод статьи «Error and Transaction Handling in SQL Server. Part One – Jumpstart Error Handling» автора Erland Sommarskog.

1. Введение

Эта статья – первая в серии из трёх статей, посвященных обработке ошибок и транзакций в SQL Server. Её цель – дать вам быстрый старт в теме обработки ошибок, показав базовый пример, который подходит для большей части вашего кода. Эта часть написана в расчете на неопытного читателя, и по этой причине я намеренно умалчиваю о многих деталях. В данный момент задача состоит в том, чтобы рассказать как без упора на почему. Если вы принимаете мои слова на веру, вы можете прочесть только эту часть и отложить остальные две для дальнейших этапов в вашей карьере.

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

Все статьи описывают обработку ошибок и транзакций в SQL Server для версии 2005 и более поздних версий.

1.1 Зачем нужна обработка ошибок?

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

Мы часто хотим, чтобы в базе данных изменения были атомарными. Например, задача по переводу денег с одного счета на другой. С этой целью мы должны изменить две записи в таблице CashHoldings и добавить две записи в таблицу Transactions. Абсолютно недопустимо, чтобы ошибки или сбой привели к тому, что деньги будут переведены на счет получателя, а со счета отправителя они не будут списаны. По этой причине обработка ошибок также касается и обработки транзакций. В приведенном примере нам нужно обернуть операцию в BEGIN TRANSACTION и COMMIT TRANSACTION, но не только это: в случае ошибки мы должны убедиться, что транзакция откачена.

2. Основные команды

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

2.1 TRY-CATCH

Основным механизмом обработки ошибок является конструкция TRY-CATCH, очень напоминающая подобные конструкции в других языках. Структура такова:

BEGIN TRY
   <обычный код>
END TRY
BEGIN CATCH
   <обработка ошибок>
END CATCH

Если какая-либо ошибка появится в <обычный код>, выполнение будет переведено в блок CATCH, и будет выполнен код обработки ошибок.

Как правило, в CATCH откатывают любую открытую транзакцию и повторно вызывают ошибку. Таким образом, вызывающая клиентская программа понимает, что что-то пошло не так. Повторный вызов ошибки мы обсудим позже в этой статье.

Вот очень быстрый пример:

BEGIN TRY
   DECLARE @x int
   SELECT @x = 1/0
   PRINT 'Not reached'
END TRY
BEGIN CATCH 
   PRINT 'This is the error: ' + error_message()
END CATCH

Результат выполнения: This is the error: Divide by zero error encountered.

Мы вернемся к функции error_message() позднее. Стоит отметить, что использование PRINT в обработчике CATCH приводится только в рамках экспериментов и не следует делать так в коде реального приложения.

Если <обычный код> вызывает хранимую процедуру или запускает триггеры, то любая ошибка, которая в них возникнет, передаст выполнение в блок CATCH. Если более точно, то, когда возникает ошибка, SQL Server раскручивает стек до тех пор, пока не найдёт обработчик CATCH. И если такого обработчика нет, SQL Server отправляет сообщение об ошибке напрямую клиенту.

Есть одно очень важное ограничение у конструкции TRY-CATCH, которое нужно знать: она не ловит ошибки компиляции, которые возникают в той же области видимости. Рассмотрим пример:

CREATE PROCEDURE inner_sp AS
   BEGIN TRY
      PRINT 'This prints'
      SELECT * FROM NoSuchTable
      PRINT 'This does not print'
   END TRY
   BEGIN CATCH
      PRINT 'And nor does this print'
   END CATCH
go
EXEC inner_sp

Выходные данные:

This prints
Msg 208, Level 16, State 1, Procedure inner_sp, Line 4
Invalid object name 'NoSuchTable'

Как можно видеть, блок TRY присутствует, но при возникновении ошибки выполнение не передается блоку CATCH, как это ожидалось. Это применимо ко всем ошибкам компиляции, таким как пропуск колонок, некорректные псевдонимы и тому подобное, которые возникают во время выполнения. (Ошибки компиляции могут возникнуть в SQL Server во время выполнения из-за отложенного разрешения имен – особенность, благодаря которой SQL Server позволяет создать процедуру, которая обращается к несуществующим таблицам.)

Эти ошибки не являются полностью неуловимыми; вы не можете поймать их в области, в которой они возникают, но вы можете поймать их во внешней области. Добавим такой код к предыдущему примеру:

CREATE PROCEDURE outer_sp AS
   BEGIN TRY
      EXEC inner_sp
   END TRY
   BEGIN CATCH
      PRINT 'The error message is: ' + error_message()
   END CATCH
go
EXEC outer_sp

Теперь мы получим на выходе это:

This prints
The error message is: Invalid object name 'NoSuchTable'.

На этот раз ошибка была перехвачена, потому что сработал внешний обработчик CATCH.

2.2 SET XACT_ABORT ON

В начало ваших хранимых процедур следует всегда добавлять это выражение:

SET XACT_ABORT, NOCOUNT ON

Оно активирует два параметра сессии, которые выключены по умолчанию в целях совместимости с предыдущими версиями, но опыт доказывает, что лучший подход – это иметь эти параметры всегда включенными. Поведение SQL Server по умолчанию в той ситуации, когда не используется TRY-CATCH, заключается в том, что некоторые ошибки прерывают выполнение и откатывают любые открытые транзакции, в то время как с другими ошибками выполнение последующих инструкций продолжается. Когда вы включаете XACT_ABORT ON, почти все ошибки начинают вызывать одинаковый эффект: любая открытая транзакция откатывается, и выполнение кода прерывается. Есть несколько исключений, среди которых наиболее заметным является выражение RAISERROR.

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

Для надежной обработки ошибок в SQL Server вам необходимы как TRY-CATCH, так и SET XACT_ABORT ON. Среди них инструкция SET XACT_ABORT ON наиболее важна. Если для кода на промышленной среде только на нее полагаться не стоит, то для быстрых и простых решений она вполне подходит.

Параметр NOCOUNT не имеет к обработке ошибок никакого отношения, но включение его в код является хорошей практикой. NOCOUNT подавляет сообщения вида (1 row(s) affected), которые вы можете видеть в панели Message в SQL Server Management Studio. В то время как эти сообщения могут быть полезны при работе c SSMS, они могут негативно повлиять на производительность в приложении, так как увеличивают сетевой трафик. Сообщение о количестве строк также может привести к ошибке в плохо написанных клиентских приложениях, которые могут подумать, что это данные, которые вернул запрос.

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

SET NOCOUNT ON
SET XACT_ABORT ON

Между ними нет никакого отличия. Я предпочитаю версию с SET и запятой, т.к. это снижает уровень шума в коде. Поскольку эти выражения должны появляться во всех ваших хранимых процедурах, они должны занимать как можно меньше места.

3. Основной пример обработки ошибок

После того, как мы посмотрели на TRY-CATCH и SET XACT_ABORT ON, давайте соединим их вместе в примере, который мы можем использовать во всех наших хранимых процедурах. Для начала я покажу пример, в котором ошибка генерируется в простой форме, а в следующем разделе я рассмотрю решения получше.

Для примера я буду использовать эту простую таблицу.

CREATE TABLE sometable(a int NOT NULL,
                       b int NOT NULL,
                       CONSTRAINT pk_sometable PRIMARY KEY(a, b))

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

CREATE PROCEDURE insert_data @a int, @b int AS 
   SET XACT_ABORT, NOCOUNT ON
   BEGIN TRY
      BEGIN TRANSACTION
      INSERT sometable(a, b) VALUES (@a, @b)
      INSERT sometable(a, b) VALUES (@b, @a)
      COMMIT TRANSACTION
   END TRY
   BEGIN CATCH
      IF @@trancount > 0 ROLLBACK TRANSACTION
      DECLARE @msg nvarchar(2048) = error_message()  
      RAISERROR (@msg, 16, 1)
      RETURN 55555
   END CATCH

Первая строка в процедуре включает XACT_ABORT и NOCOUNT в одном выражении, как я показывал выше. Эта строка – единственная перед BEGIN TRY. Все остальное в процедуре должно располагаться после BEGIN TRY: объявление переменных, создание временных таблиц, табличных переменных, всё. Даже если у вас есть другие SET-команды в процедуре (хотя причины для этого встречаются редко), они должны идти после BEGIN TRY.

Причина, по которой я предпочитаю указывать SET XACT_ABORT и NOCOUNT перед BEGIN TRY, заключается в том, что я рассматриваю это как одну строку шума: она всегда должна быть там, но я не хочу, чтобы это мешало взгляду. Конечно же, это дело вкуса, и если вы предпочитаете ставить SET-команды после BEGIN TRY, ничего страшного. Важно то, что вам не следует ставить что-либо другое перед BEGIN TRY.

Часть между BEGIN TRY и END TRY является основной составляющей процедуры. Поскольку я хотел использовать транзакцию, определенную пользователем, я ввел довольно надуманное бизнес-правило, в котором говорится, что если вы вставляете пару, то обратная пара также должна быть вставлена. Два выражения INSERT находятся внутри BEGIN и COMMIT TRANSACTION. Во многих случаях у вас будет много строк кода между BEGIN TRY и BEGIN TRANSACTION. Иногда у вас также будет код между COMMIT TRANSACTION и END TRY, хотя обычно это только финальный SELECT, возвращающий данные или присваивающий значения выходным параметрам. Если ваша процедура не выполняет каких-либо изменений или имеет только одно выражение INSERT/UPDATE/DELETE/MERGE, то обычно вам вообще не нужно явно указывать транзакцию.

В то время как блок TRY будет выглядеть по-разному от процедуры к процедуре, блок CATCH должен быть более или менее результатом копирования и вставки. То есть вы делаете что-то короткое и простое и затем используете повсюду, не особо задумываясь. Обработчик CATCH, приведенный выше, выполняет три действия:

  1. Откатывает любые открытые транзакции.
  2. Повторно вызывает ошибку.
  3. Убеждается, что возвращаемое процедурой значение отлично от нуля.

Эти три действия должны всегда быть там. Мы можете возразить, что строка

IF @@trancount > 0 ROLLBACK TRANSACTION

не нужна, если нет явной транзакции в процедуре, но это абсолютно неверно. Возможно, вы вызываете хранимую процедуру, которая открывает транзакцию, но которая не может ее откатить из-за ограничений TRY-CATCH. Возможно, вы или кто-то другой добавите явную транзакцию через два года. Вспомните ли вы тогда о том, что нужно добавить строку с откатом? Не рассчитывайте на это. Я также слышу читателей, которые возражают, что если тот, кто вызывает процедуру, открыл транзакцию, мы не должны ее откатывать… Нет, мы должны, и если вы хотите знать почему, вам нужно прочитать вторую и третью части. Откат транзакции в обработчике CATCH – это категорический императив, у которого нет исключений.

Код повторной генерации ошибки включает такую строку:

DECLARE @msg nvarchar(2048) = error_message()

Встроенная функция error_message() возвращает текст возникшей ошибки. В следующей строке ошибка повторно вызывается с помощью выражения RAISERROR. Это не самый простой способ вызова ошибки, но он работает. Другие способы мы рассмотрим в следующей главе.

Замечание: синтаксис для присвоения начального значения переменной в DECLARE был внедрен в SQL Server 2008. Если у вас SQL Server 2005, вам нужно разбить строку на DECLARE и выражение SELECT.

Финальное выражение RETURN – это страховка. RAISERROR никогда не прерывает выполнение, поэтому выполнение следующего выражения будет продолжено. Пока все процедуры используют TRY-CATCH, а также весь клиентский код обрабатывает исключения, нет повода для беспокойства. Но ваша процедура может быть вызвана из старого кода, написанного до SQL Server 2005 и до внедрения TRY-CATCH. В те времена лучшее, что мы могли делать, это смотреть на возвращаемые значения. То, что вы возвращаете с помощью RETURN, не имеет особого значения, если это не нулевое значение (ноль обычно обозначает успешное завершение работы).

Последнее выражение в процедуре – это END CATCH. Никогда не следует помещать какой-либо код после END CATCH. Кто-нибудь, читающий процедуру, может не увидеть этот кусок кода.

После прочтения теории давайте попробуем тестовый пример:

EXEC insert_data 9, NULL

Результат выполнения:

Msg 50000, Level 16, State 1, Procedure insert_data, Line 12
Cannot insert the value NULL into column 'b', table 'tempdb.dbo.sometable'; column does not allow nulls. INSERT fails.

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

CREATE PROCEDURE outer_sp @a int, @b int AS
   SET XACT_ABORT, NOCOUNT ON
   BEGIN TRY
      EXEC insert_data @a, @b
   END TRY
   BEGIN CATCH
      IF @@trancount > 0 ROLLBACK TRANSACTION
      DECLARE @msg nvarchar(2048) = error_message()
      RAISERROR (@msg, 16, 1)
      RETURN 55555
   END CATCH
go
EXEC outer_sp 8, 8

Результат работы:

Msg 50000, Level 16, State 1, Procedure outer_sp, Line 9
Violation of PRIMARY KEY constraint 'pk_sometable'. Cannot insert duplicate key in object 'dbo.sometable'. The duplicate key value is (8, 8).

Мы получили корректное сообщение об ошибке, но если вы посмотрите на заголовки этого сообщения и на предыдущее поближе, то можете заметить проблему:

Msg 50000, Level 16, State 1, Procedure insert_data, Line 12
Msg 50000, Level 16, State 1, Procedure outer_sp, Line 9

Сообщение об ошибке выводит информацию о расположении конечного выражения RAISERROR. В первом случае некорректен только номер строки. Во втором случае некорректно также имя процедуры. Для простых процедур, таких как наш тестовый пример, это не является большой проблемой. Но если у вас есть несколько уровней вложенных сложных процедур, то наличие сообщения об ошибке с отсутствием указания на место её возникновения сделает поиск и устранение ошибки намного более сложным делом. По этой причине желательно генерировать ошибку таким образом, чтобы можно было определить нахождение ошибочного фрагмента кода быстро, и это то, что мы рассмотрим в следующей главе.

4. Три способа генерации ошибки

4.1 Использование error_handler_sp

Мы рассмотрели функцию error_message(), которая возвращает текст сообщения об ошибке. Сообщение об ошибке состоит из нескольких компонентов, и существует своя функция error_xxx() для каждого из них. Мы можем использовать их для повторной генерации полного сообщения, которое содержит оригинальную информацию, хотя и в другом формате. Если делать это в каждом обработчике CATCH, это будет большой недостаток — дублирование кода. Вам не обязательно находиться в блоке CATCH для вызова error_message() и других подобных функций, и они вернут ту же самую информацию, если будут вызваны из хранимой процедуры, которую выполнит блок CATCH.

Позвольте представить вам error_handler_sp:

CREATE PROCEDURE error_handler_sp AS
 
   DECLARE @errmsg   nvarchar(2048),
           @severity tinyint,
           @state    tinyint,
           @errno    int,
           @proc     sysname,
           @lineno   int
           
   SELECT @errmsg = error_message(), @severity = error_severity(),
          @state  = error_state(), @errno = error_number(),
          @proc   = error_procedure(), @lineno = error_line()
       
   IF @errmsg NOT LIKE '***%'
   BEGIN
      SELECT @errmsg = '*** ' + coalesce(quotename(@proc), '<dynamic SQL>') + 
                       ', Line ' + ltrim(str(@lineno)) + '. Errno ' + 
                       ltrim(str(@errno)) + ': ' + @errmsg
   END
   RAISERROR('%s', @severity, @state, @errmsg)

Первое из того, что делает error_handler_sp – это сохраняет значение всех error_xxx() функций в локальные переменные. Я вернусь к выражению IF через секунду. Вместо него давайте посмотрим на выражение SELECT внутри IF:

SELECT @errmsg = '*** ' + coalesce(quotename(@proc), '<dynamic SQL>') + 
                 ', Line ' + ltrim(str(@lineno)) + '. Errno ' + 
                 ltrim(str(@errno)) + ': ' + @errmsg

Цель этого SELECT заключается в форматировании сообщения об ошибке, которое передается в RAISERROR. Оно включает в себя всю информацию из оригинального сообщения об ошибке, которое мы не можем вставить напрямую в RAISERROR. Мы должны обработать имя процедуры, которое может быть NULL для ошибок в обычных скриптах или в динамическом SQL. Поэтому используется функция COALESCE. (Если вы не понимаете форму выражения RAISERROR, я рассказываю о нем более детально во второй части.)

Отформатированное сообщение об ошибке начинается с трех звездочек. Этим достигаются две цели: 1) Мы можем сразу видеть, что это сообщение вызвано из обработчика CATCH. 2) Это дает возможность для error_handler_sp отфильтровать ошибки, которые уже были сгенерированы один или более раз, с помощью условия NOT LIKE ‘***%’ для того, чтобы избежать изменения сообщения во второй раз.

Вот как обработчик CATCH должен выглядеть, когда вы используете error_handler_sp:

BEGIN CATCH
   IF @@trancount > 0 ROLLBACK TRANSACTION
   EXEC error_handler_sp
   RETURN 55555
END CATCH

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

EXEC insert_data 8, NULL
EXEC outer_sp 8, 8

Результат выполнения:

Msg 50000, Level 16, State 2, Procedure error_handler_sp, Line 20
*** [insert_data], Line 5. Errno 515: Cannot insert the value NULL into column 'b', table 'tempdb.dbo.sometable'; column does not allow nulls. INSERT fails.
Msg 50000, Level 14, State 1, Procedure error_handler_sp, Line 20
*** [insert_data], Line 6. Errno 2627: Violation of PRIMARY KEY constraint 'pk_sometable'. Cannot insert duplicate key in object 'dbo.sometable'. The duplicate key value is (8, 8).

Заголовки сообщений говорят о том, что ошибка возникла в процедуре error_handler_sp, но текст сообщений об ошибках дает нам настоящее местонахождение ошибки – как название процедуры, так и номер строки.

Я покажу еще два метода вызова ошибок. Однако error_handler_sp является моей главной рекомендацией для читателей, которые читают эту часть. Это — простой вариант, который работает на всех версиях SQL Server начиная с 2005. Существует только один недостаток: в некоторых случаях SQL Server генерирует два сообщения об ошибках, но функции error_xxx() возвращают только одну из них, и поэтому одно из сообщений теряется. Это может быть неудобно при работе с административными командами наподобие BACKUPRESTORE, но проблема редко возникает в коде, предназначенном чисто для приложений.

4.2. Использование ;THROW

В SQL Server 2012 Microsoft представил выражение ;THROW для более легкой обработки ошибок. К сожалению, Microsoft сделал серьезную ошибку при проектировании этой команды и создал опасную ловушку.

С выражением ;THROW вам не нужно никаких хранимых процедур. Ваш обработчик CATCH становится таким же простым, как этот:

BEGIN CATCH
   IF @@trancount > 0 ROLLBACK TRANSACTION
   ;THROW
   RETURN 55555
END CATCH

Достоинство ;THROW в том, что сообщение об ошибке генерируется точно таким же, как и оригинальное сообщение. Если изначально было два сообщения об ошибках, оба сообщения воспроизводятся, что делает это выражение еще привлекательнее. Как и со всеми другими сообщениями об ошибках, ошибки, сгенерированные ;THROW, могут быть перехвачены внешним обработчиком CATCH и воспроизведены. Если обработчика CATCH нет, выполнение прерывается, поэтому оператор RETURN в данном случае оказывается не нужным. (Я все еще рекомендую оставлять его, на случай, если вы измените свое отношение к ;THROW позже).

Если у вас SQL Server 2012 или более поздняя версия, измените определение insert_data и outer_sp и попробуйте выполнить тесты еще раз. Результат в этот раз будет такой:

Msg 515, Level 16, State 2, Procedure insert_data, Line 5
Cannot insert the value NULL into column 'b', table 'tempdb.dbo.sometable'; column does not allow nulls. INSERT fails.
Msg 2627, Level 14, State 1, Procedure insert_data, Line 6
Violation of PRIMARY KEY constraint 'pk_sometable'. Cannot insert duplicate key in object 'dbo.sometable'. The duplicate key value is (8, 8).

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

В этом месте вы можете сказать себе: действительно ли Microsoft назвал команду ;THROW? Разве это не просто THROW? На самом деле, если вы посмотрите в Books Online, там не будет точки с запятой. Но точка с запятой должны быть. Официально они отделяют предыдущее выражение, но это опционально, и далеко не все используют точку с запятой в выражениях T-SQL. Более важно, что если вы пропустите точку с запятой перед THROW, то не будет никакой синтаксической ошибки. Но это повлияет на поведение при выполнении выражения, и это поведение будет непостижимым для непосвященных. При наличии активной транзакции вы получите сообщение об ошибке, которое будет полностью отличаться от оригинального. И еще хуже, что при отсутствии активной транзакции ошибка будет тихо выведена без обработки. Такая вещь, как пропуск точки с запятой, не должно иметь таких абсурдных последствий. Для уменьшения риска такого поведения, всегда думайте о команде как о ;THROW (с точкой с запятой).

Нельзя отрицать того, что ;THROW имеет свои преимущества, но точка с запятой не единственная ловушка этой команды. Если вы хотите использовать ее, я призываю вас прочитать по крайней мере вторую часть этой серии, где я раскрываю больше деталей о команде ;THROW. До этого момента, используйте error_handler_sp.

4.3. Использование SqlEventLog

Третий способ обработки ошибок – это использование SqlEventLog, который я описываю очень детально в третьей части. Здесь я лишь сделаю короткий обзор.

SqlEventLog предоставляет хранимую процедуру slog.catchhandler_sp, которая работает так же, как и error_handler_sp: она использует функции error_xxx() для сбора информации и выводит сообщение об ошибке, сохраняя всю информацию о ней. Вдобавок к этому, она логирует ошибку в таблицу splog.sqleventlog. В зависимости от типа приложения, которое у вас есть, эта таблица может быть очень ценным объектом.

Для использования SqlEventLog, ваш обработчик CATCH должен быть таким:

BEGIN CATCH
   IF @@trancount > 0 ROLLBACK TRANSACTION
   EXEC slog.catchhandler_sp @@procid
   RETURN 55555
END CATCH

@@procid возвращает идентификатор объекта текущей хранимой процедуры. Это то, что SqlEventLog использует для логирования информации в таблицу. Используя те же тестовые сценарии, получим результат их работы с использованием catchhandler_sp:

Msg 50000, Level 16, State 2, Procedure catchhandler_sp, Line 125
{515} Procedure insert_data, Line 5
Cannot insert the value NULL into column 'b', table 'tempdb.dbo.sometable'; column does not allow nulls. INSERT fails.
Msg 50000, Level 14, State 1, Procedure catchhandler_sp, Line 125
{2627} Procedure insert_data, Line 6
Violation of PRIMARY KEY constraint 'pk_sometable'. Cannot insert duplicate key in object 'dbo.sometable'. The duplicate key value is (8, 8).

Как вы видите, сообщение об ошибке отформатировано немного не так, как это делает error_handler_sp, но основная идея такая же. Вот образец того, что было записано в таблицу slog.sqleventlog:

logid logdate errno severity logproc linenum msgtext
1 2015-01-25 22:40:24.393 515 16 insert_data 5 Cannot insert …
2 2015-01-25 22:40:24.395 2627 14 insert_data 6 Violation of …

Если вы хотите попробовать SqlEventLog, вы можете загрузить файл sqleventlog.zip. Инструкция по установке находится в третьей части, раздел Установка SqlEventLog.

5. Финальные замечания

Вы изучили основной образец для обработки ошибок и транзакций в хранимых процедурах. Он не идеален, но он должен работать в 90-95% вашего кода. Есть несколько ограничений, на которые стоит обратить внимание:

  1. Как мы видели, ошибки компиляции не могут быть перехвачены в той же процедуре, в которой они возникли, а только во внешней процедуре.
  2. Пример не работает с пользовательскими функциями, так как ни TRY-CATCH, ни RAISERROR нельзя в них использовать.
  3. Когда хранимая процедура на Linked Server вызывает ошибку, эта ошибка может миновать обработчик в хранимой процедуре на локальном сервере и отправиться напрямую клиенту.
  4. Когда процедура вызвана как INSERT-EXEC, вы получите неприятную ошибку, потому что ROLLBACK TRANSACTION не допускается в данном случае.
  5. Как упомянуто выше, если вы используете error_handler_sp или SqlEventLog, мы потеряете одно сообщение, когда SQL Server выдаст два сообщения для одной ошибки. При использовании ;THROW такой проблемы нет.

Я рассказываю об этих ситуациях более подробно в других статьях этой серии.

Перед тем как закончить, я хочу кратко коснуться триггеров и клиентского кода.

Триггеры

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

С триггерами важно понимать, что они являются частью команды, которая запустила триггер, и в триггере вы находитесь внутри транзакции, даже если не используете BEGIN TRANSACTION.
Иногда я вижу на форумах людей, которые спрашивают, могут ли они написать триггер, который не откатывает в случае падения запустившую его команду. Ответ таков: нет способа сделать это надежно, поэтому не стоит даже пытаться. Если в этом есть необходимость, по возможности не следует использовать триггер вообще, а найти другое решение. Во второй и третьей частях я рассматриваю обработку ошибок в триггерах более подробно.

Клиентский код

У вас должна быть обработка ошибок в коде клиента, если он имеет доступ к базе. То есть вы должны всегда предполагать, что при любом вызове что-то может пойти не так. Как именно внедрить обработку ошибок, зависит от конкретной среды.

Здесь я только обращу внимание на важную вещь: реакцией на ошибку, возвращенную SQL Server, должно быть завершение запроса во избежание открытых бесхозных транзакций:

IF @@trancount > 0 ROLLBACK TRANSACTION

Это также применимо к знаменитому сообщению Timeout expired (которое является не сообщением от SQL Server, а от API).

6. Конец первой части

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

… и не забывайте добавлять эту строку в начало ваших хранимых процедур:

SET XACT_ABORT, NOCOUNT ON

Показывать по
10
20
40
сообщений

Новая тема

Ответить

Денис Норильск

Дата регистрации: 17.11.2006
Сообщений: 4

Есть конфигурация ЗиК непосредственно правкой конфы занимается другой человек, я только админю SQL сервер. После внесения очередных изменений в конфу при закрытии месяца SQL сервер матерится: «Microsoft OLE DB Provider for SQL Server: Divide by zero error encountered.», при этом если база лежит в dbf месяц закрывается без проблем! Разработчик конфы все валит на меня, я в свою очередь уверен что со стороны сервера все ок! Считаю что разработчик заранее не предусмотрел деление на 0 что-то типо <br><br>»ВЫБОР КОГДА ЕСТЬNULL(Таблица.Количество,0) = 0 ТОГДА 0 <br> ИНАЧЕ Таблица.Сумма/Таблица.Количество<br>КОНЕЦ КАК Цена»<br><br>Помогите разрешить спор! <br>Как доказать главбуху что проблема вызвана именно ошибкой в конфигурации?<br>Есть ли внутри 1с платформы проверка арифметических операций, может ее там нет, поэтому dbf вариант работает без проблем?<br><br><br>

Prikum

активный пользователь

офлайн

Дата регистрации: 18.02.2002
Сообщений: 20835

К сожалению эта ошибка происходит именно на сервере, поэтому сделать проверку в 1С не предоставляеться возможным. Посмотрите на каком запросе валиться 1с и что включаеться в запрос, возможно ошибка именно там. Что говорит профайлер?

Skynin

Дата регистрации: 18.08.2006
Сообщений: 30

Сталкивался с этой ситуацией.<br><br>Оказалось, если в конфигурации есть календари без единой строчки, то при их заполнении (которое запускается если месяц больше 10-го) SQL версия вылетает с этой ошибкой. А дбф — нет.<br><br>Лечится нахождением таких календарей и исправлением.<br>Находится: трассировкой помощника смены периода.

Денис Норильск

Дата регистрации: 17.11.2006
Сообщений: 4

Спасибо помогло!<br>Все утряслось все счастливы :)

Показывать по
10
20
40
сообщений

EDIT:
В последнее время я получаю много downvotes… поэтому я подумал, что просто добавлю примечание о том, что этот ответ был написан до того, как вопрос подвергся самому последнему редактированию, где возврат нулевого значения был выделен как опция… который кажется очень приемлемым. Некоторые из моих ответов были адресованы таким опасениям, как Эдвардо, в комментариях, которые, казалось, выступали за возвращение 0. Это тот случай, с которым я выступал.

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

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

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

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

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

Msg 8134, Уровень 16, состояние 1, Линия 1 делится на нулевую ошибку.

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

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

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

или

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

— лучший способ использовать NULLIF предложения?

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

17 ответов


чтобы избежать ошибки «деление на ноль», мы запрограммировали ее следующим образом:

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

но вот гораздо более приятный способ сделать это:

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

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

504

автор: Henrik Staun Poulsen


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

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

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


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

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

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

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

переписать запрос так:

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

любое число, разделенное на NULL дает NULL, и ошибка не генерируется.


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

SET ARITHABORT OFF 
SET ANSI_WARNINGS OFF

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


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

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

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

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

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


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

SELECT a / NULLIF(b, 0) FROM t 

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


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

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


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

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

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

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

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


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

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

  1. добавить ограничение проверки, которое заставляет Divisor не-ноль
  2. добавьте валидатор в форму, чтобы пользователь не мог вводить нулевые значения в это поле.

для обновления SQLs:

update Table1 set Col1 = Col2 / ISNULL(NULLIF(Col3,0),1)

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


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

Я смотрю на вычисление количества оборотов инвентаря, которые происходят в трехмесячный период. Я подсчитал, что у меня есть стоимость товаров, проданных за три месяца в размере 1000$. Годовой темп продаж 4,000 $(1,000$/3)*12. Начало инвентаризации 0. Окончание инвентаризации 0. Мой средний инвентарь теперь равен 0. У меня продажи 4000 долларов в год, а инвентаря нет. Это дает бесконечное число оборотов. Это означает, что все мои запасы преобразуются и покупаются клиентами.

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


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


CREATE FUNCTION dbo.Divide(@Numerator Real, @Denominator Real)
RETURNS Real AS
/*
Purpose:      Handle Division by Zero errors
Description:  User Defined Scalar Function
Parameter(s): @Numerator and @Denominator

Test it:

SELECT 'Numerator = 0' Division, dbo.fn_CORP_Divide(0,16) Results
UNION ALL
SELECT 'Denominator = 0', dbo.fn_CORP_Divide(16,0)
UNION ALL
SELECT 'Numerator is NULL', dbo.fn_CORP_Divide(NULL,16)
UNION ALL
SELECT 'Denominator is NULL', dbo.fn_CORP_Divide(16,NULL)
UNION ALL
SELECT 'Numerator & Denominator is NULL', dbo.fn_CORP_Divide(NULL,NULL)
UNION ALL
SELECT 'Numerator & Denominator = 0', dbo.fn_CORP_Divide(0,0)
UNION ALL
SELECT '16 / 4', dbo.fn_CORP_Divide(16,4)
UNION ALL
SELECT '16 / 3', dbo.fn_CORP_Divide(16,3)

*/
BEGIN
    RETURN
        CASE WHEN @Denominator = 0 THEN
            NULL
        ELSE
            @Numerator / @Denominator
        END
END
GO

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

Я согласен с Бешке в том, что вы не хотите скрывать ошибки. Возможно, вы не имеете дело с ядерным реактором, но скрывать ошибки в целом-плохая практика программирования. Это одна из самых современных причин языки программирования реализуют структурированную обработку исключений для разделения фактического возвращаемого значения с кодом ошибки / состояния. Это особенно верно, когда вы занимаетесь математикой. Самая большая проблема заключается в том, что вы не можете отличить правильно вычислены 0 возвращается или 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.
}

использовать NULLIF(exp,0) но таким образом —NULLIF(ISNULL(exp,0),0)

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

-1

автор: Johnny Kancharla


Понравилась статья? Поделить с друзьями:
  • Div как изменить размер шрифта
  • Dither error diffusion
  • Disturbedcom ошибка 10016 windows 10
  • Distributedcom ошибка 10000
  • Distributedcom error 10016 windows 10