Меню

Обнаружена ошибка деление на ноль sql

EDIT:
I’m getting a lot of downvotes on this recently…so I thought I’d just add a note that this answer was written before the question underwent it’s most recent edit, where returning null was highlighted as an option…which seems very acceptable. Some of my answer was addressed to concerns like that of Edwardo, in the comments, who seemed to be advocating returning a 0. This is the case I was railing against.

ANSWER:
I think there’s an underlying issue here, which is that division by 0 is not legal. It’s an indication that something is fundementally wrong. If you’re dividing by zero, you’re trying to do something that doesn’t make sense mathematically, so no numeric answer you can get will be valid. (Use of null in this case is reasonable, as it is not a value that will be used in later mathematical calculations).

So Edwardo asks in the comments «what if the user puts in a 0?», and he advocates that it should be okay to get a 0 in return. If the user puts zero in the amount, and you want 0 returned when they do that, then you should put in code at the business rules level to catch that value and return 0…not have some special case where division by 0 = 0.

That’s a subtle difference, but it’s important…because the next time someone calls your function and expects it to do the right thing, and it does something funky that isn’t mathematically correct, but just handles the particular edge case it’s got a good chance of biting someone later. You’re not really dividing by 0…you’re just returning an bad answer to a bad question.

Imagine I’m coding something, and I screw it up. I should be reading in a radiation measurement scaling value, but in a strange edge case I didn’t anticipate, I read in 0. I then drop my value into your function…you return me a 0! Hurray, no radiation! Except it’s really there and it’s just that I was passing in a bad value…but I have no idea. I want division to throw the error because it’s the flag that something is wrong.

EDIT:
I’m getting a lot of downvotes on this recently…so I thought I’d just add a note that this answer was written before the question underwent it’s most recent edit, where returning null was highlighted as an option…which seems very acceptable. Some of my answer was addressed to concerns like that of Edwardo, in the comments, who seemed to be advocating returning a 0. This is the case I was railing against.

ANSWER:
I think there’s an underlying issue here, which is that division by 0 is not legal. It’s an indication that something is fundementally wrong. If you’re dividing by zero, you’re trying to do something that doesn’t make sense mathematically, so no numeric answer you can get will be valid. (Use of null in this case is reasonable, as it is not a value that will be used in later mathematical calculations).

So Edwardo asks in the comments «what if the user puts in a 0?», and he advocates that it should be okay to get a 0 in return. If the user puts zero in the amount, and you want 0 returned when they do that, then you should put in code at the business rules level to catch that value and return 0…not have some special case where division by 0 = 0.

That’s a subtle difference, but it’s important…because the next time someone calls your function and expects it to do the right thing, and it does something funky that isn’t mathematically correct, but just handles the particular edge case it’s got a good chance of biting someone later. You’re not really dividing by 0…you’re just returning an bad answer to a bad question.

Imagine I’m coding something, and I screw it up. I should be reading in a radiation measurement scaling value, but in a strange edge case I didn’t anticipate, I read in 0. I then drop my value into your function…you return me a 0! Hurray, no radiation! Except it’s really there and it’s just that I was passing in a bad value…but I have no idea. I want division to throw the error because it’s the flag that something is wrong.

SQL Server 2017 Developer on Windows SQL Server 2017 Enterprise on Windows SQL Server 2017 Enterprise Core on Windows SQL Server 2017 Standard on Windows Еще…Меньше

Проблемы

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

Решение

Эта проблема устранена в следующем накопительном обновлении SQL Server:

       Накопительное обновление 1 для SQL Server 2017

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

Последнее накопительное обновление для SQL Server 2017

Статус

Корпорация Майкрософт подтверждает наличие этой проблемы в своих продуктах, которые перечислены в разделе «Применяется к».

Ссылки

Ознакомьтесь с терминологией, которую корпорация Майкрософт использует для описания обновлений программного обеспечения.

Нужна дополнительная помощь?

