Данный материал является иллюстрацией способов работы с запросами, использующими методику вычисления «нарастающих итогов». Также в данной статье рассматриваются вопросы практического использования запросов такого рода при партионном учете и расчете задолженностей.
ВНИМАНИЕ:
Файлы из Базы знаний - это исходный код разработки.
Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы.
Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных.
Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.
На написание данной работы меня вдохновила работа @glassman «Переход на ClickHouse для анализа метрик». Автор анализирует большой объем данных, много миллионов строк, и убедительно доказывает, что ClickHouse справляется лучше PostgreSQL.
Я же покажу как можно сократить объем данных в 49.9 раз при этом:
1. Сохранить значения локальных экстремумов
2. Отклонения от реальных значений имеют наперед заданную допустимую погрешность.
Что ж... лучше поздно, чем никогда.
Подсистема 1С для работы с регулярными выражениями: разбор выражения, проверка на соответствие шаблону, поиск вхождений в тексте.
В статье анализируются средства платформы для решения системы линейных уравнений в 1С. Приводятся доводы в пользу некорректной работы встроенных алгоритмов, а значит потенциально некорректного расчета себестоимости в типовых конфигурациях.
Обычно под распределением понимают определение сумм пропорционально коэффициентам. Предлагаю включить сюда также распределение по порядку (FIFO, LIFO) и повысить уровень размерности до 2-х. 1-ое означает, что распределение может быть не только пропорциональным, но и по порядку, а 2-ое - это вариант реализации матричного распределения: по строкам и столбцам. Возможно вас заинтересует также необычное решение этой задачи через создание DSL на базе реализации текучего интерфейса
За рекламу - спасибо.
Пробежал мельком . Пока замечание :
в статье никак не отмечено : использование нарастающих итогов "в лоб"
(т.е. использвание содинения по неравенству в запросе) для больших таблиц приведет к "падению" всего алгоритма.
По оформлению : лучше оформить статью на ИС как полагается , а не предлагать файл со статьей к скачиванию.
Формально эта статья опубликована на http://www.nashe1c.ru/materials-view.jsp?id=383 (если кому "лень качать файло" :) ).
Позже еще покопаюсь здесь - не нашел сразу как вставлять в статью картинки при публикации на этом сайте.
To Ish_2: аргументируйте, пожалуйста, как именно может повлиять размер таблиц на работу этого алгоритма?
вместо простого прибавления к сумме остатков по предыдущим партиям текущего остатка мы прибавляем функцию «MAX» от текущего остатка. Это необходимо по правилу использования группировок в запросе – все данные, не вошедшие в поля группировки, должны выражаться через функции языка запросов.
Ошибка. То есть использование функции "МАХ" в данном случае не является необходимым Правильно :
На всякий случай уточню, что представленный в статье вид запроса как-раз и позволяет сильно ограничить количество записей в результирующем наборе. Сам по себе запрос может быть транслирован в SQL без изменений и, тем самым, позволяет работать с большими начальными таблицами, получая только необходимые выходные данные (без дополнительных обработок в последующих запросах или в текстах модулей).
(6) В конфигурации прикрепленной к теме "ФИФО для любопытных" http://infostart.ru/public/68225/ использован именно такой запрос без МАХ.
Почему для больших таблиц соединение по неравенству работает оч. медленно можно прочитать в теме "Подведем итоги.Нарастающме" .
Можно убедиться в этом самостятельно : создайте таблицу tab1 с количеством записей 1 000 000 .
Запустите Ваш запрос - соединение по неравенству tab1 "сама с собой" засеките время. И всё поймете... назавтра.
(7)В упомянутой конфигурации используется конструкция вида:
SELECT
Поле1,
СУММА( Поле2 ) + Поле1
FROM
GROUP BY Поле1
Здесь да, наличие MAX/MIN не нужно. Но в моем запросе используется поле, по которому отсутствует группировка. Так что это поле ОБЯЗАТЕЛЬНО должно включаться в выражения запроса в виде функции языка запросов. Насчет скорости исполнения - я проведу такие испытания, но еще раз скажу, что суммирование или простое сравнение по индексу для SQL выполняется очень быстро. Размер таблиц в миллион записей никак не влияет на скорость исполнения. Возможно здесь будет влиять сама 1С, но это уже другой вопрос.
(9) Мда.. Зачем спорить ? Вы попробуйте.
Вот Ваш вариант с "МАХ" :
SELECT
tab1.Номенклатура,
tab1.Партия,
( SUM( ISNULL( tab2.КоличествоОстаток, 0 ) ) + MAX( tab1.КоличествоОстаток ) ) AS НарастающийИтог
FROM РегистрНакопления.ОстаткиНоменклатуры.Остатки AS tab1
LEFT JOIN РегистрНакопления.ОстаткиНоменклатуры.Остатки AS tab2
ON ( tab1.Номенклатура = tab2.Номенклатура ) AND ( tab1.Партия > tab2.Партия )
GROUP BY tab1.Номенклатура, tab1.Партия;
Вот мой без "МАХ":
SELECT
tab1.Номенклатура,
tab1.Партия,
SUM( ISNULL( tab2.КоличествоОстаток, 0 ) ) + tab1.КоличествоОстаток AS НарастающийИтог
FROM РегистрНакопления.ОстаткиНоменклатуры.Остатки AS tab1
LEFT JOIN РегистрНакопления.ОстаткиНоменклатуры.Остатки AS tab2
ON ( tab1.Номенклатура = tab2.Номенклатура ) AND ( tab1.Партия > tab2.Партия )
GROUP BY tab1.Номенклатура, tab1.Партия;
(12) Конечно НЕТ, не получилось! :) Не позорьтесь, почитайте документацию, попробуйте сами подобные запросы. Это общее правило SQL запросов и оно незыблемо :)
(14) Нет, я правда попробывал :) Честно-честно! :) 1С мне написало "Поле не входит в группу "tab1.КоличествоОстаток"". На всякий случай: платформа 8.2.14.528
(17) Вот! А теперь просто уберите строчку "Приход.Количество" из блока "СГРУППИРОВАТЬ ПО" :) Еще раз: у меня используется поле, НЕ входящее в группировку.
(14) Я тут подумал: может Вы просто перепутали конструкции запроса? То есть существует такая конструкция как "ИТОГИ Поле1, СУММА(Поле2) ПО Поле1". Вот там в самом тексте запроса функции употреблять не надо - они применяются только в итогах. На всякий случай: по-английски это пишется как "TOTALS Поле1, SUM(Поле2) BY Поле1".
Я хотел бы внимание обратить на другую ошибку. В запросе оперируют данными виртуальной таблицы Остатки, что методологически неверно, т.к. остатки - это УЖЕ результат расчета с нарастающими итогами. И использование остатков в запросе с нарастающими итогами чрезвычайно ограничено.
Пока я не вижу новизну в статье, а только ошибку.
Такое объединение в запросе существенно ограничено количеством объединяемых записей.
1
2 > 1
3 > 1 + 2
4 > 1 + 2 + 3
5 > 1 + 2 + 3 + 4
в итоге получаем таблицу
1
2 1
3 1
3 2
4 1
4 2
4 3
5 1
5 2
5 3
5 4
Вместо 5 изначальных записей, получили 11. И прогрессия эта даже не геометрическая.
(18) Про "неправильное методологическое поведение" я комментарий опущу. А вот насчет "геометрической прогрессии": в запросе нет объединения "все со всеми", соединение с "правой" таблице нужно только для вычисления функции суммы по остаткам.
(29) в регистре накопления есть Вид движения Приход и Расход. Знак при этом не меняется. Это в запросе можно получить уже Приход = Количество, а Расход = Количество*-1 или в две отдельные колонки. Знак тут не принципиален. Главное невразумительность сложения ОСТАТКОВ.
(32) Тут хитрость методологии.
Складываются остатки не по операциям движений по регистрам, а остатки по партиям. Т.е. для списания нужно набрать пул списываемых партий и, собственно, всё.
Простая задачка. Не нужно усложнять.
(34) Конечно, ничего сложного! Просто мне было нужно, чтобы все вычислялось в рамках одного запроса, а не 7-ми как в http://infostart.ru/public/61295/. Я долго копался в Интернете, думал, что кто-то ведь наверняка должен был до этого додуматься - не нашел. Если есть ссылки на другие варианты решения задач, описанных в моей статье - буду очень благодарен!
(35) решение "в лоб" опубликовано на мисте уже давно. Я ещё повторю, ничего нового, кроме непонятного практического значения остаток по партии + сумма остатков партий до него. Хотя нет. Понял. Новизны нет. Всё тот же запрос в "лоб"
(37) Это, собственно, нарастающий итог остатка :) На определенном значении он превысит необходимое количество для списания и искомый список партий для списания будет готов.
(22) а про объединение...Вот именно, что ВЫЧИСЛЕНИЕ. Для каждой из 5 записей будет производится свой расчет суммы с увеличением числа слагаемых.
т.е. будет не 1+2+3+4+5 = 5 действий сложений, как если это делать с помощью цикла в коде, а будет 11 действий сложений. И число операций сложений будет расти ОЧЕНЬ быстро.
(28) Скажем так: подсчет суммы по любому числовому полю любой SQL таблицы с помощью запроса и функции SUM() вычисляется в разы быстрее, чем с помощью перебора всех строк и дальнейшего подсчета суммы с помощью любых алгоритмов. Я придерживаюсь этого мнения.
(31) Вопрос в том, что sum() будет вызываться столько раз, сколько записей в таблице. И каждый раз ему для расчетов будет передаваться такое число все большее число записей. В отличие от этого подхода, в цикле можно хранить предыдущий итог и прибавлять уже к нему (в sql это тоже можно организовать, но речь идет о запросе 1с, которые не умеет делать такого). В результате наступит такой момент, когда время выполнения Sum() с n записей будет выполнятся дольше, чем сложение двух цифр ПромежуточныйИтог + ТекущееЧисло в очередной итерации цикла. Это одна сторона. Другая сторона, что вообще время выполнения запроса 1с СУЩЕСТВЕННО увеличится. Проблема такого подхода в запросе, что вычисления для каждой записи каждый раз выполняеются ЗАНОВО с возрастающей нагрузкой на выч.ресурсы. В коде же нагрузка постоянна (ну кроме, увеличения объема памяти под хранения итогов).
Запрос "в лоб" выигрывает на маленьких объемах данных, цикл на больших. Но они оба проигрывают оптимизированному алгоритму запроса 1с или прямому запросу sql с организацией цикла прямо в запросе.
(36) Не согласен! Использование "курсоров" вместо функций языка запросов - основная проблема производительности. Кто-то здесь уже делал тестовую базу под нарастающие итоги и проверял скорость работы запросов. Я поищу кто это был и постараюсь представить документальные доказательства.
Приведу преимущества приведенного в моей статье подхода:
1) Использование только одного запроса для получения полного списка партий для списания для всего перечня номенклатуры;
2) Относительная простота применяемого запроса для понимания;
3) Применение функций языка запросов для ускорения обработки вместо прямого перебора в коде;
4) Возможность дальнейшей оптимизации использования данного запроса;
(45)(46) Насчет "полный код процедуры списания на замену штатному":
1) Выполняем запрос, получаем все реквизиты для формирования движений списания (период, вид движений, номенклатура, партия, количество, сумма);
2) Загружаем в реквизит объекта документа "Движения" результат выполнения запроса;
3) Записываем "Движения";
Провести полноценный тест запроса не имею возможности. Но чисто теоретически он должен выполнятся крайне быстро и эффективно. Я верю в него :)
(48) Можно сделать и для всех документов сразу: в качестве "таблицы ограничений" для моего запроса можно использовать таблицу с полями "регистратор + номенклатура + количество списания" и в самом запросе просто добавить еще одну группировку по регистратору. Вот и все :)
(44)На мой взгляд приведенный "запрос в лоб на мисте" выглядит неоптимальным: применяется вложенный запрос по периодом (получается список неповторяющихся периодов), потом соединяется с полным списком периодов (не уникальный список периодов), а потом еще и группируется вновь по периодам, подсчитывая сумму.
Более логичным было бы использование такого запроса:
SELECT
tab1.Период КАК Время,
SUM( tab2.СуммаОборот ) КАК Сумма
FROM РегистрНакопления.ПродажиПоДисконтнымКартам.Обороты(, , Регистратор, ДисконтнаяКарта = &Ссылка) AS tab1
LEFT JOIN РегистрНакопления.ПродажиПоДисконтнымКартам.Обороты(, , Регистратор, ДисконтнаяКарта = &Ссылка) AS tab2
ON tab1.Период >= tab2.Период
GROUP BY tab1.Период
(49) это просто подход к проблеме - простое соединение по условию ">="
и вообще вложенные запросы признаны самой 1с неэффективными. Нужно использовать пакетные запросы. Кстати, формально, те 7 запросов, объединенные в качестве пакетных, являются 1 запросом.
(51) На мисте используется точно такое же соединение по условию ">=" :) В моем варианте запроса исправлены недостатки запроса с мисты.
Еще раз по запросу на мисте:
1) В первом вложенном запросе выбирается список уникальных периодов;
2) Затем во втором вложенном запросе выбирается полный список периодов () вместе с их суммами оборотов;
3) Затем эти два подзапроса соединяются: получается что весь список уникальных периодов соединяется со списком неуникальных периодов;
4) Затем происходит группировка по периодом, то есть опять происходит процедура формирования списка уникальных периодов;
Очевидно, что запрос на мисте не оптимален.
Насчет "7 запросов, объединенных в качестве пакетных": у меня запрос только один :) Судя по текстовому алгоритму из упоминаемой статьи этот алгоритм может быть разбит МИНИМУМ на 3 подзапроса. Еще раз - у меня эта задача решена в один запрос, понятный и ясный для понимания.
(51) А вообще-то мой запрос еще можно оптимизировать: если "РегистрНакопления.ПродажиПоДисконтнымКартам" является только оборотным регистром, то можно вместо виртуальной таблицы оборотов и детализации периодов до регистратора использовать просто полный список движений по регистру. Получилось бы еще быстрее и понятнее.
Вот как-то так:
SELECT
tab1.Период КАК Время,
SUM( tab2.СуммаОборот ) КАК Сумма
FROM РегистрНакопления.ПродажиПоДисконтнымКартам AS tab1
LEFT JOIN РегистрНакопления.ПродажиПоДисконтнымКартам AS tab2
ON tab1.Период >= tab2.Период
WHERE (tab1.ДисконтнаяКарта = &Ссылка) AND (tab2.ДисконтнаяКарта = &Ссылка)
GROUP BY tab1.Период
ошибочно.
Как я понял, автор предлагает вместо получения запросом таблицы партий из регистра накопления "остатки партий товаров" и последующего построения в цикле таблицы списания партий, решать задачу в запросе. Не вижу в этом проблемы. Думаю, что в большинстве решений, поддерживающих партионный учет, так и сделано.
Чаще всего таблице ненулевых остатков партий (по одной номенклатуре) - одна - две строки, что там оптимизировать?
(54) В моей статье в качестве последнего запроса приведено прямое решение для задачи из http://infostart.ru/public/61295/. Приведенное мною решение более компактно и понятно. Использования запросов такого вида где-либо еще (в типов ли конфигурациях или просто на просторах Интернета) я не нашел - буду благодарен за любую ссылку на подобный материал!
(55) Вышел с "больничного".
Реализация метода ФИФО предполагает получение движений для всех документов расхода за период.
Именно в такой постановке рещается задача в "ФИФо для любопытных".
В Вашей постановке задачи априори мы имеем таблицу, определяющую количество списания , с колонками :
• Столбец «Номенклатура»;
• Столбец «Количество» - общее количество, которое должно быть списано со всех партий для данной номенклатурной единицы;
Т.е. здесь мы имеем суммарное по всем документам расхода количество для списания - это серьезное упрощение
задачи. Вычислить движения по партиям нужно ведь в разрезе каждого документа расхода .
На практике при реализации метода ФИФО на входе имеем не таблицу с итогами количества по списанию ,
а таблицу с колонками
• Столбец «ДокументРасходнаяНакладная»;
• Столбец «Номенклатура»;
• Столбец «Количество» - количество списания по накладной
А это значит на выходе мы должны получить НЕ Вашу таблицу с колонками :
• Столбец «Номенклатура»;
• Столбец «Партия»;
• Столбец «КоличествоСписания» - количество списания по накладной
• Столбец «СуммаСписания» - количество списания по накладной
А другую таблицу
• Столбец «ДокументРасходнаяНакладная»;
• Столбец «Номенклатура»;
• Столбец «Партия»;
• Столбец «КоличествоСписания» - количество списания по накладной
• Столбец «СуммаСписания» - количество списания по накладной
(55) Вспомнилась фраза из старого фильма, сказанная со смешной интонацией - "Не вижу препятствий!" :) Этот вопрос я уже обсуждал в (50): очень просто вместо таблицы только с номенклатурой и количеством получить таблицу с документом, всей номенклатурой и всеми количествами. Точно также используем ее в запроса, добавляя только группировку по регистратору. То есть в "ТаблицеНоменклатуры" будет еще одно поле - "РасходнаяНакладная" (или лучше назвать это поле сразу "Регистратор" для последующей выгрузки результата запроса в набор записей движений).
Повторюсь - запросы в статье открыты для экспериментов и оптимизаций, все в Ваших руках! :)
(57) Статья была обозначена как альтернатива статье "Фифо для любопытных".
Я из этого и исхожу.
Но Ваша постановка задачи есть частный случай и сильное упрощение постановки задачи в "ФИФО для любопытных".
Т.е. никакая НЕ альтернатива.
С другой стороны , если Вы представите текст запроса для случая , когда на входе
таблица
• Столбец «ДокументРасходнаяНакладная»;
• Столбец «Номенклатура»;
• Столбец «КоличествоСписания»;
нам будет легко сравнивать альтернативные запросы.
И будет о чем поговорить. Пока нечего сравнивать.
(58) Моя статья предназначена для демонстрации другого способа получения списка партий для списания вместе со всеми количествами/суммами. Демонстрация превосходств того или иного метода - тема для отдельного исследования и статьи :) Возможно я этим и займусь, но позже.
Но все, что нужно для модификации моего запроса я уже даже описал сам :)
(59) Жаль.
1. При решении задачи полноценного ФИФО , т.е. получения движений для каждого документа расхода Вас ждут сюрпризы. И описанной Вами модификацией - лишь "добавляя только группировку по регистратору" - Вы ничего не добьетесь.
2. Остался без проверки главный подводный камень, на который налетают при использовании соединения по неравенству. При приличных объемах входных данных Ваш алгоритм - "рухнет".
См. Пример с таблицей 1 000 000 записей, которая соединяется сама с собой по неравенству.
Если бы было найдено решение , учитывающее 1 и 2 и которое принципиально отличалось бы от уже опубликованных на ИС - тогда бы мы поговорили всерьез.
(78) обязательно нужно указать в статье, что запросы эти ресурсоёмкие, у чем больше записей в начальный таблицах, тем дольше он будет выполняться - по нарастающей. Я не привык читать сложные запросы из текста, но постараюсь разобраться в них.
(80) anig99, насчет "ресурсоемкости" - проверки на эту характеристику для моих запросов у меня отсутствуют. Если кто-то смог бы их проверить на больших объемах данных (с выкладыванием статистики) то я был бы очень благодарен.
(81) насчет статистики не могу помочь пока. Но запрос с нарастающими итогами по взаиморасчетам за 3 года выполнялся около 30 минут. Просто логически рассуди - к каждой записи присоединяются все предыдущие и конечная таблица перед группировкой возрастает многократно. Представь 10 записей в начальной - это 1 + (1+1) + (1 + 1 + 1) + (1 + 1 + 1 + 1). Формула f(n) = (n² + n) / 2. 10 объединяемых записей выльется в 55 записей, которые нужно сгруппировать, 100 в 5050
(82) anig99, то, что я привел в статьях это вычисление нарастающего итога с одновременным применением больших ограничений на размер выборки. Так что однозначно говорить о "ресурсоемкости" приведенных мною запросов нельзя, по-крайней мере нельзя без предварительных практических экспериментов.
(83) даже жесткие ограничения на выборку могут не помочь - слишком быстро растёт количество объединяемых записей. Попробую получше посмотреть запросы и определить гарантируют ли ограничения оптимизацию выполнения.
(82) Кхы.. кхы..
Сумму членов арифметической прогресии находил когда -то маленький Гаусс на уроке:
(А1+АN)*N/2
Если в начальной таблице 100 записей, то при соединении каждой записи со всеми предыдущими получаем :
А1=1,АN=100,N=100.
(1+100)*100/2 = 5050
Итак ,При соединении каждой записи со всеми предыдущими получаем получаем в итоговой таблице 5050 записей.
Промежуточная таблица возросла в 55 раз.
(83) Игнорировать такое возрастание невозможно. Любые алгоритмы , игнорирующие это обстоятельство нельзя рассматривать всерьез.
(86) Я к эксперименту и призываю . Месяц назад в (7) был предлжено :
Можно убедиться в этом самостятельно : создайте таблицу tab1 с количеством записей 1 000 000 .
Запустите Ваш запрос - соединение по неравенству tab1 "сама с собой" засеките время. И всё поймете... назавтра.
(90) Могу что-нибудь потестировать. :)
Есть все версии платформы и почти все типовые конфигурации фирмы 1С.
Могу развернуть как файловые, так и SQL базы данных (кроме Oracle).
(92) alexk-is, супер! К статье Получение реквизитов движений для множества документов в рамках одного запроса прикреплена конфигурация. Там все очень "заточено" под одну задачу, но тем не менее данная конфигурация подходит для тестирования. Если у Вас получиться прогнать на ней нагрузочное тестирования и представить результаты я буду очень благодарен.
(93) Скачал. Установил. Как её подготовить к нагрузочным тестам? И где эти тесты?
Вообще-то я расчитывал на что-то вроде этого http://forum.infostart.ru/forum26/topic41810/message451107/#message451107 В плане того, что мне останется только понажимать кнопочки и опубликовать результаты. А тут получается, что нужно и данные набить, и методику нагрузочного тестирования придумать?
(94) alexk-is, хорошо, я приготовлю как специализированную конфигурацию, так и все остальные обработки. На следующей неделе постараюсь сделать и известить Вас.
(95) Возможно, что специализорованная конфигурация и не нужна вовсе. Нужны обработки для заполнения базы данных и обработки для тестирования. Конфигурацию для тестирования можно взять любую. Даже ту, что уже есть.
(96) alexk-is, на самом деле я решил провести тестирование сам на SQL Server 2008R2: сделаю имитацию таблиц партий и документов, затем заполню необходимым количеством данных и запущу запрос. Заодно нормально просмотрю план выполнения запроса.
(60) Насчет "алгоритм рухнет". На самом деле "сравнение по неравенству" применяется в базах повсеместно. Прежде всего при сортировке строк - ведь всем известно, что любые алгоритмы сортировки используют "сравнение по неравенству", иначе отсортировать строки невозможно. Естественно, что для эффективной сортировки необходимо чтобы на сортируемых столбцах стоял уникальный индекс. Сортировка будет работать и без него, вот только в этом случае конечно "алгоритм рухнет" - будет выполнятся ооочень долго и с огромными затратами памяти. Но все в наших руках, и установить индекс по измерению регистра мы можем. Так что все впорядке :)
(66) Разумеется , предварительно для таблицы, которая соединятся сама с собой , создается индекс по полю соединения. Лишь после получения индекса мы запускаем соединение "сама с собой" и засекаем время (таблица
имеет 1 000 000 записей).
..Дольше мне писать , чем Вам попробовать.
(68) Дык вот попробывать то нет возможности, я бы с радостью - у меня только "Версия для обучения программированию" со встроенным 1С ограничением размера таблиц :) Как только найду нормальную систему - обязательно попробую!
- опять ошибаетесь!
Вы решаете другую задачу.
Вы считаете известной на ткущий момент ТаблицуДолгов с колонками: Контрагент, Накладная, СуммаДолга.
Чтобы ее получить, нужно один за другим последовательно провести документы по регистру взаиморасчетов по документам расчетов.
В решении http://infostart.ru/public/61295/ этого не требуется.
То есть оно работает при отсутствии в конфигурациях регистра взаиморасчетов по документам расчетов.
Как, кстати и не требуется перепроведения при изменении документов задним числом.
Другая задача - другие методы, как можно сравнивать?
Вы привели очевидное решение своей задачи.
Очевидное потому, что по-другому запрос и не записать, поставив задачу получить запросом списываемые партии из регистра партий.
И если бы не заблуждения относительно того, что это решение чего-то проще и компактней, то статья вполне может здесь пригодиться.
(64) Именно так! - Путаете! В http://infostart.ru/public/61295/ в таблице долги нет колонки "Накладная".
Там для одного контрагента - одна строка: Сумма долга (текущая) и дата отсрочки.
Не знаю, насколько кому это решение известное.
Оно очевидное.
Потому что любой опытный программист напишет такое же.
Труднее написать по-другому.
Ну а очевидность для публикации здесь не препятствие.
(54) (63) (65) - в этих комментариях я ошибся в части задачи задолженности :cry: : задача о просроченной задолженности одна и та же!
Тем не менее, когда документов по одному контрагенту станет достаточно много,
приведенный запрос будет работать слишком долго - зависимость от числа документов квадратичная (показано в комментарии (18)).
А ведь срок давности документов в запросе не ограничен.
А вот запрос для списания партий вполне рабочий: ведь не списанных партий обычно немного (чаще даже одна).
(71) внимания, как я уже говорил, статья достойна.
Цель моих комментариев - исправить замеченные неточности относительно "альтернативности" подхода.
То есть в итоге мои выводы таковы:
1. для просроченных долгов запрос в приведенном виде не годится. Это не альтернатива.
2. для списания партий в самописных конфигурациях, если считать, что решается другая и даже более распространенная задача, вполне подходит.
Мог бы изложить свое понимание того, почему в типовых используется предварительная выгрузка остатков в таблицу, но не буду отвлекать от темы.
(76) Приведенный запрос успешно посчитает "просроченные долги" при любом количестве документов. Долго ли, али нет - дело ума того, кто будет применять это на практике :)
Вопросы оптимизации умышленно вынесены за рамки моей статьи в угоду простоте изложения материала и его понятности, о чем и сказано в последнем абзаце текста статьи.
(71) В моей статья я показал другой метод получения списка партий для списания, отличный от всего, что я нашел на просторах Интернета. Он реально работает, он компактен и по своему красив. Среди всего множества людей наверняка есть и такие, кто его уже знают - я это допускаю. Кому-то он поможет, кому-то нет. Но своей цели я уже достиг - несколько человек посчитали статью стоящей и полезной.
Я полностью уверен, что при любых условиях хранения движений (остатков/оборотов), будь то регистр или справочник - этот запрос будет работать. Еще раз - доказывать кому-то что мой запрос лучше или хуже у меня цели нет. Считаете, что моя статья недостойна внимания - я буду работать с другими, которым она полезна.
Я плюсую за простоту кода и его дальнейшую понятность для остальных программистов в случае доработок даже без коммментариев.
Сегодня этот код успешно применен и протестирован на рабочей базе.
Оговорюсь, что вместо виртуальных таблиц Обороты использовала уже заранее сформированные временные таблицы в одном немаленьком запросе (таким образом СКЛю будет несложно выбрать оптимальный план запроса).
Так как запрос изначально был немаленьким, то вставлять в него еще 7 подзапросов считаю перегрузом. А так всего десяток строк - и наглядно и просто. Keep it simple!
P.S. У меня в соединяемых временных таблицах никогда не будет 1 000 000 записей, поэтому не боюсь, что запрос рухнет на огромном массиве данных.