Нахождение уникальных наборов строк таблицы запросом

23.07.23

Разработка - Запросы

Решение задачи нахождения уникальных наборов строк таблицы запросом

Дано

У нас есть таблица с ключевой колонкой "ИД", значения которой определяют наборы связанных строк, и колонкой "Значение", в которой хранятся реквизиты строк набора. Дубли строк в таблице отсутствуют.

Пример таблицы входных данных:

 

ИД Значение
П1 А
П1 Б
П1 В
П2 А
П2 В 
П2 Б
П3 Б
П3 В
П4 А
П4 В
П4 Б
П5 А
П5 В
П5 Г
 
 
 Запишем эту таблицу в виде запроса 1С

Найти

Нужно получить таблицу, состоящую из уникальных наборов строк, т.е. в ней должны быть удалены все дубли наборов строк.

В нашем примере наборы строк с ключами П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 Г

См. также

Infostart Toolkit: Инструменты разработчика 1С 8.3 на управляемых формах

Инструментарий разработчика Роли и права Запросы СКД Платформа 1С v8.3 Управляемые формы Запросы Система компоновки данных Конфигурации 1cv8 Платные (руб)

Набор инструментов программиста и специалиста 1С для всех конфигураций на управляемых формах. В состав входят инструменты: Консоль запросов, Консоль СКД, Консоль кода, Редактор объекта, Анализ прав доступа, Метаданные, Поиск ссылок, Сравнение объектов, Все функции, Подписки на события и др. Редактор запросов и кода с раскраской и контекстной подсказкой. Доработанный конструктор запросов тонкого клиента. Продукт хорошо оптимизирован и обладает самым широким функционалом среди всех инструментов, представленных на рынке.

10000 руб.

02.09.2020    127279    689    389    

740

Пропорциональное распределение в запросе с использованием АвтоНомерЗаписи()

Запросы Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Часто поступают задачи по произвольному распределению общих сумм. После распределения иногда пропадают копейки. Суть решения добавить АвтоНомерЗаписи() в ВТ распределения, и далее используя функции МАКСИМУМ или МИНИМУМ можем положить разницу копеек в первую или последнюю строку знаменателя распределения.

11.04.2024    2415    andrey_sag    10    

29

Для чего используют конструкцию запроса "ГДЕ ЛОЖЬ" в СКД на примере конфигурации 1С:ERP

Запросы СКД Платформа 1С v8.3 Запросы Система компоновки данных 1С:ERP Управление предприятием 2 Бесплатно (free)

В типовых конфигурациях разработчики компании 1С иногда используют в отчетах, построенных на СКД, такую конструкцию, как "ГДЕ ЛОЖЬ". Такая конструкция говорит о том, что данные в запросе не будут получены совсем. Для чего же нужен тогда запрос?

13.02.2024    6108    KawaNoNeko    23    

26

Набор-объект для СКД по тексту или запросу

Запросы СКД Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Абонемент ($m)

Есть список полей в виде текста, или запрос - закидываем в набор СКД.

1 стартмани

31.01.2024    2189    2    Yashazz    0    

31

Запрос 1С copilot

Инструментарий разработчика Запросы Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Абонемент ($m)

Пишем на человеческом языке, что нам надо, и получаем текст запроса на языке 1С. Используются большие языковые модели (LLM GPT) от OpenAI или Яндекс на выбор.

5 стартмани

15.01.2024    6792    32    mkalimulin    31    

53

PrintWizard: поддержка представлений ЗУП в конструкторе

Инструментарий разработчика Запросы Платформа 1С v8.3 Бесплатно (free)

Одной из интересных задач, стоящих в процессе разработки, была поддержка механизма представлений в ЗУП. Но не просто возможность исполнения запросов с ними. Основная проблема была в том, чтобы с ними было удобно работать, а именно: создавать, модифицировать и отлаживать. Кратко о том, что в итоге получилось...

14.12.2023    1940    vandalsvq    7    

29

Объектная модель запроса "Схема запроса" 2

Запросы Платформа 1С v8.3 Запросы Конфигурации 1cv8 Бесплатно (free)

Далеко уже не новый тип данных "Схема запроса". Статья о том, как использовать его "попроще". Примеры создания текста запроса с нуля и изменение имеющегося запроса.

06.12.2023    5688    user1923546    26    

46

Начните уже использовать хранилище запросов

HighLoad оптимизация Запросы

Очень немногие из тех, кто занимается поддержкой MS SQL, работают с хранилищем запросов. А ведь хранилище запросов – это очень удобный, мощный и, главное, бесплатный инструмент, позволяющий быстро найти и локализовать проблему производительности и потребления ресурсов запросами. В статье расскажем о том, как использовать хранилище запросов в MS SQL и какие плюсы и минусы у него есть.

11.10.2023    16735    skovpin_sa    14    

101
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. dhurricane 24.07.23 04:39 Сейчас в теме
Мне кажется, что будет чуть нагляднее запрос, если конструкцию
ИМЕЮЩИЕ СУММА(ВЫБОР
		КОГДА ВходПравое.Значение ЕСТЬ NULL
			ТОГДА 0
		ИНАЧЕ 1
	КОНЕЦ) = ЧислоЧленовЛевое.Число
заменить на
ИМЕЮЩИЕ КОЛИЧЕСТВО(ВходПравое.Значение) = ЧислоЧленовЛевое.Число
Shmell; triviumfan; tormozit; +3 Ответить
2. tormozit 7146 24.07.23 07:34 Сейчас в теме
3. matol 24.07.23 10:09 Сейчас в теме
" Поэтому из них должен остаться лишь один набор, т.е. 3 строки с единым ключом П1 или П2 или П3." - наверное здесь правильно было бы написать "с единым ключом П1 или П2 или П4." ??
tormozit; +1 Ответить
4. tormozit 7146 24.07.23 10:43 Сейчас в теме
5. triviumfan 93 24.07.23 11:30 Сейчас в теме
Перечитал условие задачи 3 раза так и не понял, что требуется получить.
Чукча не читатель, но что-то Сергей тут намудрил.
7. SerVer1C 764 24.07.23 12:37 Сейчас в теме
(5) есть 3 группы с одинаковыми значениями, надо из них оставить 1 группу
Прикрепленные файлы:
6. SerVer1C 764 24.07.23 12:10 Сейчас в теме
Предлагаю альтернативный вариант:

1) находим уникальные значения;
2) определяем хеши для групп;
3) получаем неповторяющиеся группы;
4) получаем данные

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
	ВходныеДанные.Значение КАК Значение,
	АВТОНОМЕРЗАПИСИ() КАК НомерПП
ПОМЕСТИТЬ УникальныеЗначения
ИЗ
	ВходныеДанные КАК ВходныеДанные
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ВходныеДанные.ИД КАК ИД,
	СУММА(SQRT(УникальныеЗначения.НомерПП)) КАК ХешГруппы