Время прочтения: 4 мин.

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

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

declare @vol_1 int;
declare @vol_2 int;
set @vol_1 = 10;
set @vol_2 = 5;
select @vol_1/@vol_2 ratio_vol;

Соотношение значений вычисляется корректно:

При значении @vol_2 = 0 получаем сообщение об ошибке:

declare @vol_1 int;
declare @vol_2 int;
set @vol_1 = 10;
set @vol_2 = 0;
select @vol_1/@vol_2 ratio_vol;

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

1. Применение функции NULLIF.

Синтаксис NULLIF следующий:

NULLIF(expr1, expr2)

При равенстве значений двух аргументов, возвращается значение NULL.

Например:

select NULLIF (55, 55) result;

Результат запроса:

Если значения аргументов не равны, возвращается значение первого аргумента (expr1).

select NULLIF (12, 55) result;

Изменим этот запрос, добавив в него NULLIF, для обхода ошибки деления на ноль.

Логика использования функции NULLIF для задачи деления на ноль следующая:

  • используем в знаменателе функцию NULLIF с нулевым значением ее второго аргумента
  • если значение первого аргумента функции NULLIF также равно нулю, то возвращается значение NULL, и тогда в SQL Server, если мы разделим число на значение NULL, на выходе получим NULL.
  • если значение первого аргумента не равно нулю, возвращается значение первого аргумента функции NULLIF, и деление выполняется как стандартная операция деления.
declare @vol_1 int;
declare @vol_2 int;
set @vol_1 = 10;
set @vol_2 = 0;
select @vol_1/NULLIF (@vol_2, 0) ratio_vol;

Ниже, результат работы такого кода (в знаменателе – значение NULL):

Добавим в код функцию ISNULL, для того, чтобы вместо значения NULL в выводе результата вычисления получать 0.

Эта функция заменяет NULL значение в expr1 и возвращает значение expr2 в качестве вывода.

