Вступайте в нашу телеграмм-группу Инфостарт
Использование нарастающих итогов в партионном учете и не только
Разработка - Математика и алгоритмы
Фактически в данной статье рассматриваются альтернативы запросам, приведенным в статьях http://infostart.ru/public/61295/ и http://infostart.ru/public/68225/.
Полный текст статьи можно также найти на http://nashe1c.ru/materials-view.jsp?id=383.
Файлы
ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.
Подписка PRO — скачивайте любые файлы со скидкой до 85% из Базы знаний
Оформите подписку на компанию для решения рабочих задач
Оформить подписку и скачать решение со скидкойСм. также
Математика и алгоритмы Программист 1C v8.2 1C:Бухгалтерия Россия Абонемент ($m)
На написание данной работы меня вдохновила работа @glassman «Переход на ClickHouse для анализа метрик». Автор анализирует большой объем данных, много миллионов строк, и убедительно доказывает, что ClickHouse справляется лучше PostgreSQL. Я же покажу как можно сократить объем данных в 49.9 раз при этом: 1. Сохранить значения локальных экстремумов 2. Отклонения от реальных значений имеют наперед заданную допустимую погрешность.
1 стартмани
30.01.2024 11875 stopa85 12
Математика и алгоритмы Бесплатно (free)
Разработка алгоритма, построенного на модели симплекс-метода, для нахождения оптимального раскроя.
19.10.2023 18766 user1959478 57
Математика и алгоритмы Разное 1С v8.3 1C:Бухгалтерия Россия Абонемент ($m)
Расширение (+ обработка) представляют собою математический тренажер. Ваш ребенок сможет проверить свои знание на математические вычисление до 100.
2 стартмани
29.09.2023 11380 maksa2005 8
Математика и алгоритмы Инструментарий разработчика Программист 1С v8.3 Мобильная платформа Россия Абонемент ($m)
Что ж... лучше поздно, чем никогда. Подсистема 1С для работы с регулярными выражениями: разбор выражения, проверка на соответствие шаблону, поиск вхождений в тексте.
1 стартмани
09.06.2023 18838 11 SpaceOfMyHead 20
Математика и алгоритмы Программист 1С v8.3 1C:Бухгалтерия Бесплатно (free)
Три задачи - три идеи - три решения. Мало кода, много смысла. Мини-статья.
03.04.2023 12893 RustIG 9
Механизмы платформы 1С Математика и алгоритмы Программист 1С v8.3 Россия Бесплатно (free)
В статье анализируются средства платформы для решения системы линейных уравнений в 1С. Приводятся доводы в пользу некорректной работы встроенных алгоритмов, а значит потенциально некорректного расчета себестоимости в типовых конфигурациях.
23.11.2022 11988 gzharkoj 15
Математика и алгоритмы Программист 1С v8.3 Россия Абонемент ($m)
Обычно под распределением понимают определение сумм пропорционально коэффициентам. Предлагаю включить сюда также распределение по порядку (FIFO, LIFO) и повысить уровень размерности до 2-х. 1-ое означает, что распределение может быть не только пропорциональным, но и по порядку, а 2-ое - это вариант реализации матричного распределения: по строкам и столбцам. Возможно вас заинтересует также необычное решение этой задачи через создание DSL на базе реализации текучего интерфейса
1 стартмани
21.03.2022 11123 8 kalyaka 11
Математика и алгоритмы Программист 1С v8.3 Бесплатно (free)
Дополнение по формату файлов конфигурации (*.cf) в версии 8.3.16.
16.12.2021 14150 fishca 12
Пробежал мельком . Пока замечание :
в статье никак не отмечено : использование нарастающих итогов "в лоб"
(т.е. использвание содинения по неравенству в запросе) для больших таблиц приведет к "падению" всего алгоритма.
По оформлению : лучше оформить статью на ИС как полагается , а не предлагать файл со статьей к скачиванию.
Позже еще покопаюсь здесь - не нашел сразу как вставлять в статью картинки при публикации на этом сайте.
To Ish_2: аргументируйте, пожалуйста, как именно может повлиять размер таблиц на работу этого алгоритма?
В объяснении текста запроса
SELECT
tab1.Номенклатура,
tab1.Партия,
( SUM( ISNULL( tab2.КоличествоОстаток, 0 ) ) + MAX( tab1.КоличествоОстаток ) ) AS НарастающийИтог ...Написано :
Ошибка. То есть использование функции "МАХ" в данном случае не является необходимым
Правильно :
SELECT
tab1.Номенклатура,
tab1.Партия,
SUM( ISNULL( tab2.КоличествоОстаток, 0 ) ) + tab1.КоличествоОстаток AS НарастающийИтог ...использован именно такой запрос без МАХ.
Почему для больших таблиц соединение по неравенству работает оч. медленно можно прочитать в теме "Подведем итоги.Нарастающме" .
Можно убедиться в этом самостятельно : создайте таблицу tab1 с количеством записей 1 000 000 .
Запустите Ваш запрос - соединение по неравенству tab1 "сама с собой" засеките время. И всё поймете... назавтра.
SELECT
Поле1,
СУММА( Поле2 ) + Поле1
FROM
GROUP BY Поле1
Здесь да, наличие MAX/MIN не нужно. Но в моем запросе используется поле, по которому отсутствует группировка. Так что это поле ОБЯЗАТЕЛЬНО должно включаться в выражения запроса в виде функции языка запросов. Насчет скорости исполнения - я проведу такие испытания, но еще раз скажу, что суммирование или простое сравнение по индексу для SQL выполняется очень быстро. Размер таблиц в миллион записей никак не влияет на скорость исполнения. Возможно здесь будет влиять сама 1С, но это уже другой вопрос.
Вот Ваш вариант с "МАХ" :
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.Партия;Ну как ? Получилось ?
Проверена в работе конфигурация . Содержит запрос:
|ВЫБРАТЬ
| Приход.Период,
| Приход.МоментВремени,
| Приход.Партия,
| Приход.Номенклатура,
| Приход.Количество,
| Приход.Сумма,
//
| СУММА(Приход1.Количество) - Приход.Количество КАК КоличествоДо, // Внимание ! Приход.КОличество без МАХ
//
| СУММА(Приход1.Количество) КАК КоличествоПосле
|ПОМЕСТИТЬ НарПриход
|ИЗ
| Приход КАК Приход
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ Приход КАК Приход1
| ПО Приход.Номенклатура = Приход1.Номенклатура
| И Приход.МоментВремени >= Приход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. И прогрессия эта даже не геометрическая.
Нач.ост = 0
Приход 10 Остаток = 10
Приход 11 Остаток = 21
Приход 5 Остаток = 26
Расход 1 Остаток = 25
Теперь нарастающий итог для последней строки с суммой остатков будет.... пырым-пым-пым ---- 10 + 21 + 26 = 57... фигня какая-то.
Складываются остатки не по операциям движений по регистрам, а остатки по партиям. Т.е. для списания нужно набрать пул списываемых партий и, собственно, всё.
Простая задачка. Не нужно усложнять.
См. (18)
7 запросов нужно для оптимизации расчетов.
т.е. будет не 1+2+3+4+5 = 5 действий сложений, как если это делать с помощью цикла в коде, а будет 11 действий сложений. И число операций сложений будет расти ОЧЕНЬ быстро.
Запрос "в лоб" выигрывает на маленьких объемах данных, цикл на больших. Но они оба проигрывают оптимизированному алгоритму запроса 1с или прямому запросу sql с организацией цикла прямо в запросе.
1) Использование только одного запроса для получения полного списка партий для списания для всего перечня номенклатуры;
2) Относительная простота применяемого запроса для понимания;
3) Применение функций языка запросов для ускорения обработки вместо прямого перебора в коде;
4) Возможность дальнейшей оптимизации использования данного запроса;
1) Выполняем запрос, получаем все реквизиты для формирования движений списания (период, вид движений, номенклатура, партия, количество, сумма);
2) Загружаем в реквизит объекта документа "Движения" результат выполнения запроса;
3) Записываем "Движения";
Провести полноценный тест запроса не имею возможности. Но чисто теоретически он должен выполнятся крайне быстро и эффективно. Я верю в него :)
Более логичным было бы использование такого запроса:
SELECT
tab1.Период КАК Время,
SUM( tab2.СуммаОборот ) КАК Сумма
FROM РегистрНакопления.ПродажиПоДисконтнымКартам.Обороты(, , Регистратор, ДисконтнаяКарта = &Ссылка) AS tab1
LEFT JOIN РегистрНакопления.ПродажиПоДисконтнымКартам.Обороты(, , Регистратор, ДисконтнаяКарта = &Ссылка) AS tab2
ON tab1.Период >= tab2.Период
GROUP BY tab1.Период
Еще раз по запросу на мисте:
1) В первом вложенном запросе выбирается список уникальных периодов;
2) Затем во втором вложенном запросе выбирается полный список периодов () вместе с их суммами оборотов;
3) Затем эти два подзапроса соединяются: получается что весь список уникальных периодов соединяется со списком неуникальных периодов;
4) Затем происходит группировка по периодом, то есть опять происходит процедура формирования списка уникальных периодов;
Очевидно, что запрос на мисте не оптимален.
Насчет "7 запросов, объединенных в качестве пакетных": у меня запрос только один :) Судя по текстовому алгоритму из упоминаемой статьи этот алгоритм может быть разбит МИНИМУМ на 3 подзапроса. Еще раз - у меня эта задача решена в один запрос, понятный и ясный для понимания.
Вот как-то так:
SELECT
tab1.Период КАК Время,
SUM( tab2.СуммаОборот ) КАК Сумма
FROM РегистрНакопления.ПродажиПоДисконтнымКартам AS tab1
LEFT JOIN РегистрНакопления.ПродажиПоДисконтнымКартам AS tab2
ON tab1.Период >= tab2.Период
WHERE (tab1.ДисконтнаяКарта = &Ссылка) AND (tab2.ДисконтнаяКарта = &Ссылка)
GROUP BY tab1.Период
ошибочно.
Как я понял, автор предлагает вместо получения запросом таблицы партий из регистра накопления "остатки партий товаров" и последующего построения в цикле таблицы списания партий, решать задачу в запросе. Не вижу в этом проблемы. Думаю, что в большинстве решений, поддерживающих партионный учет, так и сделано.
Чаще всего таблице ненулевых остатков партий (по одной номенклатуре) - одна - две строки, что там оптимизировать?
Реализация метода ФИФО предполагает получение движений для всех документов расхода за период.
Именно в такой постановке рещается задача в "ФИФо для любопытных".
В Вашей постановке задачи априори мы имеем таблицу, определяющую количество списания , с колонками :
• Столбец «Номенклатура»;
• Столбец «Количество» - общее количество, которое должно быть списано со всех партий для данной номенклатурной единицы;
Т.е. здесь мы имеем суммарное по всем документам расхода количество для списания - это серьезное упрощение
задачи. Вычислить движения по партиям нужно ведь в разрезе каждого документа расхода .
На практике при реализации метода ФИФО на входе имеем не таблицу с итогами количества по списанию ,
а таблицу с колонками
• Столбец «ДокументРасходнаяНакладная»;
• Столбец «Номенклатура»;
• Столбец «Количество» - количество списания по накладной
А это значит на выходе мы должны получить НЕ Вашу таблицу с колонками :
• Столбец «Номенклатура»;
• Столбец «Партия»;
• Столбец «КоличествоСписания» - количество списания по накладной
• Столбец «СуммаСписания» - количество списания по накладной
А другую таблицу
• Столбец «ДокументРасходнаяНакладная»;
• Столбец «Номенклатура»;
• Столбец «Партия»;
• Столбец «КоличествоСписания» - количество списания по накладной
• Столбец «СуммаСписания» - количество списания по накладной
Повторюсь - запросы в статье открыты для экспериментов и оптимизаций, все в Ваших руках! :)
Я из этого и исхожу.
Но Ваша постановка задачи есть частный случай и сильное упрощение постановки задачи в "ФИФО для любопытных".
Т.е. никакая НЕ альтернатива.
С другой стороны , если Вы представите текст запроса для случая , когда на входе
таблица
• Столбец «ДокументРасходнаяНакладная»;
• Столбец «Номенклатура»;
• Столбец «КоличествоСписания»;
нам будет легко сравнивать альтернативные запросы.
И будет о чем поговорить. Пока нечего сравнивать.
Но все, что нужно для модификации моего запроса я уже даже описал сам :)
1. При решении задачи полноценного ФИФО , т.е. получения движений для каждого документа расхода Вас ждут сюрпризы. И описанной Вами модификацией - лишь "добавляя только группировку по регистратору" - Вы ничего не добьетесь.
2. Остался без проверки главный подводный камень, на который налетают при использовании соединения по неравенству. При приличных объемах входных данных Ваш алгоритм - "рухнет".
См. Пример с таблицей 1 000 000 записей, которая соединяется сама с собой по неравенству.
Если бы было найдено решение , учитывающее 1 и 2 и которое принципиально отличалось бы от уже опубликованных на ИС - тогда бы мы поговорили всерьез.
В понедельник с утра опубликую их и здесь - хотелось бы привлечь к статьям больше внимания и конструктивной критики. Пока можно попытаться обсудить статьи здесь :)
График.
Сумму членов арифметической прогресии находил когда -то маленький Гаусс на уроке:
(А1+АN)*N/2
Если в начальной таблице 100 записей, то при соединении каждой записи со всеми предыдущими получаем :
А1=1,АN=100,N=100.
(1+100)*100/2 = 5050
Итак ,При соединении каждой записи со всеми предыдущими получаем получаем в итоговой таблице 5050 записей.
Промежуточная таблица возросла в 55 раз.
(83) Игнорировать такое возрастание невозможно. Любые алгоритмы , игнорирующие это обстоятельство нельзя рассматривать всерьез.
Запустите Ваш запрос - соединение по неравенству tab1 "сама с собой" засеките время. И всё поймете... назавтра.
Попробуете ?
Вообще-то я расчитывал на что-то вроде этого
В плане того, что мне останется только понажимать кнопочки и опубликовать результаты. А тут получается, что нужно и данные набить, и методику нагрузочного тестирования придумать?
имеет 1 000 000 записей).
..Дольше мне писать , чем Вам попробовать.
приведено прямое решение для задачи из - опять ошибаетесь!
Вы решаете другую задачу.
Вы считаете известной на ткущий момент ТаблицуДолгов с колонками: Контрагент, Накладная, СуммаДолга.
Чтобы ее получить, нужно один за другим последовательно провести документы по регистру взаиморасчетов по документам расчетов.
В решении этого не требуется.
То есть оно работает при отсутствии в конфигурациях регистра взаиморасчетов по документам расчетов.
Как, кстати и не требуется перепроведения при изменении документов задним числом.
Другая задача - другие методы, как можно сравнивать?
Вы привели очевидное решение своей задачи.
Очевидное потому, что по-другому запрос и не записать, поставив задачу получить запросом списываемые партии из регистра партий.
И если бы не заблуждения относительно того, что это решение чего-то проще и компактней, то статья вполне может здесь пригодиться.
Там для одного контрагента - одна строка: Сумма долга (текущая) и дата отсрочки.
Не знаю, насколько кому это решение известное.
Оно очевидное.
Потому что любой опытный программист напишет такое же.
Труднее написать по-другому.
Ну а очевидность для публикации здесь не препятствие.
Тем не менее, когда документов по одному контрагенту станет достаточно много,
приведенный запрос будет работать слишком долго - зависимость от числа документов квадратичная (показано в комментарии (18)).
А ведь срок давности документов в запросе не ограничен.
А вот запрос для списания партий вполне рабочий: ведь не списанных партий обычно немного (чаще даже одна).
(71) внимания, как я уже говорил, статья достойна.
Цель моих комментариев - исправить замеченные неточности относительно "альтернативности" подхода.
То есть в итоге мои выводы таковы:
1. для просроченных долгов запрос в приведенном виде не годится. Это не альтернатива.
2. для списания партий в самописных конфигурациях, если считать, что решается другая и даже более распространенная задача, вполне подходит.
Мог бы изложить свое понимание того, почему в типовых используется предварительная выгрузка остатков в таблицу, но не буду отвлекать от темы.
Простоту также считаю большим плюсом.
Вопросы оптимизации умышленно вынесены за рамки моей статьи в угоду простоте изложения материала и его понятности, о чем и сказано в последнем абзаце текста статьи.
Я полностью уверен, что при любых условиях хранения движений (остатков/оборотов), будь то регистр или справочник - этот запрос будет работать. Еще раз - доказывать кому-то что мой запрос лучше или хуже у меня цели нет. Считаете, что моя статья недостойна внимания - я буду работать с другими, которым она полезна.
Сегодня этот код успешно применен и протестирован на рабочей базе.
Оговорюсь, что вместо виртуальных таблиц Обороты использовала уже заранее сформированные временные таблицы в одном немаленьком запросе (таким образом СКЛю будет несложно выбрать оптимальный план запроса).
Так как запрос изначально был немаленьким, то вставлять в него еще 7 подзапросов считаю перегрузом. А так всего десяток строк - и наглядно и просто. Keep it simple!
P.S. У меня в соединяемых временных таблицах никогда не будет 1 000 000 записей, поэтому не боюсь, что запрос рухнет на огромном массиве данных.
Спасибо автору!
Для получения уведомлений о новых публикациях автора подключите телеграм бот: Инфостарт бот
№ 88999
Создание 25.08.11 10:02
Обновление 25.08.11 10:02
Просмотры 18045
Загрузки 81
Рейтинг
11
Комментарии 107
Код открыт Не указано
Рубрики Математика и алгоритмы
Кому Программист
Тип файла Архив с данными
Платформа 1С v8.3
Конфигурация Универсальные
Операционная система Не имеет значения
Страна Россия
Отрасль Не имеет значения
Налоги Не имеет значения
Вид учета Не имеет значения
Доступ к файлу Абонемент ($m)