ПОМЕСТИТЬ Хеши
ИЗ
	ВходныеДанные КАК ВходныеДанные
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ УникальныеЗначения КАК УникальныеЗначения
		ПО ВходныеДанные.Значение = УникальныеЗначения.Значение

СГРУППИРОВАТЬ ПО
	ВходныеДанные.ИД
;


////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	МИНИМУМ(Хеши.ИД) КАК ИД
ПОМЕСТИТЬ ИскомыеГруппы
ИЗ
	Хеши КАК Хеши

СГРУППИРОВАТЬ ПО
	Хеши.ХешГруппы
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ВходныеДанные.ИД КАК ИД,
	ВходныеДанные.Значение КАК Значение
ИЗ
	ВходныеДанные КАК ВходныеДанные
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ИскомыеГруппы КАК ИскомыеГруппы
		ПО ВходныеДанные.ИД = ИскомыеГруппы.ИД
Показать
Sothale; binx; Somebody1; mvxyz; Totoro; user1298974; Mahon83; kuzyara; e.kogan; Rafaraf; freemaestro; SP2000; fancy; maksa2005; siamagic; CK3; rfc_digital; asupsam; triviumfan; ildarovich; +20 Ответить
8. ildarovich 7865 24.07.23 14:05 Сейчас в теме
(6) Это самое лучшее решение! Заслуживает не одного плюса.

Правда, опирается на новые возможности платформы. Если их не использовать, можно посмотреть решение из статьи Расчет хэш-функции в запросе. Там были примеры для решения подобных задач.

Отличный метод расчета хэш. Не видел такого раньше. Интересно, какова вероятность коллизий?

А метод, приведенный в статье, будет "подвисать" на больших таблицах (больше тысячи строк).
shard; mvxyz; e.kogan; Rafaraf; siamagic; tormozit; +6 Ответить
10. triviumfan 93 24.07.23 14:11 Сейчас в теме
(8) с 8.3.13 это не сказать, что новые возможности. Не знаю ни одного клиента, кто использует ниже версию даже для обычного приложения.
PS: Сергей, всё ещё жду новых статей! 1с безгранична!
11. tormozit 7146 24.07.23 14:24 Сейчас в теме
(10) SQRT() добавлена в режиме совместимости 8.3.20
e.kogan; Rafaraf; asupsam; triviumfan; +4 Ответить
12. tormozit 7146 24.07.23 14:27 Сейчас в теме
(8) Да, к решаемой в моей статье задаче ближе всех подходит твой "Пример 1. Разбиение множества элементов номенклатуры на классы, имеющие одинаковое сочетание значений свойств."
14. RustIG 1619 24.07.23 14:54 Сейчас в теме
(12) сегодня сочетание значений свойств совпадает, завтра добавится новое значение в одном из свойстве, которое не подходит к другому свойству - я сталкивался с тем, что сочетания динамически меняются. Тогда какой смысл их в классы объединять?
17. tormozit 7146 24.07.23 15:10 Сейчас в теме
(14) Почему ты адресовал вопрос мне?
35. RustIG 1619 24.07.23 20:31 Сейчас в теме
(17) <Просто ошибся.>
в целом путано все с первого прочтения - и статья, и решение, и комменты - несколько раз пришлось перечитывать, чтобы пазл сложился... да еще получается, спросил не у того человека .
</Просто ошибся.>
39. tormozit 7146 24.07.23 20:35 Сейчас в теме
(35) Ну раз ты теперь все понял, то будет здорово, если ты поможешь мне перефразировать бывшие наиболее непонятными тебе фрагменты.
44. RustIG 1619 24.07.23 20:48 Сейчас в теме
(39) я посмотрел,Дано и Найти, и посмотрел Результат - разделил в голове на группы и задался вопросом, почему же в результате нет П4 - В или П2 - В ?
посмотрел снова условие Дано и Найти - выделил для себя ряд условий, затем в голове разбил на другие группы, и снова не понял, почему П4-В не подходит. Затем прочитал решение - оказывается ты ищешь минимальное Айди, хотя в Условиях этого не было.
Решил что это ваше допущение, сравнил Результат - и только потом увидел в своей голове Условие задачи.
Сразу подумал, что узкоспециализированная задача - задал вопрос про группы (множества) (А, А, А) - посмотрел запрос - увидел что запрос с подобными группами не справится.
Решил дождаться уточнений по задаче. По ходу дела - читал комменты - собирал пазл - добавил свой коммент.
Достаточно сложно передать условие задачи, когда оно абстрактно.
Без примеров.

Обычно примеры добавляют - было такое множество - стало такое в Результате. Требуется написать алгоритм (запрос).
У вас же ответ стал для меня неким маяком - о чем же просит автор задачи.
24. tormozit 7146 24.07.23 18:30 Сейчас в теме
(8) К сожалению твое хэширование предложено только для частного случая строковых значений (в примерах состоящих из цифровых символов). В моей статье намного более общий случай - произвольные значения. И да, он конечно будет работать заметно медленнее.
55. ildarovich 7865 25.07.23 11:09 Сейчас в теме
(24) Наверное, тогда еще не было функции АВТОНОМЕРЗАПИСИ(). Теперь она есть и это расширяет область применения запроса из той статьи.
26. PerlAmutor 129 24.07.23 19:06 Сейчас в теме
(8) Запустил на таблице в 10к строк. 30 секунд выполнялся.
27. tormozit 7146 24.07.23 19:16 Сейчас в теме
(26) Твоему сообщению не хватает однозначности.
29. PerlAmutor 129 24.07.23 20:12 Сейчас в теме
(27) Я отвечал на этот комментарий Ильдаровича

"А метод, приведенный в статье, будет "подвисать" на больших таблицах (больше тысячи строк)."
9. triviumfan 93 24.07.23 14:05 Сейчас в теме
(6) Красиво. Тебя даже Ильдарович лайкнул!
А это большого стоит)
18. ildarovich 7865 24.07.23 15:27 Сейчас в теме
(6) Все же расчет хэш-функции для практического применения здесь потребуется уточнить.

Иначе в одну группу попадут разные наборы с номерами по порядку и это только в пределах 100: (1, 4) и (9);(1, 9) и (16); (1, 16) и (25); (1, 25) и (36); (1, 36) и (49); (1, 49) и (64); (1, 64) и (81); (1, 81) и (100); (4, 9) и (25); (4, 16) и (36); (4, 25) и (49); (4, 36) и (64); (4, 49) и (81); (4, 64) и (100); (9, 25) и (64); (9, 36) и (81); (9, 49) и (100); (16, 25) и (81); (16, 36) и (100); ... (1, 81) и (4, 64) и (9, 49) и (16, 36); (1, 64) и (4, 49) и (16, 25);...