Логика запроса с функциями ISNULL и NULLIF такая:

  • первый аргумент ((@vol_1/ NULLIF (@vol_2,0)) вернет значение NULL;
  • для функции ISNULL указываем нулевое значение второго аргумента;
  • так как первый аргумент — NULL, то вывод всего запроса равен нулю, т.е. значению второго аргумента.

Пример кода с функциями ISNULL и NULLIF:

declare @vol_1 int;
declare @vol_2 int;
set @vol_1 = 10;
set @vol_2 = 0;
select ISNULL (@vol_1/NULLIF (@vol_2, 0),0) ratio_vol;

Вывод результата успешного выполнения запроса:

2. Использование оператора CASE.

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

Оператор CASE проверит значение параметра @vol_2:

  • если значение @vol_2 равно нулю, возвращается значение NULL;
  • если это условие не выполняется, то производится арифметическая операция деления (@vol_1/@vol_2) и возвращается ее результат.
declare @vol_1 int;
declare @vol_2 int;
set @vol_1 = 10;
set @vol_2 = 0;
select CASE
	    when @vol_2 = 0
	    then NULL
	    else @vol_1/@vol_2
       end as ratio_vol;

Успешный результат запроса:

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

   Patrio_O_Muerte

29.07.09 — 14:29

Ситуация: у одного из пользователей валится отчет с ошибкой:

{Форма.Отчет(53)}: Ошибка при вызове метода контекста (Вывести): Ошибка выполнения запроса «Ошибка при выполнении операции над данными:

Microsoft OLE DB Provider for SQL Server: Обнаружена ошибка: деление на ноль.

HRESULT=80040E14, SQLSrvr: Error state=1, Severity=10, native=8134, line=1

»

   ПостроительОтчетаОтчет.Вывести(ЭлементыФормы.ПолеТабличногоДокумента);

по причине:

Ошибка выполнения запроса «Ошибка при выполнении операции над данными:

Microsoft OLE DB Provider for SQL Server: Обнаружена ошибка: деление на ноль.

HRESULT=80040E14, SQLSrvr: Error state=1, Severity=10, native=8134, line=1

»

по причине:

Ошибка при выполнении операции над данными:

Microsoft OLE DB Provider for SQL Server: Обнаружена ошибка: деление на ноль.

HRESULT=80040E14, SQLSrvr: Error state=1, Severity=10, native=8134, line=1

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

   Patrio_O_Muerte

5 — 29.07.09 — 14:39

ВЫБРАТЬ
   АссортиментнаяМатрицаОсновная.НоменклатураОсновная КАК Номенклатура,
   ЗакупочныеЦены.Цена КАК ЦенаЗакупочная,
   ПродажныеЦены.Цена КАК ЦенаПродажная,
   ВЫРАЗИТЬ((ВЫРАЗИТЬ(ПродажныеЦены.Цена КАК ЧИСЛО(20, 2))) / (ВЫРАЗИТЬ(ЗакупочныеЦены.Цена КАК ЧИСЛО(20, 2))) * 100 — 100 КАК ЧИСЛО(10, 2)) КАК ПроцентНадбавки,
   УчетПродаж.СуммаОборот КАК Сумма,
   УчетПродаж.КоличествоОборот КАК Количество,
   ЕСТЬNULL(АссортиментнаяМатрицаДляСравнения.Значение, «Пассивный») КАК ЗначениеИзПодчиненнойМатрицы
ИЗ
   (ВЫБРАТЬ
       АссортиментныеМатрицы.Объект КАК НоменклатураОсновная,
       АссортиментныеМатрицы.АссортиментнаяМатрица КАК МатрицаОсновная,
       АссортиментныеМатрицы.Значение КАК Значение
   ИЗ
       РегистрСведений.АссортиментныеМатрицы КАК АссортиментныеМатрицы
   {ГДЕ
       АссортиментныеМатрицы.Объект.* КАК Номенклатура,
       АссортиментныеМатрицы.АссортиментнаяМатрица.* КАК АссортиментнаяМатрицаОсновная,
       АссортиментныеМатрицы.Значение.* КАК ПризнакНоменклатурыВОсновнойМатрице}) КАК АссортиментнаяМатрицаОсновная
       ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
           ПериодыИЦены.Цена КАК Цена,
           МаксимальныеПериоды.Номенклатура КАК Номенклатура,
           МаксимальныеПериоды.СтруктурнаяЕдиница КАК СтруктурнаяЕдиница
       ИЗ
           (ВЫБРАТЬ
               ЦеныНоменклатурыЗакупочныеСрезПоследних.Номенклатура КАК Номенклатура,
               МАКСИМУМ(ЦеныНоменклатурыЗакупочныеСрезПоследних.Период) КАК Период,
               ЦеныНоменклатурыЗакупочныеСрезПоследних.СтруктурнаяЕдиница КАК СтруктурнаяЕдиница
           ИЗ
               РегистрСведений.ЦеныНоменклатурыЗакупочные.СрезПоследних({(&КонецПериода)}, {(Номенклатура), (СтруктурнаяЕдиница.АссортиментнаяМатрица) КАК АссортиментнаяМатрицаОсновная}) КАК ЦеныНоменклатурыЗакупочныеСрезПоследних

                       СГРУППИРОВАТЬ ПО
               ЦеныНоменклатурыЗакупочныеСрезПоследних.Номенклатура,
               ЦеныНоменклатурыЗакупочныеСрезПоследних.СтруктурнаяЕдиница) КАК МаксимальныеПериоды
               ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
                   ЦеныНоменклатурыЗакупочныеСрезПоследних.Номенклатура КАК Номенклатура,
                   МАКСИМУМ(ЦеныНоменклатурыЗакупочныеСрезПоследних.Период) КАК Период,
                   ЦеныНоменклатурыЗакупочныеСрезПоследних.Цена КАК Цена,
                   ЦеныНоменклатурыЗакупочныеСрезПоследних.СтруктурнаяЕдиница КАК СтруктурнаяЕдиница
               ИЗ
                   РегистрСведений.ЦеныНоменклатурыЗакупочные.СрезПоследних({(&КонецПериода)}, {(Номенклатура), (СтруктурнаяЕдиница.АссортиментнаяМатрица) КАК АссортиментнаяМатрицаОсновная}) КАК ЦеныНоменклатурыЗакупочныеСрезПоследних

                               СГРУППИРОВАТЬ ПО
                   ЦеныНоменклатурыЗакупочныеСрезПоследних.Номенклатура,
                   ЦеныНоменклатурыЗакупочныеСрезПоследних.Цена,
                   ЦеныНоменклатурыЗакупочныеСрезПоследних.СтруктурнаяЕдиница) КАК ПериодыИЦены
               ПО (ПериодыИЦены.Номенклатура = МаксимальныеПериоды.Номенклатура)
                   И (ПериодыИЦены.Период = МаксимальныеПериоды.Период)
                   И МаксимальныеПериоды.СтруктурнаяЕдиница = ПериодыИЦены.СтруктурнаяЕдиница) КАК ЗакупочныеЦены
       ПО АссортиментнаяМатрицаОсновная.НоменклатураОсновная = ЗакупочныеЦены.Номенклатура
           И АссортиментнаяМатрицаОсновная.МатрицаОсновная = ЗакупочныеЦены.СтруктурнаяЕдиница.АссортиментнаяМатрица
       ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
           АссортиментныеМатрицы.Объект КАК НоменклатураДляСравнения,
           АссортиментныеМатрицы.АссортиментнаяМатрица КАК МатрицаДляСравнения,
           АссортиментныеМатрицы.Значение КАК Значение
       ИЗ
           РегистрСведений.АссортиментныеМатрицы КАК АссортиментныеМатрицы
       {ГДЕ
           АссортиментныеМатрицы.Объект.* КАК Номенклатура,
           АссортиментныеМатрицы.АссортиментнаяМатрица.* КАК АссортиментнаяМатрицаДляСравнения}) КАК АссортиментнаяМатрицаДляСравнения
       ПО АссортиментнаяМатрицаОсновная.НоменклатураОсновная = АссортиментнаяМатрицаДляСравнения.НоменклатураДляСравнения
       ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
           МаксимальныйПериодПродажныхЦен.Номенклатура КАК Номенклатура,
           ПериодыИЦеныПродажныхЦен.Цена КАК Цена,
           МаксимальныйПериодПродажныхЦен.КатегорияЦен КАК КатегорияЦен
       ИЗ
           (ВЫБРАТЬ
               МАКСИМУМ(ЦеныНоменклатурыСрезПоследних.Период) КАК Период,
               ЦеныНоменклатурыСрезПоследних.Номенклатура КАК Номенклатура,
               ЦеныНоменклатурыСрезПоследних.КатегорияЦен КАК КатегорияЦен
           ИЗ
               РегистрСведений.ЦеныНоменклатуры.СрезПоследних({(&КонецПериода)}, {(Номенклатура), (СтруктурнаяЕдиница.АссортиментнаяМатрица) КАК АссортиментнаяМатрицаОсновная, (КатегорияЦен)}) КАК ЦеныНоменклатурыСрезПоследних

                       СГРУППИРОВАТЬ ПО
               ЦеныНоменклатурыСрезПоследних.Номенклатура,
               ЦеныНоменклатурыСрезПоследних.КатегорияЦен) КАК МаксимальныйПериодПродажныхЦен
               ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
                   ЦеныНоменклатурыСрезПоследних.Номенклатура КАК Номенклатура,
                   ЦеныНоменклатурыСрезПоследних.Период КАК Период,
                   ЦеныНоменклатурыСрезПоследних.Цена КАК Цена
               ИЗ
                   РегистрСведений.ЦеныНоменклатуры.СрезПоследних({(&КонецПериода)}, {(Номенклатура), (СтруктурнаяЕдиница.АссортиментнаяМатрица) КАК АссортиментнаяМатрицаОсновная, (КатегорияЦен)}) КАК ЦеныНоменклатурыСрезПоследних) КАК ПериодыИЦеныПродажныхЦен
               ПО МаксимальныйПериодПродажныхЦен.Период = ПериодыИЦеныПродажныхЦен.Период
                   И МаксимальныйПериодПродажныхЦен.Номенклатура = ПериодыИЦеныПродажныхЦен.Номенклатура) КАК ПродажныеЦены
       ПО АссортиментнаяМатрицаОсновная.НоменклатураОсновная = ПродажныеЦены.Номенклатура
       ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
           УчетПродажОбороты.Номенклатура КАК Номенклатура,
           УчетПродажОбороты.Склад.Владелец.АссортиментнаяМатрица КАК СкладВладелецАссортиментнаяМатрица,
           УчетПродажОбороты.СуммаОборот КАК СуммаОборот,
           УчетПродажОбороты.КоличествоОборот КАК КоличествоОборот
       ИЗ
           РегистрНакопления.УчетПродаж.Обороты({(&НачалоПериода)}, {(&КонецПериода)}, , {(Номенклатура), (Склад.Владелец.АссортиментнаяМатрица) КАК АссортиментнаяМатрицаДляПродаж}) КАК УчетПродажОбороты) КАК УчетПродаж
       ПО АссортиментнаяМатрицаОсновная.НоменклатураОсновная = УчетПродаж.Номенклатура
           И АссортиментнаяМатрицаОсновная.МатрицаОсновная = УчетПродаж.СкладВладелецАссортиментнаяМатрица

