Дано
У нас есть таблица с ключевой колонкой "ИД", значения которой определяют наборы связанных строк, и колонкой "Значение", в которой хранятся реквизиты строк набора. Дубли строк в таблице отсутствуют.
Пример таблицы входных данных:
ИД | Значение |
П1 | А |
П1 | Б |
П1 | В |
П2 | А |
П2 | В |
П2 | Б |
П3 | Б |
П3 | В |
П4 | А |
П4 | В |
П4 | Б |
П5 | А |
П5 | В |
П5 | Г |
Найти
Нужно получить таблицу, состоящую из уникальных наборов строк, т.е. в ней должны быть удалены все дубли наборов строк.
В нашем примере наборы строк с ключами П1, П2, П4 являются дублями. Поэтому из них должен остаться лишь один набор, т.е. 3 строки с единым ключом П1 или П2 или П4.
Решение 1 (общее)
Сначала рассмотрим наиболее универсальное решение
1. Считаем количество строк в каждом наборе
2. Находим все пары наборов, являющихся дублями по составу
3. Назначаем каждому набору минимальный ключ среди всех его дублей
4. Выводим только наборы, имеющие минимальный ключ в своей группе дублей
Запишем это в виде запроса 1С, принимающего на вход созданную выше временную таблицу ВходныеДанные:
//{Запрос: 1, -4 ////////////////////////////////////////
ВЫБРАТЬ
ВходныеДанные.ИД КАК ИД,
КОЛИЧЕСТВО(*) КАК Число
ПОМЕСТИТЬ ЧислоЧленовВНаборе
ИЗ
ВходныеДанные КАК ВходныеДанные
СГРУППИРОВАТЬ ПО
ВходныеДанные.ИД
;
//{Запрос: 2, -3 ////////////////////////////////////////
ВЫБРАТЬ
ВходЛевое.ИД КАК ИДЛевое,
ВходПравое.ИД КАК ИДПравое
ПОМЕСТИТЬ ПарыДублейНаборов
ИЗ
ВходныеДанные КАК ВходЛевое
ЛЕВОЕ СОЕДИНЕНИЕ ВходныеДанные КАК ВходПравое
ЛЕВОЕ СОЕДИНЕНИЕ ЧислоЧленовВНаборе КАК ЧислоЧленовПравое
ПО ВходПравое.ИД = ЧислоЧленовПравое.ИД
ПО ИСТИНА
И ВходЛевое.Значение = ВходПравое.Значение
И ВходЛевое.ИД >= ВходПравое.ИД
ЛЕВОЕ СОЕДИНЕНИЕ ЧислоЧленовВНаборе КАК ЧислоЧленовЛевое
ПО ВходЛевое.ИД = ЧислоЧленовЛевое.ИД
ГДЕ ЧислоЧленовПравое.Число = ЧислоЧленовЛевое.Число
СГРУППИРОВАТЬ ПО
ВходЛевое.ИД,
ЧислоЧленовЛевое.Число,
ВходПравое.ИД
ИМЕЮЩИЕ КОЛИЧЕСТВО(ВходПравое.Значение) = ЧислоЧленовЛевое.Число
;
//{Запрос: 3, -2 ////////////////////////////////////////
ВЫБРАТЬ
ВходныеДанные.ИД КАК ИД,
МИНИМУМ(ПарыДублейНаборов.ИДПравое) КАК ИДГруппы
ПОМЕСТИТЬ НаборыСГруппами
ИЗ
ВходныеДанные КАК ВходныеДанные
ЛЕВОЕ СОЕДИНЕНИЕ ПарыДублейНаборов КАК ПарыДублейНаборов
ПО ВходныеДанные.ИД = ПарыДублейНаборов.ИДЛевое
СГРУППИРОВАТЬ ПО
ВходныеДанные.ИД
;
//{Запрос: 4, -1 ////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВходныеДанные.*
ИЗ
НаборыСГруппами КАК НаборыСГруппами
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВходныеДанные КАК ВходныеДанные
ПО ВходныеДанные.ИД = НаборыСГруппами.ИДГруппы
Решение 2 (свертка)
Теперь рассмотрим решение для частного случая, когда у нас различных значений каждого реквизита во всей таблице не более 100 (выше будет арифметическое переполнение).
1. Найдем и пронумеруем различные значения реквизита функцией АВТОНОМЕРЗАПИСИ(), добавленной в платформе 8.3.13
2. Посчитаем свертку для каждого набора формулой Сумма(2^<Номер значения реквизита>). Такая свертка имеет нулевую вероятность коллизий, поэтому решение будет строгим.
3. Найдем каждому результату свертки (группе дублей) минимальный ключ среди всех его наборов
4. Выводим только наборы, имеющие минимальный ключ в своей группе дублей
Это решение намного быстрее общего решения, если количество строк в таблице велико.
Запишем это в виде запроса 1С, принимающего на вход созданную выше временную таблицу ВходныеДанные:
//{Запрос: 1, -5 ////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВходныеДанные.Значение КАК Значение,
АВТОНОМЕРЗАПИСИ() КАК НомерПП
ПОМЕСТИТЬ УникальныеЗначения
ИЗ
ВходныеДанные КАК ВходныеДанные
;
//{Запрос: 2, -4 ////////////////////////////////////////
ВЫБРАТЬ
МИНИМУМ(УникальныеЗначения.НомерПП) КАК Номер
ПОМЕСТИТЬ МинимальныйНомер
ИЗ
УникальныеЗначения КАК УникальныеЗначения
;
//{Запрос: 3, -3 ////////////////////////////////////////
ВЫБРАТЬ
ВходныеДанные.ИД КАК ИД,
СУММА(POW(2, УникальныеЗначения.НомерПП - МинимальныйНомер.Номер)) КАК СверткаГруппы
ПОМЕСТИТЬ Свертки
ИЗ
ВходныеДанные КАК ВходныеДанные
ВНУТРЕННЕЕ СОЕДИНЕНИЕ УникальныеЗначения КАК УникальныеЗначения
ПО ВходныеДанные.Значение = УникальныеЗначения.Значение,
МинимальныйНомер КАК МинимальныйНомер
СГРУППИРОВАТЬ ПО
ВходныеДанные.ИД
;
//{Запрос: 4, -2 ////////////////////////////////////////
ВЫБРАТЬ
МИНИМУМ(Свертки.ИД) КАК ИД
ПОМЕСТИТЬ ГруппыДублейНаборов
ИЗ
Свертки КАК Свертки
СГРУППИРОВАТЬ ПО
Свертки.СверткаГруппы
;
//{Запрос: 5, -1 ////////////////////////////////////////
ВЫБРАТЬ
ВходныеДанные.ИД КАК ИД,
ВходныеДанные.Значение КАК Значение
ИЗ
ВходныеДанные КАК ВходныеДанные
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ГруппыДублейНаборов КАК ИскомыеГруппы
ПО ВходныеДанные.ИД = ИскомыеГруппы.ИД
Решение 3 (хеширование)
Теперь рассмотрим очень похожее на №2 решение без ограничения на число различных значений реквизитов, но нестрогое с очень низкой вероятностью получения некорректного результата.
1. Найдем и пронумеруем различные значения реквизита функцией АВТОНОМЕРЗАПИСИ(), добавленной в платформе 8.3.13
2. Посчитаем хеш-функцию для каждого набора формулой Сумма(COS(<Номер значения реквизита>)). Такая формула имеет максимальную вероятность коллизии 10^-9, что обосновано ildarovich в комментарии. Функция COS() добавлена в платформе 8.3.20
3. Найдем каждому хешу (группе дублей) минимальный ключ среди всех его наборов
4. Выводим только наборы, имеющие минимальный ключ в своей группе дублей
Это решение намного быстрее общего решения, если количество строк в таблице велико.
Запишем это в виде запроса 1С, принимающего на вход созданную выше временную таблицу ВходныеДанные:
//{Запрос: 1, -4 ////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
ВходныеДанные.Значение КАК Значение,
АВТОНОМЕРЗАПИСИ() КАК НомерПП
ПОМЕСТИТЬ УникальныеЗначения
ИЗ
ВходныеДанные КАК ВходныеДанные
;
//{Запрос: 2, -3 ////////////////////////////////////////
ВЫБРАТЬ
ВходныеДанные.ИД КАК ИД,
СУММА(COS(УникальныеЗначения.НомерПП)) КАК Хеш
ПОМЕСТИТЬ Хеши
ИЗ
ВходныеДанные КАК ВходныеДанные
ВНУТРЕННЕЕ СОЕДИНЕНИЕ УникальныеЗначения КАК УникальныеЗначения
ПО ВходныеДанные.Значение = УникальныеЗначения.Значение
СГРУППИРОВАТЬ ПО
ВходныеДанные.ИД
;
//{Запрос: 3, -2 ////////////////////////////////////////
ВЫБРАТЬ
МИНИМУМ(Свертки.ИД) КАК ИД
ПОМЕСТИТЬ ГруппыДублейНаборов
ИЗ
Хеши КАК Свертки
СГРУППИРОВАТЬ ПО
Свертки.Хеш
;
//{Запрос: 4, -1 ////////////////////////////////////////
ВЫБРАТЬ
ВходныеДанные.ИД КАК ИД,
ВходныеДанные.Значение КАК Значение
ИЗ
ВходныеДанные КАК ВходныеДанные
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ГруппыДублейНаборов КАК ИскомыеГруппы
ПО ВходныеДанные.ИД = ИскомыеГруппы.ИД
Результат
ИД | Значение |
П1 | А |
П1 | Б |
П1 | В |
П3 | Б |
П3 | В |
П5 | А |
П5 | В |
П5 | Г |