Не знаю, что там со случаями, когда корни не целые. Но эти очевидные коллизии настораживают. Сейчас есть и другие функции в запросах.

В общем, вопрос нужно еще исследовать...
19. dimaster 39 24.07.23 15:49 Сейчас в теме
(18) я так понял, вместо sqrt можно что-нить другое. типа Log10.
22. SerVer1C 764 24.07.23 16:26 Сейчас в теме
(19) Простой функции недостаточно, т.к. Log10 даст коллизию, например, для групп (10, 100) и (1000)
20. SerVer1C 764 24.07.23 16:07 Сейчас в теме
(18) Согласен, квадратный корень не всегда уместен. Можно взять более сложную функцию, например:
SQRT(НомерПП) + LOG10(НомерПП) - тогда результат почти всегда будет иррациональным. Тогда вряд ли будут коллизии, но моя математическая подушка, к сожалению, не позволяет мне найти доказательство для этого.
72. SerVer1C 764 27.07.23 10:02 Сейчас в теме
(20)
(6)
Подумал, что идеальным решением будет просто SQRT(НомерПП + SQRT(2)) - результат всегда иррациональный. В интернете при быстром поиске на нашел подтверждения, что могут существовать суммы иррациональных чисел, которые равны для разных наборов исходных слагаемых на положительной полуоси. Этот метод работает быстро и не упрется в переполнение даже для миллионов уникальных значений, в отличии от функции возведения в степень.
73. tormozit 7146 27.07.23 10:28 Сейчас в теме
(72) Да. Эта формула уже вызывает больше доверия.
25. tormozit 7146 24.07.23 18:39 Сейчас в теме
(6) Такого рода способы хэширования наборов очень быстрые и потому заманчивые. Но доказательство их корректности (вероятности коллизии) кажется будет довольно сложным для большого числа уникальных значений. Даже если НЕкорректность не будет доказана, использовать их придется лишь для задач, допускающих нестрогость решения.

Для небольшого числа уникальных значений (до 100 в СУБД MSSQL) гарантирует отсутствие коллизий формула
СУММА(Pow(2, УникальныеЗначения.НомерПП))
51. tormozit 7146 25.07.23 08:14 Сейчас в теме
(6) Добавил строгий вариант этого решения в статью в раздел "Решение 2 (хеширование)"
52. SerVer1C 764 25.07.23 09:40 Сейчас в теме
(51) Зачем добавлена ВТ "МинимальныйНомер" - там ВСЕГДА будет 1 . Если хотите степень двойки считать от 0, то просто вычитайте 1. (но это псевдооптимизация).
53. tormozit 7146 25.07.23 09:53 Сейчас в теме
Документация по платформе говорит что начальное значение НЕ гарантируется
Данная функция может быть использована в списке выборки при создании временной таблицы для создания поля с уникальным, последовательно возрастающим значением во временной таблице.

Аналогичный ответ дают и сами разработчики платформы.
SerVer1C; +1 Ответить
13. RustIG 1619 24.07.23 14:52 Сейчас в теме
а таких наборов разве не может быть: (А, А, Б), (А, А, А) ?
15. SerVer1C 764 24.07.23 15:08 Сейчас в теме
(13) кстати, да. Моё решение такой кейс успешно пережёвывает )
16. tormozit 7146 24.07.23 15:08 Сейчас в теме
(13) Дубли строк в таблице отсутствуют. Добавил эту информацию в раздел "Дано".
23. SerVer1C 764 24.07.23 16:41 Сейчас в теме
(16) Как говорит RustIG, если добавить в набор

П5 | А
П5 | А
П6 | А
П6 | А

то приведенный в статье алгоритм не справится с поставленной задачей.
30. PerlAmutor 129 24.07.23 20:16 Сейчас в теме
(23) Да, запрос из статьи не справился. Выдает одну строчку где во всех колонках NULL.
Мой вариант из другого комментария справился.
31. PerlAmutor 129 24.07.23 20:25 Сейчас в теме
(13) Да, такое похоже только хешем. Т.е. метод может еще и подойдет для уникальных комбинаций измерений.
Прикрепленные файлы:
32. tormozit 7146 24.07.23 20:29 Сейчас в теме
(31) Учись генерировать текст запроса создания входной таблицы https://www.hostedredmine.com/issues/948732
34. PerlAmutor 129 24.07.23 20:30 Сейчас в теме
36. tormozit 7146 24.07.23 20:32 Сейчас в теме
(34) Тогда учись применять умение =) Не заставляй других вручную набивать входные данные для повторения твоего теста.
41. PerlAmutor 129 24.07.23 20:38 Сейчас в теме
(36) Держи, для хорошего человека не жалко ))


ВЫБРАТЬ 1 КАК ИД, "A" КАК Значение
ПОМЕСТИТЬ ТЗ
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 1 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 1 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 3 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 3 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 4 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 4 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 5 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 6 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 7 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 7 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 7 КАК ИД, "D" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 8 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 8 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 8 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 9 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 9 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 9 КАК ИД, "A" КАК Значение


Показать
21. sapervodichka 6812 24.07.23 16:07 Сейчас в теме
Сергей, спасибо, за тест на ещё живой ум или уже скорее мёртвый ))) прочёл и понял, что мне точно нужно возобновлять чтение книг, отказывается мозг воспринимать информацию = ожирел
28. PerlAmutor 129 24.07.23 20:04 Сейчас в теме
Пожалуй выложу тоже свой вариант

ВЫБРАТЬ
	"П1" КАК ИД,
	"А" КАК Значение
ПОМЕСТИТЬ ВходныеДанные

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	"П1",
	"Б"

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	"П1",
	"В"

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	"П2",
	"А"

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	"П2",
	"В"

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	"П2",
	"Б"

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	"П3",
	"Б"

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	"П3",
	"В"

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	"П4",
	"А"

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	"П4",
	"В"

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	"П4",
	"Б"
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Т.ИД КАК ИД,
	КОЛИЧЕСТВО(*) КАК КолВо
ПОМЕСТИТЬ ВтИтоги
ИЗ
	ВходныеДанные КАК Т

СГРУППИРОВАТЬ ПО
	Т.ИД

ИНДЕКСИРОВАТЬ ПО
	Т.ИД
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Т.ИД КАК ИД,
	Т.Значение КАК Значение,
	ТИтоги.КолВо КАК КолВо
ПОМЕСТИТЬ ВтСвязь
ИЗ
	ВходныеДанные КАК Т
		ЛЕВОЕ СОЕДИНЕНИЕ ВтИтоги КАК ТИтоги
		ПО Т.ИД = ТИтоги.ИД