УПОРЯДОЧИТЬ ПО
   Номенклатура
АВТОУПОРЯДОЧИВАНИЕ

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

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

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

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

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

PostgreSQL, SQL, Блог компании OTUS. Онлайн-образование


Рекомендация: подборка платных и бесплатных курсов дизайна интерьера — https://katalog-kursov.ru/

В преддверии старта курса PostgreSQL подготовили небольшой полезный материал.

Большинство языков программирования предназначены для профессиональных разработчиков, знающих алгоритмы и структуру данных. Язык SQL немного отличается.

SQL используется аналитиками, специалистами по обработке данных, руководителями по программному продукту, проектировщиками и многими другими. Эти специалисты имеют доступ к базам данных, но у них не всегда есть интуиция и понимание, чтобы написать эффективные запросы.

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

Будьте внимательны во время деления целых чисел

В PostgreSQL деление целого числа на целое число в результате дает целое число. Не делайте так:

db=# (
  SELECT tax / price AS tax_ratio
  FROM sale
);
 tax_ratio
----------
    0

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

 db=# (
  SELECT tax / price::float AS tax_ratio
  FROM sale
);
 tax_ratio
----------
 0.17

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

Защита от ошибок деления на ноль

Деление на ноль — известная ошибка:

db=# SELECT 1 / 0
ERROR: division by zero

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

