Подскажите, есть два вида документов, например Заказ Покупателя и Производство, как программно найти документы Производство и Заказ Покупателя где одинаковая табличная часть т.е. все элементы номенклатуры одинаковы
(1) ceramica, выбирать содержимое табличных частей документов, и сличать построчно ТЧ.
Кстати, порядок ведь может отличатся ? а еще количество, сумма..
Я бы для такой задачи целую процедуру написал. Если она конечно разовая.
А если это какой то постоянный процесс, думаю надо что-то менять, делать какую то взаимосвязь, иначе это все выглядит как через одно место
Ну да, как написали в (2), если уже придётся извращаться.
Можно просто выбирать документ по значениям ТЧ которого будет выполняться поиск, потом индексировать по наименованию номенклатуры сравниваемые табличные части и только тогда их сравнивать построчно.
Советую сделать в документе "ЗаказПокупателя" ссылку на документ "Производство", если так планируется продолжать, если у вас логика такова, что на один документ заказа приходится один документ производства.
(1) ceramica, в статье "Расчет хэш-функции в запросе" есть Пример 2. Там приведен запрос, позволяющий сгруппировать документы, содержащие один и тот же набор номенклатуры.
Правда, итоговая задача там другая - найти наиболее популярные наборы товаров, но сам принцип подойдет и для вашего случая.
Если же один из документов фиксирован. Например, дан заказ, а требуется найти все документы "Производство" с таким же составом номенклатуры, то множество вариантов решения можно посмотреть в статье INFOSTART EVENT 2012: разбор решений конкурса разработчиков
(5) ildarovich, круто-круто.
Я просто тоже хотел предложить вариант с хешами, но ничего толкового в голову не пришло, на эту тему. А тут прямо на блюдечке! Спасибо за статью)
Всё так или иначе сводится к сравнению табличных частей. Вопрос только в том, насколько принципиально это дело ускорять, потому что при самом очевидном алгоритме сравнения сложность задачи растёт как M*N*m*n, где большие буквы - число документов, а маленькие - характерный размер их табличных частей.
(8) ceramica, самый разумный вариант это не сравнивать табличные части построчно вообще. Вариант ildarovich я бы рекомендовал только тогда, когда необходимо выжать из скорости всё, что можно. Не потому, что этот способ плох (по мне так он и вовсе наилучший из возможных), а потому, что эти запросы сложны для понимания.
Для себя я бы сделал так: выгрузил бы документы в два массива. Из меньшего массива сделал бы соответствие, где ключ - хэш из кодов номенклатуры табличной части, а значение - массив ссылок на документ. Дальше обходим больший массив с первыми документами и получаем из соответствия ссылки на вторые документы.
Только надо помнить, что равенство хэшей не гарантирует равенства ТЧ, так что финальное сравнение всё равно надо будет провести.
Можно номенклатуры обоих ТЧ выгрузить в ТЗ, добавить колонку с заполненным значением = 1 и потом
в этой ТЗ свернуть эту колонку по номенклатуре,
пробегая свернутую на предмет поиска числа 2, если встретится хотя бы одна 1, то ТЧ не равны.
(10) makfromkz, Да твой вариант тоже хороший, ведь необходимо только получить булево значение при сверке ТЧ, но я поступил все таки через массив, вот функция если кому надо:
Функция ПроверитьИдентичностьМассивов(ПервыйМассив, ВторойМассив)
НаибольшийИндекс = ПервыйМассив.ВГраница();
Если НаибольшийИндекс > ВторойМассив.ВГраница () Тогда
Возврат Ложь;
КонецЕсли;
Для Счетчик = 0 По НаибольшийИндекс Цикл
Если ПервыйМассив[Счетчик] <> ВторойМассив[Счетчик] Тогда
Возврат Ложь;
КонецЕсли;
КонецЦикла;
Если эта задача будет выполняться регулярно, то я посоветовал-бы создать регистр сведений (если надо сравнивать только проведённые документы - то подчинённый регистратору, иначе - с независимым режимом записи). Создать соответствующую подписку на событие (либо при проведении, либо при записи). И в этот регистр сведений записывать хэш сумму по необходимым данным из ТЧ. А потом выбрать записи регистра сведений с одинаковыми хэш-суммами - задача не сложная.
Если это одноразовая задача (или неохота возиться с регистром сведений):
1. создаём соответствие в котором ключ - хэш-сумма по ТЧ и при заполнении этого соответствия сначала анализируем наличие записи по ключу, если такой ключ есть - то имеем документы с одинаковой ТЧ (выводим их куда надо по условиям задачи), если ключа нет - добавляем в соответствие и продолжаем.
2. создаем ТЗ с 2-мя колонками "Документ" и "ХэшСумма", заполняем эту ТЗ, затем загоняем её как параметр запроса (Выбрать ТЗ.Документ, ТЗ.ХэшСумма ПОМЕСТИТЬ ВТ ИЗ &ТЗ КАК ТЗ), и дальше формируем группировку по необходимости.
Замечание: если надо пропустить какие-то документы с одинаковой ТЧ (например, не нужны заказы с одинаковой ТЧ), то 2-м способом это сделать намного проще.
При создании хэш-суммы надо иметь в виду, что порядок строк и регистр букв для хэш суммы играет роль, поэтому ТЧ надо скопировать в ТЗ, упорядочить ТЗ по какому-то признаку и потом уже по ТЗ вычислять хэш сумму (на мой взгляд, лучше всего взять строковое представление уникального идентификатора элемента справочника и сортировать по нему, а перед вычислением хэш-суммы в ТЗ оставить только колонки с GUID элемента справочника и то, что ещё надо, например - количество).
ВЫБРАТЬ
ОбщиеСтроки.Заказ,
ОбщиеСтроки.Реализация,
ОбщиеСтроки.ОбщихСтрок
ИЗ
(ВЫБРАТЬ
Заказы.Заказ КАК Заказ,
Реализации.Реализация КАК Реализация,
КОЛИЧЕСТВО(*) КАК ОбщихСтрок
ИЗ
(ВЫБРАТЬ РАЗЛИЧНЫЕ
РеализацияТоваровУслугТовары.Ссылка КАК Реализация,
РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары) КАК Реализации
ВНУТРЕННЕЕ СОЕДИНЕНИЕ (ВЫБРАТЬ РАЗЛИЧНЫЕ
ЗаказПокупателяТовары.Ссылка КАК Заказ,
ЗаказПокупателяТовары.Номенклатура КАК Номенклатура
ИЗ
Документ.ЗаказПокупателя.Товары КАК ЗаказПокупателяТовары) КАК Заказы
ПО Реализации.Номенклатура = Заказы.Номенклатура
СГРУППИРОВАТЬ ПО
Заказы.Заказ,
Реализации.Реализация) КАК ОбщиеСтроки
ГДЕ
(ОбщиеСтроки.Заказ, ОбщиеСтроки.ОбщихСтрок) В
(ВЫБРАТЬ
ЗаказыСтрок.Заказ,
ЗаказыСтрок.КолвоСтрок
ИЗ
(ВЫБРАТЬ
ЗаказПокупателяТовары.Ссылка КАК Заказ,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ ЗаказПокупателяТовары.Номенклатура) КАК КолвоСтрок
ИЗ
Документ.ЗаказПокупателя.Товары КАК ЗаказПокупателяТовары
СГРУППИРОВАТЬ ПО
ЗаказПокупателяТовары.Ссылка) КАК ЗаказыСтрок)
И (ОбщиеСтроки.Реализация, ОбщиеСтроки.ОбщихСтрок) В
(ВЫБРАТЬ
РеализацииСтрок.Реализация,
РеализацииСтрок.КолвоСтрок
ИЗ
(ВЫБРАТЬ
РеализацияТоваровУслугТовары.Ссылка КАК Реализация,
КОЛИЧЕСТВО(РАЗЛИЧНЫЕ РеализацияТоваровУслугТовары.Номенклатура) КАК КолвоСтрок
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
СГРУППИРОВАТЬ ПО
РеализацияТоваровУслугТовары.Ссылка) КАК РеализацииСтрок)
(18) mpudy, в комментарии (7) приведена похожая на правду оценка быстродействия этого очевидного подхода. Она не дает надеяться на пригодность данного метода для практических задач. Там квадратичная сложность по числу документов, а есть еще число строк! В задачах поиска подстроки в строке подобные алгоритмы называются "брутфорсом". При том, что есть методы линейной сложности.
(22) ildarovich, на практике, имхо, в большинстве случаев есть дополнительные критерии отбора, например разность дат не более n дней и т.д. и т.п. Для одноразового использования сойдет.
При количестве строк в заказах покупателей 6 753 591 и 8 693 484 в реализациях работает около 10 минут, временная база выростает до 40ГБ)))
На более скромных заказах поставщику 72 896 и поступлениях 115 510 запрос выполняется секунды.
УТ если что.
(25) mpudy, вот вариант решения на основе хэш-функции в соответствии с комментарием (5)
ВЫБРАТЬ
Товары.Ссылка,
Товары.Номенклатура.Код КАК Аргумент
ПОМЕСТИТЬ ИсходныеДанные
ИЗ
Документ.ЗаказПокупателя.Товары КАК Товары
ГДЕ
Товары.Ссылка.Организация = &Организация
И Товары.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
Товары.Ссылка,
Товары.Номенклатура.Код
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
Товары.Ссылка.Организация = &Организация
И Товары.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
ИсходныеДанные.Ссылка,
СУММА(923521 * ВЫБОР ПОДСТРОКА(ИсходныеДанные.Аргумент, 5, 1)
КОГДА "0"
ТОГДА 1
КОГДА "1"
ТОГДА 2
КОГДА "2"
ТОГДА 3
КОГДА "3"
ТОГДА 4
КОГДА "4"
ТОГДА 5
КОГДА "5"
ТОГДА 6
КОГДА "6"
ТОГДА 7
КОГДА "7"
ТОГДА 8
КОГДА "8"
ТОГДА 9
КОГДА "9"
ТОГДА 10
ИНАЧЕ 0
КОНЕЦ + 28629151 * ВЫБОР ПОДСТРОКА(ИсходныеДанные.Аргумент, 6, 1)
КОГДА "0"
ТОГДА 1
КОГДА "1"
ТОГДА 2
КОГДА "2"
ТОГДА 3
КОГДА "3"
ТОГДА 4
КОГДА "4"
ТОГДА 5
КОГДА "5"
ТОГДА 6
КОГДА "6"
ТОГДА 7
КОГДА "7"
ТОГДА 8
КОГДА "8"
ТОГДА 9
КОГДА "9"
ТОГДА 10
ИНАЧЕ 0
КОНЕЦ + 887503681 * ВЫБОР ПОДСТРОКА(ИсходныеДанные.Аргумент, 7, 1)
КОГДА "0"
ТОГДА 1
КОГДА "1"
ТОГДА 2
КОГДА "2"
ТОГДА 3
КОГДА "3"
ТОГДА 4
КОГДА "4"
ТОГДА 5
КОГДА "5"
ТОГДА 6
КОГДА "6"
ТОГДА 7
КОГДА "7"
ТОГДА 8
КОГДА "8"
ТОГДА 9
КОГДА "9"
ТОГДА 10
ИНАЧЕ 0
КОНЕЦ + 1742810335 * ВЫБОР ПОДСТРОКА(ИсходныеДанные.Аргумент, 8, 1)
КОГДА "0"
ТОГДА 1
КОГДА "1"
ТОГДА 2
КОГДА "2"
ТОГДА 3
КОГДА "3"
ТОГДА 4
КОГДА "4"
ТОГДА 5
КОГДА "5"
ТОГДА 6
КОГДА "6"
ТОГДА 7
КОГДА "7"
ТОГДА 8
КОГДА "8"
ТОГДА 9
КОГДА "9"
ТОГДА 10
ИНАЧЕ 0
КОНЕЦ + 2487512833 * ВЫБОР ПОДСТРОКА(ИсходныеДанные.Аргумент, 9, 1)
КОГДА "0"
ТОГДА 1
КОГДА "1"
ТОГДА 2
КОГДА "2"
ТОГДА 3
КОГДА "3"
ТОГДА 4
КОГДА "4"
ТОГДА 5
КОГДА "5"
ТОГДА 6
КОГДА "6"
ТОГДА 7
КОГДА "7"
ТОГДА 8
КОГДА "8"
ТОГДА 9
КОГДА "9"
ТОГДА 10
ИНАЧЕ 0
КОНЕЦ + 4098453791 * ВЫБОР ПОДСТРОКА(ИсходныеДанные.Аргумент, 10, 1)
КОГДА "0"
ТОГДА 1
КОГДА "1"
ТОГДА 2
КОГДА "2"
ТОГДА 3
КОГДА "3"
ТОГДА 4
КОГДА "4"
ТОГДА 5
КОГДА "5"
ТОГДА 6
КОГДА "6"
ТОГДА 7
КОГДА "7"
ТОГДА 8
КОГДА "8"
ТОГДА 9
КОГДА "9"
ТОГДА 10
ИНАЧЕ 0
КОНЕЦ + 2498015937 * ВЫБОР ПОДСТРОКА(ИсходныеДанные.Аргумент, 11, 1)
КОГДА "0"
ТОГДА 1
КОГДА "1"
ТОГДА 2
КОГДА "2"
ТОГДА 3
КОГДА "3"
ТОГДА 4
КОГДА "4"
ТОГДА 5
КОГДА "5"
ТОГДА 6
КОГДА "6"
ТОГДА 7
КОГДА "7"
ТОГДА 8
КОГДА "8"
ТОГДА 9
КОГДА "9"
ТОГДА 10
ИНАЧЕ 0
КОНЕЦ) КАК Хэш
ПОМЕСТИТЬ СырыеДанные
ИЗ
ИсходныеДанные КАК ИсходныеДанные
СГРУППИРОВАТЬ ПО
ИсходныеДанные.Ссылка
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
СырыеДанные.Ссылка
ИЗ
СырыеДанные КАК СырыеДанные
ИТОГИ ПО
СырыеДанные.Хэш
Показать
Ищет и выводит в виде дерева все документы ЗаказПокупателя и РеализацияТоваровИУслуг с одинаковой табличной частью. Работает супербыстро (секунды на любой базе). Проверено на УТ10.3. По-хорошему еще по дате можно упорядочить.
(27) ildarovich,
круто, так то )))
Но у тебя же по коду номенклатуры анализирует ?
А если в документе отличается количество, или цена, или еще какие то реквизиты. Оне ведь этого не найдет, хоть формально табличные части будут вроде-бы одинаковые, и все равно надо писать дополнительную проверку.
(28) Boneman, ну, во первых, в задании говорилось только о номенклатуре. Автор решения (18) также задание понял. Если нужно анализировать еще какие-либо числовые поля, то их нетрудно замешать в хэш. Количество - так вообще элементарно. Так как хэш числовой, достаточно хэш номенклатуры на количество умножить перед сверткой по ссылке. То же с ценой. Если нужны ссылочные типы, имеющие уникальный реквизит код (символьный или числовой), то также нет проблем.
Выигрыш по времени очень большой. Так что стоит за него побороться, настраивая способ расчета хэшей на особенности задачи. Других достаточно быстрых для практики вариантов я не вижу.
(32) vasyak319, т.е. как? Если хэш формируется из одних и тех же данных, выстроенных в одинаковой последовательности, то как они могут быть не равны? Точнее как они могут быть равны, но данные не совпадать?
(33) lSanderSl, как бы вы ни формировали хэш, в конечном итоге вы делаете из M байт N байт. Расскажите, как можно сделать 8^M уникальных хешей, каждый из которых длиной N байт, если N<M (в идеале - N<<M).
(38) vasyak319, ну если мы берём в расчёт два показателя - вероятность возникновения коллизии и производительность, то значимость первого безусловно выше (иначе всё предприятие лишается смысла).
Раз уж тема заинтересовала стольких форумчан, может посчитаем вероятность возникновения коллизии варианта от ildarovich и того что предложит mpudy?
(37) mpudy, результаты будут очень интересны для меня. Если не трудно, не могли бы на тех же данных запрос из (27) попробовать. Понятно, что когда будет время.
Несколько вопросов по задаче:
1) что за конфигурация? Если не типовая, то - точные названия (как в конфигураторе) документов "ЗаказПокупателя" и "Производство", а также названия сравниваемых табличных частей. Также название реквизита ТЧ, хранящего номенклатуру.
2) код номенклатуры - символьный? какая длина? из каких символов может состоять (есть не цифры?).
3) нужно ли учитывать количество номенклатуры в строках сравниваемых ТЧ?
(20) mpudy, у меня есть готовый запрос, решающий данную задачу, приведенный в статье "Расчет хэш-функции в запросе". Чтобы адаптировать его к данному случаю, мне нужно это уточнение.
(23) PetroP, это должно быть исключением. Иначе теряется сам смысл этого реквизита. Для практических целей можно этот факт просто проверить в базе клиента. При необходимости - перенумеровать. У своих клиентов такого давно не видел. Но вообще, конечно, это уточняющий вопрос к ТС:
4) есть ли две разных номенклатуры с одинаковым кодом у него в базе.
С начале сделать два вложенных запроса. Во вложенных запросах сгруппировать документы по ссылке с суммой по количеству и количеству строк в документе. Сделать внутреннее соединение по этим показателям. Так отбросим большую часть документов. Вторым пакетом запроса вложенный запрос, где сделать левое соединение к одному документу другого по тч. Строки опять сгруппировать по количеству. В основном запросе сделать левое соединение любого документа к этому вложенному по условию равенства количества из первого запроса количеству из второго.
- необходимо однократное сравнение или сравнение будет выполняться по определенному расписанию?
- может ли одна номенклатура быть введена в нескольких строках табличной части?
При однократном сравнении логику можно реализовать в одном запросе. Например сначала вычислить для документов количество строк в ТЧ, после сравнивать составы табличных частей документов, количество строк в которых одинаковое.
При сравнении по расписанию необходимы требования к расписанию, среднее количество новых документов между итерациями поиска, требования к нагрузке и производительности.
Так как вероятность возникновения коллизий не равна нулю, то после нахождения якобы совпадающих документов, их можно и нужно дополнительно проверить. Но с учетом того, что строки могут быть в другом порядке.
сравнение в запросе:
1) определяем поля которые должны быть идентичны для обоих табличных частей. {Поле1, Поле2}
[необходимо установить порядок полей, согласно их "весу" для индексирования] 2) во временную таблицу "вт_тч1" помещаем выборку с этими полями по первому документу. {Ссылка1, Поле1, Поле2}
[указываем индексы] 3) во временную таблицу "вт_тч2" помещаем выборку с этими полями по второму документу. {Ссылка2, Поле1, Поле2}
[указываем индексы] 4) финальная выборка состоит из соединения двух подзапросов соответствующих врем таблиц:
ВЫБРАТЬ
Ссылка1, Ссылка2,
*
ИЗ
(?) тч1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ (?) тч2 ПО тч1.{Поле1, Поле2, КоличествоСтрокТЧ} = тч2.{Поле1, Поле2, КоличествоСтрокТЧ}
в подзапросах под знаком "?" содержится запрос в соответствующие временные таблицы вида:
ВЫБРАТЬ
Ссылка1,
Поле1, Поле2,
КоличествоСтрокТЧ
ИЗ
вт_тч1
ВНУТРЕННЕЕ СОЕДИНЕНИЕ (
ВЫБРАТЬ
Ссылка1,
КОЛИЧЕСТВО(Ссылка1) КоличествоСтрокТЧ
из вт_тч1
СГРУППИРОВАТЬ ПО Ссылка1
) дляКоличСтрокТЧ ПО вт_тч1.Ссылка1 = дляКоличСтрокТЧ.Ссылка1
Таким образом получаем сравнение по набору полей и сравнение по количеству строк в табличной части одного документа.
Достаточная ли такая "одинаковость"?
У меня выполняется долго - за минуты. Но тут всё индивидуально и есть место оптимизации. Сколько памяти требуется не знаю, мне хватило
Таким образом получаем сравнение по набору полей и сравнение по количеству строк в табличной части одного документа.
А вот не факт. Пример - ключевые колонки Номенклатура и Количество, а в одном из документов товар с разными Ценами. Количество строк разное, а, по сути, табличные части могут быть одинаковыми
(48) Altair777, (51) vasyak319,
составленный запрос - рабочий.
Не понимаю ваши претензии, задача тривиальна - типичное сравнение двух наборов данных.
Что может быть проще их соединения по полному совпадению в каждом из полей?
Нюанс был только в сравнении количества строк, которое невозможно получить сразу.
Может быть вам непонятна вольность в синтаксисе соединения:
тч1.{Поле1, Поле2, КоличествоСтрокТЧ} = тч2.{Поле1, Поле2, КоличествоСтрокТЧ}
извините, лень писать было, обозначил схематично:
тч1.Поле1 = тч2.Поле1
И
тч1.Поле1 = тч2.Поле2
И
тч1.КоличествоСтрокТЧ = тч2.КоличествоСтрокТЧ
Пример:
ТЧДокумент1 ТЧДокумент2
Ссылка Данные Ссылка Данные
А 1 Б 1
А 2 Б 2
А 3 В 1
С 0 В 2
С 3 В 3
Совпадают наборы А и В, и построчно, и по количеству.
Если Данные представляют некие измерения и агрегатные поля, то достаточно в "вт_тч1" и "вт_тч2" свернуть по измерениям.
Получим
ТЧДокумент1 ТЧДокумент2
Ссылка Данные Ссылка Данные
А 6 Б 3
С 3 В 6
(56) vasyak319, (55) ildarovich,
понял о чём вы, спасибо за терпение! :)
действительно, нет контрольного количества строк после объединения.
тогда поможет такой фокус:
в финальной выборке нужно сгруппировать и с условием
*от полей самих данных на этом этапе придется отказаться ВЫБРАТЬ
Ссылка1, Ссылка2, КоличествоСтрокТЧ
ИЗ
...
СГРУППИРОВАТЬ ПО
Ссылка1, Ссылка2, КоличествоСтрокТЧ
ИМЕЮЩИЕ
КОЛИЧЕСТВО(Ссылка1) = КоличествоСтрокТЧ
прикрепляю файлик для консоли запросов, с вариантами.