ГДЕ
	ТИтоги.ИД ЕСТЬ НЕ NULL 
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Т1.ИД КАК ИДЛевое,
	ОставляемСтроки.ИД КАК ИДПравое
ПОМЕСТИТЬ ПарыДублейНаборов
ИЗ
	ВтСвязь КАК Т1
		ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
			МИНИМУМ(Т2.ИД) КАК ИД,
			Т2.Значение КАК Значение,
			Т2.КолВо КАК КолВо
		ИЗ
			ВтСвязь КАК Т2
		
		СГРУППИРОВАТЬ ПО
			Т2.Значение,
			Т2.КолВо) КАК ОставляемСтроки
		ПО Т1.Значение = ОставляемСтроки.Значение
			И Т1.КолВо = ОставляемСтроки.КолВо
ГДЕ
	Т1.ИД <> ОставляемСтроки.ИД

СГРУППИРОВАТЬ ПО
	Т1.ИД,
	Т1.КолВо,
	ОставляемСтроки.ИД

ИМЕЮЩИЕ
	КОЛИЧЕСТВО(Т1.Значение) = Т1.КолВо
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ВходныеДанные.ИД КАК ИД,
	ВходныеДанные.Значение КАК Значение
ИЗ
	ВходныеДанные КАК ВходныеДанные
ГДЕ
	НЕ ИСТИНА В
				(ВЫБРАТЬ ПЕРВЫЕ 1
					ИСТИНА
				ИЗ
					ПарыДублейНаборов КАК Т
				ГДЕ
					ВходныеДанные.ИД = Т.ИДЛевое)
Показать
sapervodichka; +1 Ответить
33. Восьмой 88 24.07.23 20:29 Сейчас в теме
Коллеги подскажите пожалуйста, для каких практических задач это применимо?
37. PerlAmutor 129 24.07.23 20:34 Сейчас в теме
(33) Например
1. Поиск и исправление некорректных движений документов
2. Распределение одинаковых наборов строк по группам. В моем случае понадобилось растащить права пользователей на группы пользователей из самописного регистра, где каждому пользователю были выделены конкретные права, которые часто пересекались с такие же правами у других пользователей.
Восьмой; +1 Ответить
38. Восьмой 88 24.07.23 20:35 Сейчас в теме
(37)
Спасибо Коллега, возьму на заметку.
40. tormozit 7146 24.07.23 20:37 Сейчас в теме
(37) В твоей задаче №2 под правами имеются ввиду роли? Т.е. искать совпадения (а не пересечения) наборов ролей?
42. PerlAmutor 129 24.07.23 20:42 Сейчас в теме
(40) Нет, там группы складов (список доступных складов у каждого пользователя). Доработанный RLS под регистр и параметры сеансов. Он теперь мешает перейти на Производительный режим RLS, т.к. там невозможно использовать параметры сеансов, а значит остается использовать группы и профили...
43. tormozit 7146 24.07.23 20:48 Сейчас в теме
(42) Если будут желание, опиши подробнее свои задачи в заявке по ИР с прицелом на задачу проекта https://www.hostedredmine.com/issues/966601
Кстати по твоей задаче №1 я даже примерно не понял, что имеется ввиду.
45. PerlAmutor 129 24.07.23 20:50 Сейчас в теме
(43)
Про исправление движений документов? Ну в ERP частенько встречается, когда из-за левых соединение с дублями ключей аналитик или реестра документов движения и проводки задваиваются/зачетверяются и т.д.
46. tormozit 7146 24.07.23 20:52 Сейчас в теме
(45) Т.е. ключом набора строк в таблице регистра выступает регистратор+измерение1[+измерениеХ]?
47. PerlAmutor 129 24.07.23 20:53 Сейчас в теме
(46) Да, тут видимо пришлось бы запрос динамически генерировать по метаданным. Но для частных случаев поиска можно и вручную.
48. denacid 94 24.07.23 21:14 Сейчас в теме
Немного упростил для восприятия

ВЫБРАТЬ
	ВходныеДанные.ИД КАК ИД,
	КОЛИЧЕСТВО(*) КАК Число
ПОМЕСТИТЬ ЧислоЧленовВНаборе
ИЗ
	ВходныеДанные КАК ВходныеДанные
СГРУППИРОВАТЬ ПО
	ВходныеДанные.ИД
;

ВЫБРАТЬ
	ВходныеДанные.*,
	ЧислоЧленовВНаборе.Число КАК ЧислоВНаборе
ПОМЕСТИТЬ ВходныеДанныеСЧислом
ИЗ
	ВходныеДанные КАК ВходныеДанные
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
	ЧислоЧленовВНаборе КАК ЧислоЧленовВНаборе
ПО
	ВходныеДанные.ИД = ЧислоЧленовВНаборе.ИД	
;

ВЫБРАТЬ
	Вход1.Ид
ПОМЕСТИТЬ Дубли
ИЗ
	ВходныеДанныеСЧислом КАК Вход1
ЛЕВОЕ СОЕДИНЕНИЕ
	ВходныеДанныеСЧислом КАК Вход2
ПО
	Вход1.ИД > Вход2.ИД
	И Вход1.ЧислоВНаборе = Вход2.ЧислоВНаборе
	И Вход1.Значение = Вход2.Значение
СГРУППИРОВАТЬ ПО
	Вход1.Ид,
	Вход2.Ид
ИМЕЮЩИЕ
	КОЛИЧЕСТВО(Вход1.Значение) = КОЛИЧЕСТВО(Вход2.Значение)
;

ВЫБРАТЬ
	ВходныеДанные.*	
ИЗ
	ВходныеДанные КАК ВходныеДанные
ГДЕ
	НЕ Ид В(ВЫБРАТЬ Дубли.Ид Из Дубли)
Показать
49. tormozit 7146 24.07.23 22:51 Сейчас в теме
(48) Ты похоже тестировал только на примере из статьи. В нем действительно отработает корректно. Но некорректно сработает на таблице, если заменить в ее последней строке на "Г"
ВЫБРАТЬ
	"П1" КАК ИД,
	"А" КАК Значение
ПОМЕСТИТЬ ВходныеДанные
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
	"П1" КАК ИД,
	"Б" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
	"П1" КАК ИД,
	"В" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
	"П2" КАК ИД,
	"А" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
	"П2" КАК ИД,
	"В" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
	"П2" КАК ИД,
	"Б" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
	"П3" КАК ИД,
	"Б" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
	"П3" КАК ИД,
	"В" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
	"П4" КАК ИД,
	"А" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
	"П4" КАК ИД,
	"В" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ
	"П4" КАК ИД,
	"Г" КАК Значение
Показать