db=# SELECT 1 / NULLIF(0, 0);
 ?column?
----------
   -

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

При делении любого числа на NULL результатом будет NULL. Чтобы получить некоторое значение, вы можете свернуть все выражение с COALESCE и предоставить дефолтное значение:

db=# SELECT COALESCE(1 / NULLIF(0, 0), 1);
 ?column?
----------
    1

Функция COALESCE очень полезна. Она допускает любое количество аргументов и возвращает первое значение, которое не является неопределенным.

Знайте разницу между UNION и UNION ALL

Классический вопрос на собеседовании начального уровня для разработчиков и администраторов баз данных: «В чем разница между функциями UNION и UNION ALL?».

UNION ALL объединяет результаты одного или нескольких запросов. UNION делает то же самое, и к тому же исключает повторяющиеся строки. Не делайте так:

 SELECT created_by_id FROM sale
  UNION
  SELECT created_by_id FROM past_sale
);
QUERY PLAN
-----------
Unique  (cost=2654611.00..2723233.86 rows=13724572 width=4)
  ->  Sort  (cost=2654611.00..2688922.43 rows=13724572 width=4)
        Sort Key: sale.created_by_id
        ->  Append  (cost=0.00..652261.30 rows=13724572 width=4)
              ->  Seq Scan on sale  (cost=0.00..442374.57 rows=13570157 width=4)
              ->  Seq Scan on past_sale  (cost=0.00..4018.15 rows=154415 width=4)

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

