Иногда возникает необходимость убрать дубли из результата запроса. Причём делать это хочется при помощи запроса - так быстрее (критично на больших объёмах данных).
Дубли имеются в виду частичные - т.е. это такие записи, где значения, скажем, по 3 полям совпадают (у двух или более записей), а по остальным полям - нет. Я назвал такие совпадающие поля условно "поля сверки дублей". В общем-то, их может быть сколько угодно - не обязательно именно 3.
Я написал процедуру, убирающую дубли запросом. Она принимает на входе временную таблицу запроса, из которой надо удалить дубли, список полей сверки дублей. Результат помещается в новую временную таблицу того же запроса. Опционально она может показать найденные дубли (и даже вывести их на экран в печатной таблице). Вот код этой процедуры:
Она проверена в 1С:Бухгалтерия 2.5, в ЗУП, возможно, потребуется исправить "ОбщегоНазначения" на "ОбщегоНазначенияЗК".
// Процедура удаления дублей из временной таблице запроса (только для обычных форм).
// Параметры:
// "Запрос" - запрос, содержащий временную таблицу, из которой надо удалить дубли.
// "ИмяВТВход" - имя временной таблицы запроса, из которой надо удалить дубли. Удаляется после проработки процедуры (как более не нужная).
// "ПоляСверкиДубля" - строка вида "Поле1,Поле2,Поле3", содержащая имена тех полей из "ИмяВТВход", по которым мы будем искать дубли.
// "ИмяВТВыход" - имя временной таблицы запроса, в которую помещается результат работы процедуры - т.е. "ИмяВТВход", откуда убрали все дубли.
// "ОставитьВТДубли" - оставить или нет временную таблицу с обнаруженными дублями после проработки процедуры.
// "ПоказыватьДубли" - выводить или нет печатную таблицу с обнаруженными дублями.
// "ЗаголовокТаблицыДублей" - заголовок для печатной таблицы "ПоказыватьДубли".
Процедура УдалитьДублиВТ(Запрос, ИмяВТВход, ПоляСверкиДубля, ИмяВТВыход, ОставитьВТДубли = Ложь, ПоказыватьДубли = Ложь, ЗаголовокТаблицыДублей = "Таблица найденных дублей")
//Прототип: http://www.forum.mista.ru/topic.php?id=550537
МПСД = ОбщегоНазначения.РазложитьСтрокуВМассивПодстрок(ПоляСверкиДубля,",");
ПоляЗапроса = "";
ПоляСоединения = "";
Для каждого Элем Из МПСД Цикл
ПоляЗапроса = ПоляЗапроса + ИмяВТВход + "." + Элем + ", ";
ПоляСоединения = ПоляСоединения + "(" + ИмяВТВход + "." + Элем + " = " + "ВТ_ДУБЛИ1." + Элем + ") И ";
КонецЦикла;
ПоляЗапроса = Лев(ПоляЗапроса, СтрДлина(ПоляЗапроса)-2);
ПоляСоединения = Лев(ПоляСоединения, СтрДлина(ПоляСоединения)-3);
Запрос.Текст = "ВЫБРАТЬ СУММА(1) КАК КоличествоДублей, " + ПоляЗапроса + " ПОМЕСТИТЬ ВТ_ДУБЛИ ИЗ " + ИмяВТВход + " КАК " + ИмяВТВход +
" СГРУППИРОВАТЬ ПО " + ПоляЗапроса + " ИМЕЮЩИЕ СУММА(1) > 1 ; ВЫБРАТЬ ВТ_ДУБЛИ.*, 1 КАК СтрокаДубля ПОМЕСТИТЬ ВТ_ДУБЛИ1 ИЗ ВТ_ДУБЛИ КАК ВТ_ДУБЛИ; ";
Запрос.Текст = Запрос.Текст + "ВЫБРАТЬ " + ИмяВТВход + ".*, ЕСТЬNULL(ВТ_ДУБЛИ1.СтрокаДубля,0) КАК СтрокаДубля ПОМЕСТИТЬ ВТ_ВЫХОД ИЗ " + ИмяВТВход + " КАК " + ИмяВТВход +
" ЛЕВОЕ СОЕДИНЕНИЕ ВТ_ДУБЛИ1 КАК ВТ_ДУБЛИ1 ПО " + ПоляСоединения + " ГДЕ ВТ_ДУБЛИ1.СтрокаДубля ЕСТЬ NULL";
Запрос.Выполнить();
Если ПоказыватьДубли Тогда
ДанныеТаблицы = Новый Запрос;
ДанныеТаблицы.МенеджерВременныхТаблиц = Запрос.МенеджерВременныхТаблиц;
ДанныеТаблицы.Текст =
"Выбрать 0 КАК НомерСтроки, *
| Из ВТ_ДУБЛИ
|";
ТЗДубли = ДанныеТаблицы.Выполнить().Выгрузить();
Для Сч = 0 По ТЗДубли.Количество()-1 Цикл
ТЗДубли[Сч].НомерСтроки = Сч + 1;
КонецЦикла;
Построитель=Новый ПостроительОтчета();
Построитель.ИсточникДанных=Новый ОписаниеИсточникаДанных(ТЗДубли);
Построитель.ТекстЗаголовка = ЗаголовокТаблицыДублей;
Построитель.Вывести();
КонецЕсли;
ДанныеТаблицы = Новый Запрос;
ДанныеТаблицы.МенеджерВременныхТаблиц = Запрос.МенеджерВременныхТаблиц;
ДанныеТаблицы.Текст =
"Выбрать ПЕРВЫЕ 1 *
| Из ВТ_ВЫХОД
|";
ТЗВыход = ДанныеТаблицы.Выполнить().Выгрузить();
ПоляЗапроса = "";
Для каждого Колонка Из ТЗВыход.Колонки Цикл
Если Колонка.Имя <> "СтрокаДубля" Тогда
ПоляЗапроса = ПоляЗапроса + Колонка.Имя + ", ";
КонецЕсли;
КонецЦикла;
ПоляЗапроса = Лев(ПоляЗапроса, СтрДлина(ПоляЗапроса)-2);
Запрос.Текст = "ВЫБРАТЬ " + ПоляЗапроса + " ПОМЕСТИТЬ " + ИмяВТВыход + " ИЗ ВТ_ВЫХОД; УНИЧТОЖИТЬ " + ИмяВТВход +
"; УНИЧТОЖИТЬ ВТ_ВЫХОД; УНИЧТОЖИТЬ ВТ_ДУБЛИ1";
Если НЕ ОставитьВТДубли Тогда
Запрос.Текст = Запрос.Текст + "; УНИЧТОЖИТЬ ВТ_ДУБЛИ";
КонецЕсли;
Запрос.Выполнить();
КонецПроцедуры