Расширил пример в статье, чтобы предупредить подобные ошибки.
50. denacid 94 24.07.23 23:28 Сейчас в теме
(49) Блин, точно, косякнул, так должно быть получше:
ВЫБРАТЬ
	ВходныеДанные.ИД КАК ИД,
	КОЛИЧЕСТВО(*) КАК Число
ПОМЕСТИТЬ ЧислоЧленовВНаборе
ИЗ
	ВходныеДанные КАК ВходныеДанные
СГРУППИРОВАТЬ ПО
	ВходныеДанные.ИД
;

ВЫБРАТЬ
	ВходныеДанные.*,
	ЧислоЧленовВНаборе.Число КАК ЧислоВНаборе
ПОМЕСТИТЬ ВходныеДанныеСЧислом
ИЗ
	ВходныеДанные КАК ВходныеДанные
ВНУТРЕННЕЕ СОЕДИНЕНИЕ
	ЧислоЧленовВНаборе КАК ЧислоЧленовВНаборе
ПО
	ВходныеДанные.ИД = ЧислоЧленовВНаборе.ИД	
;

ВЫБРАТЬ
	Вход1.Ид
ПОМЕСТИТЬ Дубли
ИЗ
	ВходныеДанныеСЧислом КАК Вход1
ЛЕВОЕ СОЕДИНЕНИЕ
	ВходныеДанныеСЧислом КАК Вход2
ПО
	Вход1.ИД > Вход2.ИД
	И Вход1.ЧислоВНаборе = Вход2.ЧислоВНаборе
	И Вход1.Значение = Вход2.Значение
СГРУППИРОВАТЬ ПО
	Вход1.Ид,
	Вход1.ЧислоВНаборе,
	Вход2.Ид
ИМЕЮЩИЕ
	Вход1.ЧислоВНаборе = КОЛИЧЕСТВО(Вход2.Значение)
;

ВЫБРАТЬ
	ВходныеДанные.*	
ИЗ
	ВходныеДанные КАК ВходныеДанные
ГДЕ
	НЕ Ид В(ВЫБРАТЬ Дубли.Ид Из Дубли)
Показать
54. ildarovich 7865 25.07.23 11:07 Сейчас в теме
(26) Нужно еще запустить на 1К, 100К. Предполагаю, что результат будет 0.3 сек, 3000 сек (50 минут!). Зависимость квадратичная.

Тогда как у запроса из (6) зависимость линейная. Все запросы (6) должны в секунду укладываться.

Есть разница? - Заставлять пользователя ждать пятьдесят минут ради задачи, которая решается за секунду?
56. ildarovich 7865 25.07.23 11:25 Сейчас в теме
(25) Если воспользоваться информацией по ссылке и провести эксперименты, отбросить СУБД DB2, переписать формулу в виде
СУММА(Pow(2, УникальныеЗначения.НомерПП) - 8)

то можно вытянуть еще штук 25 значений )). Получится 125.

Но строго говоря, это не хэш. Это битовая строка - уникальный идентификатор. Хороший пример ее применения. Но не для этой задачи. Здесь, по моему мнению, нужна проверенная хэш-функция. Чуть позже постараюсь обосновать. Вопрос интересный, дискуссионный.
tormozit; +1 Ответить
58. tormozit 7146 25.07.23 11:50 Сейчас в теме
(56) Согласен, что мою формулу скорее правильней называть "уникальной сверткой" или "сжатием в число" множеств, т.к. она не обеспечивает фиксированную емкость результата и имеет нулевую вероятность коллизий. Хеш-функция по определению имеет не нулевую вероятность коллизий, т.к. емкость результата у нее фиксирована. Хорошая хеш-функция имеет исчезающе малую вероятность коллизий в диапазоне применения. Жду уже 2-й день когда ты явишь ее нам и обоснуешь вероятность коллизий =)
63. ildarovich 7865 26.07.23 13:35 Сейчас в теме
(58)
Жду уже 2-й день когда ты явишь ее нам и обоснуешь вероятность коллизий =)
Честно говоря, таких намерений у меня не было, но если интерес есть, подумаю над этим.

Пока просьба над моим вопросом подумать.
Он в следующем посте.
64. ildarovich 7865 26.07.23 13:38 Сейчас в теме
(58) (63) Есть интересный вопрос, связанный с обсуждаемой темой. Но довольно общего характера.

Начну издалека.

По моему представлению, сам собой напрашивается комбинированный способ решения обсуждаемой в статье задачи:
На первом этапе применяется запрос типа (6) с "правильной" хэш-функцией.
На втором этапе найденные совпадения перепроверяются запросом типа первого из статьи.

Теперь предположим, что правильная хэш-функция дает вероятность коллизий 10^(-38).
То есть работает как MD5.
Это значит, что если все время жизни Вселенной (4,35*10^17 сек) каждую секунду выполнять запрос к таблице из миллиона наборов строк,
то только в одной из квадриллиона (10^15) параллельных вселенных один раз может появиться коллизия.

То есть весь код запроса второго этапа всегда будет работать зря!

Так вот, вопрос: готовы ли вы выкинуть этот код за его ненадобностью или нет?

А если упростить хэш-функцию до CRC32 с вероятностью коллизий 2^(-32)?

То есть где провести границу? - Какую вероятность коллизий можно посчитать достаточной ("исчезающе малой")?
65. tormozit 7146 26.07.23 13:50 Сейчас в теме
(63) MD5 подходит. Как ее посчитать в запросе?
Понятно что можно сначала порционным считыванием посчитать во встроенном языке и затем при желании кинуть в запрос пары ключ-хеш.
66. ildarovich 7865 26.07.23 14:15 Сейчас в теме
(65) Думаю, MD5 не посчитать. Во всяком случае, запросом разумной сложности. Но все свойства MD5 (кроме вероятности коллизий) нам не нужны. Это не задачи криптографии.

В хорошей хэш-функции требуется тщательно перемешивание битов входных аргументов. Сгодилось бы и CRC128 (если потребовать 2^(-128)).
67. tormozit 7146 26.07.23 14:22 Сейчас в теме
Ладно даю четкий ответ. Вероятность коллизий хочу менее 2^(-30)
ildarovich; +1 Ответить
68. ildarovich 7865 26.07.23 14:58 Сейчас в теме
(67) Приятно, что здесь наши точки зрения совпадают. Я тоже считал бы достаточным (по многим причинам) CRC32.

Хорошо, берем за основу.
77. ildarovich 7865 29.07.23 18:58 Сейчас в теме
(58)

Итак, перепробовав разные подходы (в том числе, расчет CRC32 в запросе), я пришел к очень простому заключительному выводу: для расчета хэш-функции в запросе на основе нумерации строк лучше всего подойдет функция

СУММА(SIN(НомерПП)),

либо функция

СУММА(COS(НомерПП)).

Любопытно, что это преобразование, фактически, является расчетом одного из коэффициентов ДПФ. То есть у него есть логическая основа.