Если вам не нужно удалять повторяющиеся строки, лучше использовать функцию UNION ALL:

 db=# (
  SELECT created_by_id FROM sale
  UNION ALL
  SELECT created_by_id FROM past_sale
);
QUERY PLAN
-----------
 Append  (cost=0.00..515015.58 rows=13724572 width=4)
   ->  Seq Scan on sale  (cost=0.00..442374.57 rows=13570157 width=4)
   ->  Seq Scan on past_sale  (cost=0.00..4018.15 rows=154415 width=4)

Выполняется намного проще. Результаты получены, сортировка не требуется.

Будьте внимательны при подсчете столбцов, допускающих неопределенное значение

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

db=# pset null NULL
Null display is "NULL".
db=# WITH tb AS (
  SELECT 1 AS id
  UNION ALL
  SELECT null AS id
)
SELECT *
FROM tb;
  id
------
    1
 NULL

Столбец id содержит значение null. Посчитаем столбец id:

 db=# WITH tb AS (
  SELECT 1 AS id
  UNION ALL
  SELECT null AS id
)
SELECT COUNT(id)
FROM tb;
 count
-------
     1

В таблице две строки, но функция COUNT возвращает 1. Это произошло, потому что функция COUNT игнорирует неопределенные значения.

Чтобы посчитать строки, используйте функцию COUNT(*):

 db=# WITH tb AS (
  SELECT 1 AS id
  UNION ALL
  SELECT null AS id
)
SELECT COUNT(*)
FROM tb;
 count
-------
  2

Эта особенность также может быть полезна. Например, если поле с именем modified содержит неопределенное значение в том случае, если строка не была изменена, вы можете рассчитать процент измененных строк следующим образом:

db=# (
  SELECT COUNT(modified) / COUNT(*)::float AS modified_pct
  FROM sale
);
 modified_pct
---------------
  0.98

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

db=# WITH tb AS (
  SELECT null AS id
  UNION ALL
  SELECT null AS id
)
SELECT SUM(id::int)
FROM tb;
 sum
-------
 NULL

Это все документированные операции, так что будьте внимательны!

Обратите внимание на часовые пояса

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

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

SELECT created_at::date, COUNT(*)
FROM sale
GROUP BY 1

Без точной настройки часового пояса вы можете получить разные результаты в зависимости от часового пояса, установленного приложением:

 now
------------
 2019-11-08
db=# SET TIME ZONE 'australia/perth';
SET
db=# SELECT now()::date;
now
------------
2019-11-09

Если вы не уверены, с каким часовым поясом работаете, вы можете делать это неправильно.

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

SELECT (timestamp at time zone 'asia/tel_aviv')::date, COUNT(*)
FROM sale
GROUP BY 1;

За установку часового пояса обычно отвечает приложение. Например, чтобы получить часовой пояс, используемый psql:

db=# SHOW timezone;
TimeZone
----------
Israel
db=# SELECT now();
now
-------------------------------
2019-11-09 11:41:45.233529+02

А чтобы установить часовой пояс в PSQL:

 db=# SET timezone TO 'UTC';
SET
db=# SELECT now();
now
-------------------------------
2019-11-09 09:41:55.904474+00

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

Избегайте преобразований в индексированных полях

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