Чтобы убедиться, что функция подходит, нужно рассчитать вероятность коллизий и убедиться, что она меньше заданной.

Для этого используем центральную предельную теорему теории вероятностей, которая говорит, что

что сумма достаточно большого количества слабо зависимых случайных величин, имеющих примерно одинаковые масштабы (ни одно из слагаемых не доминирует, не вносит в сумму определяющего вклада), имеет распределение, близкое к нормальному.

Это как раз наш случай.

То есть значение ХЭШ функции СУММА(SIN(НомерПП)) как случайная величина будет иметь нормальное распределение.

Математическое ожидание µ случайной величины СУММА(SIN(НомерПП)) будет равно нулю. И, исходя из формы графика плотности распределения вероятности (перевернутый колокол), ноль будет самым частым значением ХЭШ функции. Дисперсия σ^2 итогового распределения будет равна сумме дисперсий исходных распределений.

Дисперсия σ^2 случайной величины SIN(X) равна 0.5.

Потому, что рассчитывается как СРЕДНЕЕ(sin^2(x)) и известно, что СРЕДНЕЕ(sin^2(x)) = СРЕДНЕЕ(cos^2(x)) и sin^2(x) + cos^2(x) = 1.

Значит, дисперсия σ^2 случайной величины СУММА(SIN(НомерПП)) будет равна 0.5 N, а среднее квадратичное отклонение σ равно √(0.5 N), где N - число членов суммы.

Проверка показала, что значение СУММА(SIN(НомерПП)) считается в запросе с точностью девяти десятичных знаков.

Также у нас есть функция ошибок Гаусса erf(x), которая позволяет рассчитать вероятность того, что значение суммы будет отклоняться от математического ожидания (нуля) на вес младшего разряда ε = 10^(-9). А это и есть удвоенная вероятность того, что ХЭШ функция будет принимать значение ноль с точностью девяти десятичных знаков.

Таким образом, максимальная вероятность коллизий будет равна

0.5 erf (ε / (σ * √2 ) ).

Есть разложение

erf(x) = ( 2 / √π ) ( x - x^3 / 3 + x^5 / 10 - ... ).

Учитывая, что x в нашем случае x очень мало, формулу можно предельно упростить, оставив только первое слагаемое.

В итоге максимальная вероятность коллизий окажется равной

ε / ( √(0.5 N) * √(2 π)) = ε / √( π N) = 10^(-9) / √( π N),

и, значит, укладывается в заданные пределы. А в среднем будет еще меньше.
begemot; farukshin; tormozit; +3 Ответить
78. tormozit 7146 30.07.23 20:59 Сейчас в теме
(77) Конечно это не 10^-30. Но благодарю за как всегда тщательный подход к решению. Добавил в статью "Решение 3 (хеширование)" со ссылкой на этот комментарий.
57. ildarovich 7865 25.07.23 11:48 Сейчас в теме
(20) Коллизии будут обязательно.

Мы не можем взаимно однозначно отобразить множество разных сочетаний значений в группе, которых 2^(max(НомерПП)) и диапазон значений агрегатной функции 10^38.

А вообще формулы
SUM(SQRT(НомерПП)) 


SUM(SQRT(НомерПП)) + SUM(LOG10(НомерПП))


по зрелому размышлению, недостаточно эффективны (кстати, в последней формуле лучше тогда не суммировать, а отдельно считать ХЭШ1 и ХЭШ2).

Так как не используют весь диапазон возможных значений ХЭШ [0, 10^38] (упрощенно). А также не дают равную "плотность" этого отображения (которая определяет вероятность коллизии).

Так что лучше пользоваться проверенными преобразованиями типа приведенных в той статье, на которую я уже ссылался. Но можно изобрести что-то новое на базе появившихся функций. Минимальную вероятность коллизий (минимальную максимальную вероятность) обеспечит равномерное отображение на весь диапазон возможных значений ХЭШ.
59. SerVer1C 764 25.07.23 12:54 Сейчас в теме
(57)
(6)
Согласен, что вероятность коллизий <> 0

Тогда 2-ю и 3-ю ВТ можно переписать подобным образом:

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
	ВходныеДанные.ИД КАК ИД,
	СУММА(SIN(УникальныеЗначения.НомерПП)) КАК ХешГруппы1,
	СУММА(COS(УникальныеЗначения.НомерПП)) КАК ХешГруппы2,
	СУММА(LOG(УникальныеЗначения.НомерПП)) КАК ХешГруппы3,
	СУММА(LOG10(УникальныеЗначения.НомерПП)) КАК ХешГруппы4,
	СУММА(EXP(УникальныеЗначения.НомерПП)) КАК ХешГруппы5,
//	...
	СУММА(SQRT(УникальныеЗначения.НомерПП)) КАК ХешГруппыN
ПОМЕСТИТЬ Хеши
ИЗ
	ВходныеДанные КАК ВходныеДанные
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ УникальныеЗначения КАК УникальныеЗначения
		ПО ВходныеДанные.Значение = УникальныеЗначения.Значение

СГРУППИРОВАТЬ ПО
	ВходныеДанные.ИД
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	МИНИМУМ(Хеши.ИД) КАК ИД
ПОМЕСТИТЬ ИскомыеГруппы
ИЗ
	Хеши КАК Хеши

СГРУППИРОВАТЬ ПО
	Хеши.ХешГруппы1,
	Хеши.ХешГруппы2,
	Хеши.ХешГруппы3,
	Хеши.ХешГруппы4,
	Хеши.ХешГруппы5,
//	...
	Хеши.ХешГруппыN
;
Показать


где вместо многоточий можно вставить ещё большое кол-во функций в их разном сочетании. По моему предположению, вероятность коллизий будет стремиться к 0. Это можно применять даже на боевых базах - если и случится ошибка, то, условно, раз в 100 лет. Пользователи при работе с базой чаще ошибаются, т.е. человеческий фактор создаст больше проблем, чем очень редкая коллизия.
60. PerlAmutor 129 26.07.23 05:19 Сейчас в теме
Доработал запрос под ситуацию, когда может быть несколько групп со значениями П8:"ААА", П9:"ААА" и ошибочно определяется вторая пара, где первое значение в группе П1:"АБВ", т.к. содержит "А". Использовал КОЛИЧЕСТВО(РАЗЛИЧНЫЕ) для дополнительного условия


ВЫБРАТЬ 1 КАК ИД, "A" КАК Значение
ПОМЕСТИТЬ ТЗ
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 1 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 1 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 3 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 3 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 4 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 4 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 5 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 6 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 7 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 7 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 7 КАК ИД, "D" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 8 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 8 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 8 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 9 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 9 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 9 КАК ИД, "A" КАК Значение
;
ВЫБРАТЬ
	Т.ИД КАК ИД,
	КОЛИЧЕСТВО(*) КАК КолВо,
	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Т.Значение) КАК КолВоРазличных
ПОМЕСТИТЬ ВтИтоги
ИЗ
	ВходныеДанные КАК Т

СГРУППИРОВАТЬ ПО
	Т.ИД

ИНДЕКСИРОВАТЬ ПО
	Т.ИД
;
////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Т.ИД КАК ИД,
	Т.Значение КАК Значение,
	ТИтоги.КолВо КАК КолВо,
	ТИтоги.КолВоРазличных КАК КолВоРазличных
ПОМЕСТИТЬ ВтСвязь
ИЗ
	ВходныеДанные КАК Т
		ЛЕВОЕ СОЕДИНЕНИЕ ВтИтоги КАК ТИтоги
		ПО Т.ИД = ТИтоги.ИД
ГДЕ
	ТИтоги.ИД ЕСТЬ НЕ NULL 
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Т1.ИД КАК ИДЛевое,
	ОставляемСтроки.ИД КАК ИДПравое,
	ОставляемСтроки.КолВо,
	ОставляемСтроки.КолВоРазличных
ПОМЕСТИТЬ ПарыДублейНаборов
ИЗ
	ВтСвязь КАК Т1
		ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
			МИНИМУМ(Т2.ИД) КАК ИД,
			Т2.Значение КАК Значение,
			Т2.КолВо КАК КолВо,
			Т2.КолВоРазличных КАК КолВоРазличных
		ИЗ
			ВтСвязь КАК Т2
		
		СГРУППИРОВАТЬ ПО
			Т2.Значение,
			Т2.КолВо,
			Т2.КолВоРазличных) КАК ОставляемСтроки
		ПО Т1.Значение = ОставляемСтроки.Значение
			И Т1.КолВо = ОставляемСтроки.КолВо
			И Т1.КолВоРазличных = ОставляемСтроки.КолВоРазличных
ГДЕ
	Т1.ИД <> ОставляемСтроки.ИД

СГРУППИРОВАТЬ ПО
	Т1.ИД,
	Т1.КолВо,
	ОставляемСтроки.ИД,
	ОставляемСтроки.КолВо,
	ОставляемСтроки.КолВоРазличных

ИМЕЮЩИЕ
	КОЛИЧЕСТВО(Т1.Значение) = ОставляемСтроки.КолВо
	И КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Т1.Значение) = ОставляемСтроки.КолВоРазличных
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ВходныеДанные.ИД КАК ИД,
	ВходныеДанные.Значение КАК Значение
ИЗ
	ВходныеДанные КАК ВходныеДанные
ГДЕ
	НЕ ИСТИНА В
				(ВЫБРАТЬ ПЕРВЫЕ 1
					ИСТИНА
				ИЗ
					ПарыДублейНаборов КАК Т
				ГДЕ
					ВходныеДанные.ИД = Т.ИДЛевое)

Показать
61. zqzq 23 26.07.23 09:32 Сейчас в теме
Было бы удобнее для восприятия отсортировать пример по
ИД, Значение
, а не только по ИД.
62. e.kogan 1892 26.07.23 12:25 Сейчас в теме
Всё весело и вкусно, но когда я читаю "КОЛИЧЕСТВО(*) КАК Число", мне хочется спросить, ну неужели * тут будет лучше банального 1, а называть колонку именем типа хороший тон?
Так в целом ага, сама сколько раз такое решала разнообразно ) кстати, про автономерзаписи надо будет не забывать, спасибо за напоминание
69. PerlAmutor 129 26.07.23 20:33 Сейчас в теме
Переделал на объединение вместо "левого соединения", чтобы стабилизировать зависимость скорости выполнения от количества данных в двух таблицах.

ВЫБРАТЬ 1 КАК ИД, "A" КАК Значение
ПОМЕСТИТЬ ВходныеДанные
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 1 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 1 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 2 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 3 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 3 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 4 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 4 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 5 КАК ИД, "C" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 6 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 7 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 7 КАК ИД, "B" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 7 КАК ИД, "D" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 8 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 8 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 8 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 9 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 9 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 9 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 10 КАК ИД, "A" КАК Значение
ОБЪЕДИНИТЬ ВСЕ
ВЫБРАТЬ 10 КАК ИД, "A" КАК Значение;

//{Запрос: 1, -5 ////////////////////////////////////////
ВЫБРАТЬ
	Т.ИД КАК ИД,
	КОЛИЧЕСТВО(1) КАК КолВо,
	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Т.Значение) КАК КолВоРазличных
ПОМЕСТИТЬ ВтИтоги
ИЗ
	ВходныеДанные КАК Т
СГРУППИРОВАТЬ ПО
	Т.ИД
ИНДЕКСИРОВАТЬ ПО
	Т.ИД
;
//{Запрос: 2, -4 ////////////////////////////////////////
ВЫБРАТЬ
	Т.ИД КАК ИД,
	Т.Значение КАК Значение,
	ТИтоги.КолВо КАК КолВо,
	ТИтоги.КолВоРазличных КАК КолВоРазличных
ПОМЕСТИТЬ ВтСвязь
ИЗ
	ВходныеДанные КАК Т
	ЛЕВОЕ СОЕДИНЕНИЕ ВтИтоги КАК ТИтоги
	ПО Т.ИД = ТИтоги.ИД
ГДЕ ТИтоги.ИД ЕСТЬ НЕ NULL
;
//{Запрос: 3, -3 ////////////////////////////////////////
ВЫБРАТЬ
	Т.ИДЛевое КАК ИДЛевое,
	Т.ИДПравое КАК ИДПравое,
	Т.Значение КАК Значение,
	Т.КолВо КАК КолВо,
	Т.КолВоРазличных КАК КолВоРазличных,
	Т.Контроль КАК Контроль
ПОМЕСТИТЬ ВтДанные
ИЗ
	(ВЫБРАТЬ
		Т1.ИД КАК ИДЛевое,
		NULL КАК ИДПравое,
		Т1.Значение КАК Значение,
		Т1.КолВо КАК КолВо,
		Т1.КолВоРазличных КАК КолВоРазличных,
		1 КАК Контроль
	ИЗ
		ВтСвязь КАК Т1
	ОБЪЕДИНИТЬ ВСЕ
	ВЫБРАТЬ
		NULL КАК ИДЛевое,
		МИНИМУМ(Т2.ИД) КАК ИДПравое,
		Т2.Значение КАК Значение,
		Т2.КолВо КАК КолВо,
		Т2.КолВоРазличных КАК КолВоРазличных,
		- 1 КАК Контроль
	ИЗ
		ВтСвязь КАК Т2
	СГРУППИРОВАТЬ ПО
		Т2.Значение,
		Т2.КолВо,
		Т2.КолВоРазличных) КАК Т