SELECT * FROM sale
  WHERE created at time ZONE 'asia/tel_aviv' > '2019-10-01'
);
QUERY PLAN
----------
Seq Scan on sale (cost=0.00..510225.35 rows=4523386 width=276)
Filter: timezone('asia/tel_aviv', created) > '2019-10-01 00:00:00'::timestamp without time zone

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

Один из способов использования индекса в этом случае — применить преобразование в правой части:

SELECT * FROM sale WHERE created > '2019-10-01' AT TIME ZONE 'asia/tel_aviv' );
QUERY PLAN
----------
Index Scan using sale_created_ix on sale  (cost=0.43..4.51 rows=1 width=276)
Index Cond: (created > '2019-10-01 00:00:00'::timestamp with time zone)

Другим примером использования дат является фильтрация определенного периода:

 db=# (
 SELECT * FROM sale WHERE created + INTERVAL '1 day' > '2019-10-01'
);
QUERY PLAN
----------
 Seq Scan on sale  (cost=0.00..510225.35 rows=4523386 width=276)
   Filter: ((created + '1 day'::interval) > '2019-10-01 00:00:00+03'::timestamp with time zone)

Как и до этого, интервальная функция в поле created препятствовала базе данных использовать индекс. Чтобы база данных использовала индекс, примените преобразование в правой части выражения вместо всего поля:

 SELECT *
  FROM sale
  WHERE created > '2019-10-01'::date - INTERVAL '1 day'
);
QUERY PLAN
----------
 Index Scan using sale_created_ix on sale  (cost=0.43..4.51 rows=1 width=276)
   Index Cond: (created > '2019-10-01 00:00:00'::timestamp without time zone)

Заключение

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

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

Новая тема

Ответить

Марат

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

Здравствуйте, помогите пожалуйста с ошибкой. Пытаюсь сформировать : Отчеты — Анализ учета по НДС

{ОбщийМодуль.ДлительныеОперации.Модуль(376)}: Ошибка при выполнении операции над данными:
Microsoft SQL Server Native Client 11.0: Обнаружена ошибка: деление на ноль.
HRESULT=80040E14, SQLSrvr: SQLSTATE=22012, state=1, Severity=10, native=8134, line=1

            ВызватьИсключение(ТекстОшибки);

Произошло после перехода из 8.3.10(ошибки не было) на 8.3.13.
Версия 1с серверная. Документов сотни тысяч, просматривать руками каждый — не вариант.

Тестово перевел из серверной в файловую базу — на удивление все заработало БЕЗ Ошибок.

Vladko

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

Марат,посмотрите как на последней 8.3.12 работает. Здесь ясно, что это — глюк платформы. Если ошибка повторится, то напишите на горячую линию 1С сообщение об ошибке.

Марат

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

Я писал. Мне говорят проверить все документы (а их огромное количество с незапамятных времен).

Вот ответ от v8 V8@1c.ru

При формировании отчета «Анализ учета по НДС» выдается сообщение об ошибке «Обнаружена ошибка: деление на ноль» если в информационной базе присутствуют документы следующих видов: -Поступление на расчетный счет, -Списание с расчетного счета, -Операция по платежной карте -Поступление наличных со значением кратности взаиморасчетов равной нулю.

Отчет не формируется даже за сегодняшнюю дату, при условии что ни одного действия с документами не было произведено.
На 8.3.12 не могу проверить, так как нет этой платформы. я обновлялся с 8.3.10 напрямую до 8.3.13.Но суть в том, что в 8.3.10 все работало.

Марат

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

Ошибку исправил. Сделал универсальный отчет, установил на все документы условие: Кратность = 0.
Поправил все документы — Внимание, учитываются даже НЕ проведенные документы.

Vladko

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

Марат,в универсальном отчете можно тоже добавить в отбор условие на проведённость документа

Марат

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

Vladko, Спасибо, буду знать. Дальше копать в отчете не стал, проблема нашлась и решилась.

EvJ2019

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

Марат пишет:

Цитата

              Ошибку исправил. Сделал универсальный отчет, установил на все документы условие: Кратность = 0. Поправил все документы — Внимание, учитываются даже НЕ проведенные документы.

Добрый день. А какие документы вы поправляли, скажите, пожалуйста?
Такая же ошибка. Но в документах Поступление на расчетный счет, Списание с расчетного счета, Операция по платежной карте, Поступление наличных нет реквизита Кратность взаиморасчетов.

Valentin46

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

Марат пишет:

Цитата
На 8.3.12 не могу проверить, так как нет этой платформы. я обновлялся с 8.3.10 напрямую до 8.3.13.

А это как понимать, нельзя ли пояснить?

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

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

  • Использование функции NULLIF()
  • Использование оператора CASE
  • Использование ВЫКЛ. ARITHABORT

Сначала мы создадим базу данных для выполнения операций SQL.

Запрос:

CREATE DATABASE Test;

Выход:

Успешно выполненные команды показывают, что база данных «Тест» создана.

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

DECLARE @Num1 INT;
DECLARE @Num2 INT;

После объявления переменной мы должны установить значения. Установите значение второй переменной равным нулю.

SET @Num1=12;
SET @Num2=0;

Способ 1: использование функции NULLIF()

Если оба аргумента равны, возвращается NULL. Если оба аргумента не равны, возвращается значение первого аргумента.

Синтаксис:

NULLIF(exp1, exp2);

Теперь мы используем функцию NULLIF() в знаменателе с нулевым значением второго аргумента.

SELECT @Num1/NULLIF(@Num2,0) AS Division;
  • На сервере SQL, если мы разделим любое число на значение NULL, его вывод будет NULL.
  • Если первый аргумент равен нулю, это означает, что если значение Num2 равно нулю, то функция NULLIF() возвращает значение NULL.
  • Если первый аргумент не равен нулю, то функция NULLIF() возвращает значение этого аргумента. И деление происходит как обычно.

Вот полный запрос.

Запрос:

DECLARE @Num1 INT;
DECLARE @Num2 INT;
SET @Num1=12;
SET @Num2=0;
SELECT @Num1/NULLIF(@Num2,0) AS Division;

Выход:

Способ 2: использование оператора CASE

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

Мы должны проверить значение знаменателя, то есть значение переменной Num2. Если он равен нулю, верните NULL, в противном случае верните обычное деление.

SELECT CASE
WHEN @Num2=0
THEN NULL
ELSE @Num1/@Num2
END AS Division;

Вот полный запрос:

Запрос:

DECLARE @Num1 INT;
DECLARE @Num2 INT;
SET @Num1=12;
SET @Num2=0;
SELECT CASE
    WHEN @Num2=0
    THEN NULL
    ELSE @Num1/@Num2
END AS Division;

Выход:

Способ 3: ВЫКЛЮЧИТЕ ARITHABORT

Чтобы контролировать поведение запросов, мы можем использовать методы SET. По умолчанию ARITHABORT включен. Он завершает запрос и возвращает сообщение об ошибке. Если мы установим его в OFF, он завершится и вернет значение NULL.

Как и в случае с ARITHBORT, мы должны установить ANSI_WARNINGS в положение OFF, чтобы избежать появления сообщения об ошибке.

SET ARITHABORT OFF;
SET ANSI_WARNINGS OFF;

Вот полный запрос:

Запрос:

SET ARITHABORT OFF;
SET ANSI_WARNINGS OFF;
DECLARE @Num1 INT;
DECLARE @Num2 INT;
SET @Num1=12;
SET @Num2=0;
Select @num1/@Num2;

Выход:

0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

А вот еще интересные материалы:

  • Яшка сломя голову остановился исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного исправьте ошибки
  • Ясность цели позволяет целеустремленно добиваться намеченного где ошибка
  • Обнаружена ошибка на устройстве device harddisk3 dr3 во время выполнения операции страничного обмена
  • Нужно сказать должное идее президента речевая ошибка