;
//{Запрос: 4, -2 ////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
	МАКСИМУМ(Т.ИДЛевое) КАК ИДЛевое,
	МИНИМУМ(Т.ИДПравое) КАК ИДПравое
ПОМЕСТИТЬ ПарыДублейНаборов
ИЗ
	ВтДанные КАК Т
ГДЕ НЕ (ИСТИНА В (
		ВЫБРАТЬ ПЕРВЫЕ 1
			ИСТИНА КАК Поле1
		ИЗ
			ВтДанные КАК Т2
		ГДЕ Т.ИДЛевое = Т2.ИДПравое))
СГРУППИРОВАТЬ ПО
	Т.Значение,
	Т.КолВо,
	Т.КолВоРазличных
ИМЕЮЩИЕ Т.КолВо - Т.КолВоРазличных = СУММА(Т.Контроль)
;
//{Запрос: 5, -1 ////////////////////////////////////////
ВЫБРАТЬ
	ВходныеДанные.ИД КАК ИД,
	ВходныеДанные.Значение КАК Значение
ИЗ
	ВходныеДанные КАК ВходныеДанные
ГДЕ НЕ (ИСТИНА В (
	ВЫБРАТЬ ПЕРВЫЕ 1
		ИСТИНА КАК Поле1
	ИЗ
		ПарыДублейНаборов КАК Т
	ГДЕ ВходныеДанные.ИД = Т.ИДЛевое))
Показать
70. tormozit 7146 26.07.23 20:59 Сейчас в теме
(69) Регрессия. Исходный тест не пройден.
71. PerlAmutor 129 26.07.23 21:25 Сейчас в теме
(70) Да, проблемка. Последним запросом находятся лишь пары групп дублей, а если их 3 и более, то потребовалось бы писать CTE, которое в цикле делает объединение до тех пор пока не останется дублей. Т.е. у меня получился однопроходный запрос.
74. ildarovich 7865 27.07.23 13:51 Сейчас в теме
(72)
SQRT(НомерПП + SQRT(2)) - результат всегда иррациональный
- тут ошибка. Расчет идет с конечной точностью (чаще всего до девяти знаков после запятой). Поэтому результат все же рациональный (это всегда десятичная дробь).

И проблема тут не только в выборе функции. Кстати, косинус (или синус) от аргумента, задаваемого в радианах, кажется, подойдет еще лучше. Так как на области значений даст более равномерное покрытие.

Проблема в агрегатной функции СУММА. Сумма большого количества аргументов дает нормальное распределение. Это означает, что срединные значения ХЭШ-функции будут более вероятны, чем крайние. То есть разряды ХЭШ будут использоваться не с максимальной эффективностью.

Вообще, когда за вами гонятся, можно закидать преследователя всем чем под руку попадется. Это я о решении из (59). Оно "на скорую руку". Но если такой спешки нет, лучше подыскать что-то из области более бережливого программирования.
Krio2; tormozit; SerVer1C; +3 Ответить
75. SerVer1C 764 27.07.23 14:05 Сейчас в теме
(74) Да, машина не будет хранить бесконечное кол-во разрядов. Посмотрел: в MS - 14 знаков после запятой, в PG - 16 знаков. Эска ещё больше хранит. Ну даже эти 14 знаков для данной задачи достаточно. В разумных базах кол-во записей для обработки в запросе не превышает 100 млн. Получается, вероятность "вляпаться" 1 на миллион запросов.
76. naf2000 28.07.23 11:33 Сейчас в теме
Некоторая вариация первого решения:

ВЫБРАТЬ
	ВходныеДанные1.ИД КАК ИД1,
	ВходныеДанные2.ИД КАК ИД2,
	ВходныеДанные1.Значение КАК Значение
ПОМЕСТИТЬ Пересечения
ИЗ
	ВходныеДанные КАК ВходныеДанные1
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВходныеДанные КАК ВходныеДанные2
		ПО ВходныеДанные1.ИД <= ВходныеДанные2.ИД
			И ВходныеДанные1.Значение = ВходныеДанные2.Значение
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
	Пересечения.ИД1 КАК ИД1,
	Пересечения.ИД2 КАК ИД2
ПОМЕСТИТЬ Пары
ИЗ
	Пересечения КАК Пересечения
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Пары.ИД1 КАК ИД1,
	Пары.ИД2 КАК ИД2,
	ВходныеДанные.Значение КАК Значение,
	1 КАК Сумматор
ПОМЕСТИТЬ Объединение
ИЗ
	Пары КАК Пары
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВходныеДанные КАК ВходныеДанные
		ПО Пары.ИД1 = ВходныеДанные.ИД

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	Пары.ИД1,
	Пары.ИД2,
	ВходныеДанные.Значение,
	1
ИЗ
	Пары КАК Пары
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВходныеДанные КАК ВходныеДанные
		ПО Пары.ИД2 = ВходныеДанные.ИД

ОБЪЕДИНИТЬ ВСЕ

ВЫБРАТЬ
	Пересечения.ИД1,
	Пересечения.ИД2,
	Пересечения.Значение,
	-2
ИЗ
	Пересечения КАК Пересечения
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	Объединение.ИД1 КАК ИД1,
	Объединение.ИД2 КАК ИД2
ПОМЕСТИТЬ Отношения
ИЗ
	Объединение КАК Объединение

СГРУППИРОВАТЬ ПО
	Объединение.ИД1,
	Объединение.ИД2

ИМЕЮЩИЕ
	СУММА(Объединение.Сумматор) = 0
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	МИНИМУМ(Отношения.ИД1) КАК ИД1,
	Отношения.ИД2 КАК ИД2
ПОМЕСТИТЬ Классы
ИЗ
	Отношения КАК Отношения

СГРУППИРОВАТЬ ПО
	Отношения.ИД2
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
	Классы.ИД1 КАК ИД
ПОМЕСТИТЬ ПредставителиКлассов
ИЗ
	Классы КАК Классы
;

////////////////////////////////////////////////////////////­////////////////////
ВЫБРАТЬ
	ВходныеДанные.ИД КАК ИД,
	ВходныеДанные.Значение КАК Значение
ИЗ
	ПредставителиКлассов КАК ПредставителиКлассов
		ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВходныеДанные КАК ВходныеДанные
		ПО ПредставителиКлассов.ИД = ВходныеДанные.ИД
Показать
79. user889675 03.10.23 15:23 Сейчас в теме
Пример в статье и комментаторы оперируют "ВходныеДанные", но как быть если у меня вместо набора из трех значений (А,Б,В) - триста, или три тысячи? Или не одна колонка, по которой требуется выбрать записи с уникальными сочетаниями?
Оставьте свое сообщение