наименование колонок и типы данных в колонках идентичны, обе таблицы отсортированы по данным первой колонке. как оптимально произвести их сравнение?
---
По результатам текущего обсуждения выпущена статья Лучшие методы сравнения таблиц значений.

В ней задача обобщена на случай составного ключа. Для этого все функции переписаны. Также изменено представления результатов сравнения. Это я сделал на свой страх и риск. Возражения принимаются.

Поэтому возможны помарки и может оказаться необходимой перепроверка. Если есть желание - подключайтесь. Обнаружены интересные артефакты, связанные, вероятно с местом хранения строк таблицы значений. Они еще требуют объяснения.

Несколько методов осталось "в запасе". Например, использование нового объекта 8.3, позволяющего рассчитать хэш-функцию. Ну и разные доработки свертки.

Но зато выводы получились очень четкими. Каждому методу нашлось свое место. Буквально таблицу решений по выбору метода можно построить. Посмотрю на результаты обсуждения (если оно будет) и сделаю это.
---
kostyaomsk, причина пояснит приложенная картинка. Это результаты теста при использовании SQL-сервера. Тут есть и запросные (вторая часть строчек) и беззапросные методы. Обратите внимание на последнюю строку. Это время выполнения вот такой процедуры
```1c
    Функция СравнениеТаблицЗначений(Таблица1, Таблица2, ПрочиеКолонки = ") Экспорт

    ИмяКлюча = Таблица1.Колонки[0].Имя;

    Для ё = 1 По Таблица1.Колонки.Количество() - 1 Цикл ПрочиеКолонки = ПрочиеКолонки + ", " + Таблица1.Колонки[ё].Имя
    КонецЦикла;

    Запрос = Новый Запрос(

    "ВЫБРАТЬ Т.Ключ, -1 КАК Знак
    |ПОМЕСТИТЬ Т1
    |ИЗ &Таблица1 КАК Т
    |;
    |ВЫБРАТЬ Т.Ключ, 1 КАК Знак
    |ПОМЕСТИТЬ Т2
    |ИЗ &Таблица2 КАК Т"

    );

    Запрос.Текст = СтрЗаменить(Запрос.Текст, "Т.Ключ,", ИмяКлюча + " КАК Ключ" + ПрочиеКолонки + ",");

    Запрос.УстановитьПараметр("Таблица1", Таблица1);
    Запрос.УстановитьПараметр("Таблица2", Таблица2);

    Возврат Запрос.Выполнить().Выгрузить()

    КонецФункции
```
В ней ничего не делается. Никакой обработки. Только производится ввод данных из таблиц значений во временные таблицы. Видите время? - 1896 мс. А за это время - за 1588 мс. свертка уже успела получить и выдать готовый результат!
Конечно, если вычесть это время из времени 2631 самого быстрого запроса полного соединения, то получается, что на обработку SQL потратит меньше времени 735 мс, но вод передача данных в запрос все портит!
---
У меня была похожая задачка. Могу посоветовать выгрузить ТЗ в табличные части и сравнивать их запросом

// Функция сравнивает ТЧ док "ОприходованиеТоваров" и ТЧ док "ЗаказНаПроизводство" (с учетом всех корректировок)
// Если они одинаковые вернет Истину в противном случае Ложь
Функция ПроверкаЗаполненияОприходования(ОприходованиеТоваров,ЗаказНаПроизводство)

 Запрос = Новый Запрос;
 Запрос.Текст =
  "ВЫБРАТЬ РАЗРЕШЕННЫЕ
  | Подсчет.Номенклатура,
  | Подсчет.ЕдиницаИзмерения,
  | Подсчет.Количество,
  | СУММА(Подсчет.Счетчик) КАК СчетчикМин,
  | СУММА(Подсчет.Счетчик) КАК СчетчикМакс
  |ИЗ
  | (ВЫБРАТЬ
  | ПотребностиЗаказовНаПроизводство.Номенклатура КАК Номенклатура,
  | ПотребностиЗаказовНаПроизводство.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
  | ПотребностиЗаказовНаПроизводство.Количество КАК Количество,
  | 0.5 КАК Счетчик
  | ИЗ
  | РегистрНакопления.ПотребностиЗаказовНаПроизводство КАК ПотребностиЗаказовНаПроизводство
  | ГДЕ
  | ПотребностиЗаказовНаПроизводство.ЗаказНаПроизводство.Ссылка = &ЗаказНаПроизводство
  |
  | ОБЪЕДИНИТЬ ВСЕ
  |
  | ВЫБРАТЬ
  | ОприходованиеТоваровТовары.Номенклатура,
  | ОприходованиеТоваровТовары.ЕдиницаИзмерения,
  | ОприходованиеТоваровТовары.Количество,
  | 0.5
  | ИЗ
  | Документ.ОприходованиеТоваров.Товары КАК ОприходованиеТоваровТовары
  | ГДЕ
  | ОприходованиеТоваровТовары.Ссылка = &ОприходованиеТоваров) КАК Подсчет
  |
  |СГРУППИРОВАТЬ ПО
  | Подсчет.Номенклатура,
  | Подсчет.Количество,
  | Подсчет.ЕдиницаИзмерения
  |ИТОГИ
  | МИНИМУМ(СчетчикМин),
  | МАКСИМУМ(СчетчикМакс)
  |ПО
  | ОБЩИЕ";
 Запрос.УстановитьПараметр("ОприходованиеТоваров", ОприходованиеТоваров);
 Запрос.УстановитьПараметр("ЗаказНаПроизводство", ЗаказНаПроизводство);
 Выборка = Запрос.Выполнить().Выбрать();
 Выборка.Следующий();

 ТЧ_Совпадают = (Выборка.СчетчикМин = Выборка.СчетчикМакс И Выборка.СчетчикМин = 1);

 Возврат ТЧ_Совпадают;

КонецФункции
//...-Никулин
---
Nadushka74, есть в типовых конфигурация процедура сравнения двух таблиц. В моей УСО:
```1c
    Функция СравнитьТаблицыНаборовЗаписей(ТаблицаЗначений1, ТаблицаЗначений2) Экспорт

    Если ТипЗнч(ТаблицаЗначений1) <> Тип("ТаблицаЗначений") ИЛИ ТипЗнч(ТаблицаЗначений2) <> Тип("ТаблицаЗначений") Тогда
    Возврат Ложь;
    КонецЕсли;

    Если ТаблицаЗначений1.Количество() <> ТаблицаЗначений2.Количество() Тогда
    Возврат Ложь;
    КонецЕсли;

    Если ТаблицаЗначений1.Колонки.Количество() <> ТаблицаЗначений2.Колонки.Количество() Тогда
    Возврат Ложь;
    КонецЕсли;

    // Проверим поля
    Для каждого Колонка Из ТаблицаЗначений1.Колонки Цикл
    Если ТаблицаЗначений2.Колонки.Найти(Колонка.Имя) = Неопределено Тогда
    Возврат Ложь;
    КонецЕсли;
    КонецЦикла;
    Для каждого Колонка Из ТаблицаЗначений2.Колонки Цикл
    Если ТаблицаЗначений1.Колонки.Найти(Колонка.Имя) = Неопределено Тогда
    Возврат Ложь;
    КонецЕсли;
    КонецЦикла;

    // сформируем строку индекса для оптимизации поиска по таблице значений
    СтрокаИндекса = ";
    Для каждого Колонка Из ТаблицаЗначений1.Колонки Цикл
    Если СтрокаИндекса = " Тогда
    СтрокаИндекса = Колонка.Имя;
    Иначе
    СтрокаИндекса = СтрокаИндекса+","+Колонка.Имя;
    КонецЕсли;
    КонецЦикла;
    // добавим индекс
    ТаблицаЗначений2.Индексы.Добавить(СтрокаИндекса);

    // Проверим записи
    Для каждого СтрокаТаблицы Из ТаблицаЗначений1 Цикл
    СтруктураПоиска = Новый Структура;
    Для каждого Колонка Из ТаблицаЗначений1.Колонки Цикл
    СтруктураПоиска.Вставить(Колонка.Имя, СтрокаТаблицы[Колонка.Имя]);
    КонецЦикла;
    СтрокиТаблицы2 = ТаблицаЗначений2.НайтиСтроки(СтруктураПоиска);
    Если СтрокиТаблицы2.Количество() <> 1 Тогда
    Возврат Ложь;
    КонецЕсли;
    КонецЦикла;

    // сформируем строку индекса для оптимизации поиска по таблице значений
    СтрокаИндекса = ";
    Для каждого Колонка Из ТаблицаЗначений2.Колонки Цикл
    Если СтрокаИндекса = " Тогда
    СтрокаИндекса = Колонка.Имя;
    Иначе
    СтрокаИндекса = СтрокаИндекса+","+Колонка.Имя;
    КонецЕсли;
    КонецЦикла;
    // добавим индекс
    ТаблицаЗначений1.Индексы.Добавить(СтрокаИндекса);

    Для каждого СтрокаТаблицы Из ТаблицаЗначений2 Цикл
    СтруктураПоиска = Новый Структура;
    Для каждого Колонка Из ТаблицаЗначений2.Колонки Цикл
    СтруктураПоиска.Вставить(Колонка.Имя, СтрокаТаблицы[Колонка.Имя]);
    КонецЦикла;
    СтрокиТаблицы1 = ТаблицаЗначений1.НайтиСтроки(СтруктураПоиска);
    Если СтрокиТаблицы1.Количество() <> 1 Тогда
    Возврат Ложь;
    КонецЕсли;
    КонецЦикла;

    Возврат Истина;

    КонецФункции // СравнитьТаблицыЗначений()
```
---
решение по скорости медленнее раз в 100 , если сравнивть тз в 10 000 строк
---
Nadushka74, а просто
```1c
    Если ТаблицаЗначений1 <> ТаблицаЗначений2 Тогда
    Возврат "Разные";
    КонецЕсли;
```
не работает?
или - нужно именно отличия получить?
---
Nadushka74, Выгрузить в Excel.
Одна таблица на один лист, вторая на второй.
На третьем листе пишем функцию а1лист1=а1лист2 и растягиваем ее на ширину колонок таблицы и на глубину таблицы.
На четветом листе делаем функцию "И" с параметрами таблицы с третьего листа (вся таблица).
---
Хм... интересную штуку заметил у ТС.
В (1) говорится об оптимальном сравнении таблиц. Вопрос - что есть оптимально?
В современной теории разработки прикладного ПО (не системного) основной упор делается на удобство модификации и поддержки кода.
Исходя из этого, наиболее оптимальный ответ в (5).
---
Lama12-1, код в (5) только по форме правильный, а по сути - ужасен. То, что делается одной строкой кода растянуто на 76 строк. Есть такое выражение "переливать из пустого в порожнее". Это можно делать не только в устной речи, но и в письменной и при написании кода тоже.
Можно было бы просто написать что то типа
```1c
    Функция СравнитьТаблицыНаборовЗаписей(ТаблицаЗначений1, ТаблицаЗначений2, ИменаПолейЧерезЗапятую) Экспорт
    Возврат ТаблицаЗначений1.Сортировать(ИменаПолейЧерезЗапятую).ЗначениеВСтрокуВнутр() = ТаблицаЗначений2.Сортировать(ИменаПолейЧерезЗапятую).ЗначениеВСтрокуВнутр()
    КонецФункции
```
А вместо этого... Мало того, что (5) это длинно, но еще и работает жутко медленно.
---
ну, такое прокатит, если обе тз отсортированы изначально одинаково.
---
Nadushka74,

Который раз меня удивляет отсутствие метода отбора "НЕ",
а было бы так просто...

1) сравнил размеры таблиц (количество строк)
2) меньшую запихал в отбор
3) из второй выбрал строки методом "НЕ ОТБОР"

получил строки которых нет в первой таблице

П.С. Правда в первой таблице могут быть строки которых нет во второй...
Поэтому просьба к автору топика

Конкретизировать критерии сравнения...

Например:
- Найти строки котрые совпадают в обоих таблицах
- Найти строки которые отличаются в обоих таблицах
- Найти строки которые "добавились" в одной из таблиц
-...
---
Кстати проверил как лучше заполнять результат

```1c
    Массив=ЗначениеИзФайла(КаталогПрограммы()+"ТаблицыДляТестов");
    Тз1=Массив[0];

    Старт = ТочноеТекущееВремя();

    Рез=Новый массив;

    Для каждого Стр Из Тз1 Цикл

    массив=Новый массив;
    Массив.Добавить(стр[0]);
    Массив.Добавить(1);
    Рез.Добавить(массив);
    КонецЦикла;
    Сообщить("Время массива с добавлением " + (ТочноеТекущееВремя() - Старт)+ " мсек.");

    Старт = ТочноеТекущееВремя();

    Рез=Новый массив;

    Для каждого Стр Из Тз1 Цикл

    массив=Новый массив(2);
    Массив[0]=стр[0];
    Массив[1]=1;
    Рез.Добавить(массив);
    КонецЦикла;
    Сообщить("Время массива по индексу " + (ТочноеТекущееВремя() - Старт)+ " мсек.");

    Старт = ТочноеТекущееВремя();

    Рез=Новый Соответствие;

    Для каждого Стр Из Тз1 Цикл

    рез[стр[0]]=1;

    КонецЦикла;
    Сообщить("Время соответствия " + (ТочноеТекущееВремя() - Старт)+ " мсек.");
```

Время массива с добавлением 136 мсек.
Время массива по индексу 133 мсек.
Время соответствия 76 мсек.
---
Serginio, Советую перейти на: ТекущаяУниверсальнаяДатаВМиллисекундах() - Встроенная в 1С функция получения даты в миллисекундах...
---
Спасибо
---
Serginio, это всего лишь значит, что два больше чем один, то есть два оператора медленнее, чем один, а не сравнение массив - соответствие.
Чтобы точно сравнить скорость массива и соответствия предложенным кодом, нужно еще вычесть время работы "обертки", то есть прогнать холостой цикл - без всего и вычесть его время из полученного.
Я попробовал загонять результат в разные массивы, чтобы не хранить там же отметку: что это за ключ (удаленный, измененный, добавленный). Но общей картины это не поменяло.
---
Ну вы тут и срач развели 100500 сообщений. А в (1) всего навсего из монотонных таблиц, где изменили петрова на сидорова нужно узнать что теперь у нас в строке 5 стоит сидоров.
---
Только идеи :)
1. Запросом (но смотря сколько колонок нужно сравнивать)
2. Циклом в цикле :)
---
340+ Если так модифицировать то слияние на некоторых тестах обходит Хэш

```1c
    Функция ПолучитьИдексПоля(Тз,имяПоля)

    Колонки=Тз.Колонки;
    Колонка=Колонки.Найти(ИмяПоля);

    Если Колонка=неопределено Тогда
    тз.ВыбратьСтроку();

    ВызватьИсключение "Не найдена колонка "+ИмяПоля;
    КонецЕсли;

    возврат Колонки.Индекс(Колонка);
    КонецФункции

    Функция ПолучитьМассивИдексовПолей(Тз,Колонки)
    Рез=Новый Массив;
    Для каждого стр из Колонки Цикл
    рез.Добавить(ПолучитьИдексПоля(Тз,стр))
    КонецЦикла;
    возврат рез
    КонецФункции

    Функция СравнитьТаблицы(Т1,Т2,Колонки, Ключ) Экспорт

    РезТ1=Новый Массив;
    РезТ2=Новый Массив;
    Одинаковые=Новый Массив;
    Измененные=Новый Массив;
    ИндексКлючаТ1=ПолучитьИдексПоля(Т1,Ключ);
    ИндексКлючаТ2=ПолучитьИдексПоля(Т2,Ключ);

    Индекс2=0;
    КолСтрВТз2=Т2.Количество();
    Сравнение = Новый СравнениеЗначений;

    ИндексыКолонокТ1=ПолучитьМассивИдексовПолей(Т1,Колонки);
    ИндексыКолонокТ2=ПолучитьМассивИдексовПолей(Т2,Колонки);
    // СтрТ1=ПолучитьСтроку165(Т1,Индекс1);

    Если Индекс2 <> КолСтрВТз2 Тогда
    СтрТ2=Т2[Индекс2];
    Знач2=СтрТ2[ИндексКлючаТ2];
    КонецЕсли;

    Для каждого СтрТ1 Из т1 Цикл

    Если Индекс2 = КолСтрВТз2 Тогда
    РезТ1.Добавить(СтрТ1);
    Продолжить;
    КонецЕсли;

    Знач1=СтрТ1[ИндексКлючаТ1];
    Пока истина Цикл
    РезультатСравнения = Сравнение.Сравнить(Знач1,Знач2);
    Если РезультатСравнения < 0 Тогда
    РезТ1.Добавить(СтрТ1);

    прервать
    ИначеЕсли РезультатСравнения > 0 Тогда
    РезТ2.Добавить(СтрТ2);
    Индекс2=Индекс2+1;
    Если Индекс2 <> КолСтрВТз2 Тогда
    СтрТ2=Т2[Индекс2];
    Знач2=СтрТ2[ИндексКлючаТ2];
    Иначе
    прервать
    КонецЕсли;

    Иначе
    Массив=Новый Массив(2);
    Массив[0]=СтрТ1;
    Массив[1]=СтрТ2;

    КолонкиИдентичны=истина;
    сч2=0;
    Для каждого сч из ИндексыКолонокТ1 Цикл
    если СтрТ1[сч]<>СтрТ2[ИндексыКолонокТ2[сч2]] Тогда
    КолонкиИдентичны=ложь;
    прервать
    КонецЕсли;
    сч2=сч2+1;
    КонецЦикла;

    Если КолонкиИдентичны Тогда
    Одинаковые.Добавить(массив);
    Иначе
    Измененные.Добавить(массив);
    КонецЕсли;

    Индекс2=Индекс2+1;
    Если Индекс2 <> КолСтрВТз2 Тогда
    СтрТ2=Т2[Индекс2];
    Знач2=СтрТ2[ИндексКлючаТ2];
    КонецЕсли;

    прервать
    КонецЕсли;
    КонецЦикла;
    КонецЦикла;

    Пока Индекс2 <> КолСтрВТз2 Цикл
    СтрТ2=Т2[Индекс2];
    РезТ2.Добавить(СтрТ2);
    Индекс2=Индекс2+1;
    КонецЦикла;

    Результат = Новый Структура("Одинаковые, Т1, Т2, Измененные",Одинаковые,РезТ1,РезТ2,Измененные);

    Возврат Результат;
    КонецФункции
```
---
Serginio, я переписал все упомянутые способы в едином стиле. При этом считал, что ответ нужен в виде пар: ключ-знак, где знак отмечает удаление, изменение или добавление строки, идентифицируемой значением "ключ". Таким образом, я посчитал, что одинаковые строки отмечать не нужно. При этом ваше слияние в моей интерпретации выглядит так:
```1c
    Функция СравнениеТаблицЗначений_(Т1, Т2, Индекс1 = 0, Индекс2 = 0) Экспорт

    Ответ = Новый Соответствие;

    Сравнение = Новый СравнениеЗначений;

    ВГраница1 = Т1.Количество() - 1;
    ВГраница2 = Т2.Количество() - 1;

    ВГраница = Т1.Колонки.Количество() - 1;

    Пока Индекс1 <= ВГраница1 И Индекс2 <= ВГраница2 Цикл

    Ключ1 = Т1[Индекс1][0];
    Ключ2 = Т2[Индекс2][0];

    РезультатСравнения = Сравнение.Сравнить(Ключ1, Ключ2);

    Если РезультатСравнения < 0 Тогда
    Ответ[Ключ1] = -1;
    Индекс1 = Индекс1 + 1
    ИначеЕсли РезультатСравнения > 0 Тогда
    Ответ[Ключ2] = 1;
    Индекс2 = Индекс2 + 1
    Иначе
    Для ё = 1 По ВГраница Цикл
    Если Т1[Индекс1][ё] <> Т2[Индекс2][ё] Тогда
    Ответ[Ключ1] = 0;
    Прервать
    КонецЕсли
    КонецЦикла;
    Индекс1 = Индекс1 + 1;
    Индекс2 = Индекс2 + 1
    КонецЕсли

    КонецЦикла;

    Пока Индекс1 <= ВГраница1 Цикл
    Ответ[Т1[Индекс1][0]] = -1;
    Индекс1 = Индекс1 + 1
    КонецЦикла;

    Пока Индекс2 <= ВГраница2 Цикл
    Ответ[Т2[Индекс2][0]] = 1;
    Индекс2 = Индекс2 + 1
    КонецЦикла;

    Результат = Новый ТаблицаЗначений; Результат.Колонки.Добавить("Ключ"); Результат.Колонки.Добавить("Значение");

    Для Каждого Элемент Из Ответ Цикл
    ЗаполнитьЗначенияСвойств(Результат.Добавить(), Элемент)
    КонецЦикла;

    Результат.Колонки[1].Имя = "Знак";

    Возврат Результат

    КонецФункции
```
Основной цикл здесь
```1c
    Пока Индекс1 <= ВГраница1 И Индекс2 <= ВГраница2 Цикл

    Ключ1 = Т1[Индекс1][0];
    Ключ2 = Т2[Индекс2][0];

    РезультатСравнения = Сравнение.Сравнить(Ключ1, Ключ2);

    Если РезультатСравнения < 0 Тогда
    Ответ[Ключ1] = -1;
    Индекс1 = Индекс1 + 1
    ИначеЕсли РезультатСравнения > 0 Тогда
    Ответ[Ключ2] = 1;
    Индекс2 = Индекс2 + 1
    Иначе
    Для ё = 1 По ВГраница Цикл
    Если Т1[Индекс1][ё] <> Т2[Индекс2][ё] Тогда
    Ответ[Ключ1] = 0;
    Прервать
    КонецЕсли
    КонецЦикла;
    Индекс1 = Индекс1 + 1;
    Индекс2 = Индекс2 + 1
    КонецЕсли

    КонецЦикла;
```
Вряд ли отсюда можно еще что-то убрать. //Хотя посмотрел еще раз на код и понял, что можно взять здесь не соответствие, а три массива, сейчас попробую...
В то же время соединение awk по соответствию выглядит у меня так:
```1c
    Функция СравнениеТаблицЗначений_(Т1, Т2) Экспорт

    Ответ = Новый Соответствие;

    ХэшМап = Новый Соответствие;
    Для Каждого Строка Из Т2 Цикл
    ХэшМап[Строка[0]] = Строка
    КонецЦикла;

    Для Каждого Ведущая Из Т1 Цикл
    Ключ = Ведущая[0];
    Ведомая = ХэшМап[Ключ];
    Если Ведомая = Неопределено Тогда
    Ответ[Ключ] = -1
    Иначе
    Для ё = 1 По Т1.Колонки.Количество() - 1 Цикл
    Если Ведущая[ё] <> Ведомая[ё] Тогда
    Ответ[Ключ] = 0;
    Прервать
    КонецЕсли
    КонецЦикла;
    ХэшМап.Удалить(Ключ)
    КонецЕсли
    КонецЦикла;

    Для Каждого Элемент Из ХэшМап Цикл
    Ответ[Элемент.Ключ] = 1
    КонецЦикла;

    Результат = Новый ТаблицаЗначений; Результат.Колонки.Добавить("Ключ"); Результат.Колонки.Добавить("Значение");

    Для Каждого Элемент Из Ответ Цикл
    ЗаполнитьЗначенияСвойств(Результат.Добавить(), Элемент)
    КонецЦикла;

    Результат.Колонки[1].Имя = "Знак";

    Возврат Результат

    КонецФункции
```
Основной цикл здесь - это
```1c
    Для Каждого Ведущая Из Т1 Цикл
    Ключ = Ведущая[0];
    Ведомая = ХэшМап[Ключ];
    Если Ведомая = Неопределено Тогда
    Ответ[Ключ] = -1
    Иначе
    Для ё = 1 По Т1.Колонки.Количество() - 1 Цикл
    Если Ведущая[ё] <> Ведомая[ё] Тогда
    Ответ[Ключ] = 0;
    Прервать
    КонецЕсли
    КонецЦикла;
    ХэшМап.Удалить(Ключ)
    КонецЕсли
    КонецЦикла;

    Для Каждого Элемент Из ХэшМап Цикл
    Ответ[Элемент.Ключ] = 1
    КонецЦикла;
```
На вгляд, операций, выполняемых в цикле явно меньше. При этом число повторений цикла примерно равное. Это и объясняет худшие результаты слияния. Вот картина результатов на текущий момент. Для ее получения все циклы во всех вариантах я записывал в одну строку. Также я посмотрел профайлером затраты на перекладку результатов из соответствия в таблицу значений. Они не столь значительны, чтобы повлиять на результат.
Слиянию может помочь только сокращение числа операций в основном цикле (хвосты можно не смотреть). Не знаю, как этого добиться.
Также для простоты я беру все колонки, а первая (нулевая) - ключ. Чем определять номера нужных колонок и затем каждый раз их из массива выбирать, легче скопировать таблицу с нужным составом колонок на другое место.
---
ildarovich, двойная свертка при приведении к Соответствии резко падает (нелинейно растет время выполнения). Код:

```1c
    Функция СравнитьТаблицы(ТЗ1,ТЗ2,Колонки, ИмяПервойКолонки) Экспорт
    Результат = Новый Структура("Одинаковые, Т1, Т2, Измененные", Новый Соответствие, Новый Массив, Новый Массив, Новый Соответствие);
    ТЗ=ТЗ1.Скопировать();
    ТЗ.Колонки.Добавить("РезультатСравнения");
    ТЗ.ЗаполнитьЗначения(-1,"РезультатСравнения");
    Для Каждого СтрокаТЗ2 из ТЗ2 Цикл
    СтрокаТЗ=ТЗ.Добавить();
    ЗаполнитьЗначенияСвойств(СтрокаТЗ,СтрокаТЗ2);
    СтрокаТЗ.РезультатСравнения=1;
    КонецЦикла;
    ПереченьКолонок=";
    Для каждого Колонка из Колонки Цикл
    ПереченьКолонок=ПереченьКолонок+","+Колонка;
    КонецЦикла;
    ПереченьКолонок = Сред(ПереченьКолонок,2);
    ТЗПК=ТЗ.Скопировать(,ИмяПервойКолонки+",РезультатСравнения");
    ТЗПК.Свернуть(ИмяПервойКолонки,"РезультатСравнения");
    ТЗ.Свернуть(ПереченьКолонок,"РезультатСравнения");
    Для каждого СтрокаТЗПК из ТЗПК Цикл
    Если СтрокаТЗПК.РезультатСравнения=0 Тогда
    Ключ = СтрокаТЗПК[ИмяПервойКолонки];
    ПолнаяСтрока = ТЗПК.Найти(Ключ, ИмяПервойКолонки);
    Если ПолнаяСтрока.РезультатСравнения=0 Тогда
    Результат.Одинаковые.Вставить(ПолнаяСтрока, ПолнаяСтрока);
    Иначе
    Результат.Измененные.Вставить(ПолнаяСтрока, ПолнаяСтрока);
    КонецЕсли;
    ИначеЕсли СтрокаТЗПК.РезультатСравнения > 0 Тогда
    Результат.Т2.Добавить(СтрокаТЗПК);
    Иначе
    Результат.Т1.Добавить(СтрокаТЗПК);
    КонецЕсли;
    КонецЦикла;
    Возврат Результат;
    КонецФункции
```
---
awk, я имел ввиду не эту двойную свертку, а вот эту
```1c
    Функция СравнениеТаблицЗначений(Т1, Т2, ПрочиеКолонки = ") Экспорт

    ИмяКлюча = Т1.Колонки[0].Имя;

    Для ё = 1 По Т1.Колонки.Количество() - 1 Цикл ПрочиеКолонки = ПрочиеКолонки + ", " + Т1.Колонки[ё].Имя
    КонецЦикла;

    Т2.Колонки.Добавить("Знак");
    Т2.ЗаполнитьЗначения(1, "Знак");

    ТЗ = Т1.Скопировать();
    ТЗ.Колонки.Добавить("Знак");
    ТЗ.ЗаполнитьЗначения(-1, "Знак");
    ТЗ.Колонки.Добавить("Счет");

    Для ё = 1 По Т2.Количество() Цикл ТЗ.Вставить(0) КонецЦикла;

    Для ё = 0 По Т2.Колонки.Количество() - 1 Цикл ТЗ.ЗагрузитьКолонку(Т2.ВыгрузитьКолонку(ё), ё) КонецЦикла;

    Т2.Колонки.Удалить("Знак");

    ТЗ.ЗаполнитьЗначения(1, "Счет");

    ТЗ.Свернуть(ИмяКлюча + ПрочиеКолонки, "Знак,Счет");

    Ответ = ТЗ.Скопировать(Новый Структура("Счет", 1), ИмяКлюча + ",Знак");

    Ответ.Свернуть(ИмяКлюча, "Знак");

    Ответ.Колонки[0].Имя = "Ключ";

    Возврат Ответ

    КонецФункции
```
---
awk, а в том коде проблема всего лишь вот в этом оператореПолнаяСтрока = ТЗПК.Найти(Ключ, ИмяПервойКолонки);так как у ТЗПК нет индекса по этой колонке.
Кстати, возможно, при формировании теста (в том куске, что я выкладывал) тоже не хватало индекса по ключевой колонке. И была проблема в том, что очень долго формировались тесты из-за проверки повторяемости значения ключа.
Я этот кусок переделал - сейчас у меня тестовые таблицы быстро строятся.
---
awk, а вообще это более широкий вопрос. Его можно обсудить отдельно.
Соответствия Одинаковые, Измененные - это структуры, привязанные к сравниваемым таблицам, размещенным в памяти. Если таблицы переместить, скопировать, свернуть и т.п., то актуальность данных в этих структурах утрачивается. Они очень коротко-живущие, сиюминутные, промежуточные. Их нельзя ни сериализовать, ни вернуть из функции без самих таблиц, вообще никак без самих таблиц использовать.
Насколько они вообще нужны - это принципиальный вопрос.
Единственный способ их быстро получить после свертки - это еще раз выполнить соединение по ХэшМап (индексу). Никакого другого пути нет и быть не может. Так что требование возврата таких структур - это просто требование использования ХэшМап. Никакого соревнования, никаких вариантов. Очень жесткое требование.
Получается ни сравнение двух таблиц, а СВЯЗЫВАНИЕ строк экземпляров таблиц в оперативной памяти по ключевому полю.
То есть мы ответили на вопрос: как быстро связать ссылками две таблицы значений. Ответ - используйте соответствие. Это - единственно верный способ. Все остальные - потеря времени.
А как только мы отвяжемся от получения ничего не значащих ссылок, перейдем к содержательной стороне сравнения, поймем, а зачем нам на самом деле были нужны ссылки - для чего они нужны в конце-концов - тогда и появится неопределенность в выборе лучшего метода.
---
ildarovich, Я выбрал такой результат как показательный в теле функции, а не как функциональный. Функцианал как мне кажется, полностью покрывающий практику ECUD (equil, create, update, delete):

1. "Равны" ли таблицы? Для срабатывания триггеров.
2. Что удалили? Для зачистки связанных данных.
3. Что изменили? Для перепроведения
4. Что добавили? Для генерации чего-либо.
5. Одинаковые элементы нужны для LRu кэша, но как то к 1С не применимо...
---
awk, еще раз говорю: вопрос тоньше!

Сначала исключим психологические моменты:
1) Я не воспринимаю двойную свертку как свой метод - этот метод общеизвестный и первый какой приходит в голову. Я всего лишь его компактно записал, чтобы не было никаких потерь (YanTSys не все сделал для этого).
2) Я очень люблю соответствие (ХэшМап) и всюду его применяю - я за него так же болею.
3) Использование соответствия вместо индекса в этой задаче - это ваша и очень ценная находка, достойная отдельной публикации. В другой ветке народ уже интересовался - я эту вашу находку уже упоминал. Особенно, если речь будет идти о составных ключах [K1][K2]...[KN]. Правда, пока мне непонятно как красиво обрабатывать отсутствие ключа в очередном измерении.

Теперь о нашем соревновании. Углубившись в задачу, я очень многое про нее понял, над чем первоначально не задумывался.
И, в частности, вот что:
Результат, включающий соответствия Одинаковые[УказательНаСтрокуВТаблице1] -> УказательНаСтрокуВТаблице2 и Измененные[УказательНаСтрокуВТаблице1] -> УказательНаСтрокуВТаблице2 пользователю не требуется. Это промежуточные данные. Которые получаются как побочный эффект по ходу работы вашего метода.
Если пользователь спрашивает: какие строки изменились, то ему нельзя отвечать: изменились строки, расположенные в оперативной памяти по адресу 0x5555 и по адресу 0x7777. Ему это будет непонятно и не нужно. Но если он спросит: а сколько полей в этой записи изменилось, какие поля изменились, на сколько изменились поля, то на все эти содержательные вопросы и многие другие можно будет ответить, получив и имея эти соответствия.
Но, если задача ставится именно так: разместить в памяти структуру для быстрого ответа на такие вопросы, то решение становится безальтернативным. Если это спрашивается, то нужно использовать ХэшМап и только ХэшМап. - Никакого соревнования нет и быть не может! (слияние тоже не перемещает строки в памяти, но тратит больше отдельных операций и при реализации на 1С, видимо, проигрывает).
Так что если хотите допустить других участников, то разрешайте ответы, не содержащие УказательНаСтрокуВТаблице1 и УказательНаСтрокуВТаблице2, а содержащие КлючСтрокиВТаблице1 и КлючСтрокиВТаблице2. Тогда и будет соревнование.
---
ildarovich, Сначала исключим психологические моменты:
1) Я не воспринимаю двойную свертку как свой метод Вызвало улыбку. Неужели кто-то считает общеизвестные алгоритмы своим изобретением?

Использование соответствия вместо индекса в этой задаче - это ваша :))) Не моя. Типовое решение в другой среде... В Яве HashMap - это очень популярная коллекция. При добавлении 5 строчек в которую превращается во вполне эффективный LRU кэш.

Еще раз: результат функции - не принципиален. Например я бы в рабочей среде немного изменил сигнатуру вызова сравнения, дабы передать "посетителя". А вставки заменил с:

```1c
    Результат.Одинаковые.Вставить(ПолнаяСтрока, ПолнаяСтрока);
```

на:

```1c
    Попытка Посетитель.Одинаковые(ПерваяСтрока, ВтораяСтрока, Отказ); Исключение КонецПопытки;
    Если Отказ Тогда Возврат Результат; КонецЕсли;
```
---
awk, ну, мне показалось, что спор сворачивает в сторону: чей метод лучше. Чей - не в смысле изобретен, а кто предложил. Не так - ну и хорошо.

Попыток - исключений в коде интуитивно сторонюсь. Для меня это что-то типа goto. Хотя, может и зря. Нужно на скорость проверить.

Запихивать в ответ по второму разу полные строки?

Ну а если результат функции не принципиален, почему всем не взять "общительную" коллекцию типа массива или той же таблицы значений. Вы же за это даже баллы накидывать собирались. У меня дурацкая тяга к симметрии и мне в этой задаче нравится результат в виде таблицы значений. Типа на вход подали ТЗ и на выходе ТЗ. Такая матричная арифметика. Только одна структура.

Ну или по крайней мере исключить указатели на строки. Тогда все будут на равных. В общем, мое единственное условие - исключить указатели, то есть тип данных СтрокаТаблицыЗначений.
---
ildarovich, Более красиво без попыток, но более сложно для клиента:

```1c
    Если Поддерживает(Посетитель, "Одинаковые", 3) Тогда Посетитель.Одинаковые(ПерваяСтрока, ВтораяСтрока, Отказ); КонеЕсли;
```

То есть посетителю надо предусмотреть функцию:

```1c
    Функция МетодыМодуля() Экспорт

    Результат = Новый Структура();
    Результат.Вставить("Одинаковые", Новый Структура("КоличествоПараметров, ВозвращаетЗначение", 3, Ложь));
    Возврат Результат;

    КонецФункции;
```

Вот только получение МетодыМодуля() все равно в попытке делать :))...
---
awk, кажется, задача слишком простая, чтобы ООП сюда приворачивать, но это только мое мнение. Тут еще не исчерпана универсальность в виде задания состава ключей, в том числе КлючевыеКолонки = "". Что с этим делать? Меня больше интересует не интерфейс, а собственно способ, хотя связь есть - это понятно. Но, если сделать быструю универсальную функцию на базе этих изысканий - будет круто. Причем просто универсальна уже есть - на Мисте от fixin.
---
ildarovich, ООП тут не причем. Обычный callback.
---
awk, ну, и кстати, использование ХэшМап для поиска в ТЗ вместо индекса с замерами - напишите статью или заметку!
Актуальность вот из этого комментария следует http://forum.infostart.ru/forum24/topic119600/message1243153/#message1243153.
Я так понял в поиске через [К1][К2][КN] отсутствие данных по измерению попыткой-исключением обходите? Как с быстродействием - не снижается?
---
ildarovich, Зачем? Если К1[П1] = Неопределено, то К1[П1][П2] нет смысла проверять...
---
awk, но тогда потребуется некрасивая гребенка из Если ИначеЕсли вместо красивой, быстрой и компактной записи [][][]. Не съедят ли эти проверки весь выигрыш по сравнению с индексированием?
---
ildarovich,
```1c
    Функция СоздатьДерево(Данные, МассивКлючей)

    Дерево = Новый Соответствие;

    Для каждого ЭлементДанных Из Данные Цикл
    ДобавитьКлюч(Дерево, МассивКлючей, 0, ЭлементДанных);
    КонецЦикла;

    Возврат Дерево;

    КонецФункции

    Процедура ДобавитьКлюч(Дерево, МассивКлючей, Уровень, ЭлементДанных)

    Если Уровень = МассивКлючей.ВГранице() Тогда
    Дерево[ЭлементДанных[МассивКлючей[Уровень]]] = ЭлементДанных;
    ИначеЕсли Уровень < МассивКлючей.ВГранице() Тогда
    Если Дерево[ЭлементДанных[МассивКлючей[Уровень]]] = Неопределено Тогда
    Дерево[ЭлементДанных[МассивКлючей[Уровень]]] = Новый Соответствие;
    КонецЕсли;
    ДобавитьКлюч(Дерево, МассивКлючей, Уровень+1, ЭлементДанных);
    КонецЕсли;

    КонецПроцедуры

    Функция ПолучитьДанные(Дерево, МассивКлючей, Уровень=0)
    Если Дерево = Неопределено Тогда
    Возврат Неопределено;
    ИначеЕсли Уровень = МассивКлючей.ВГранице() Тогда
    Возврат Дерево[МассивКлючей[Уровень]];
    Иначе
    Возврат ПолучитьДанные(Дерево[МассивКлючей[Уровень]], МассивКлючей, Уровень + 1);
    КонецЕсли;
    КонецФункции
```

Не думаю что будут сильные задержки. Хотя построить индекс по идее должен выйграть.
---
awk, ну, при таком расточительном способе еще кучу вариантов можно предложить.
Я бы тут предпочел при том, что сказал ранее

```1c
    Попытка
    X = ХэшМап[К1][К2][К3][К4][К5]
    Исключение
    Х = Неопределено
    КонецПопытки
```
---
ildarovich, Советую замерить быстродействие. Исключение генерирует достаточно много данных. Это может "убить" всю прелесть использования. То есть при "попадании" выигрышный в скорости, а при "стрельбе" мимо цели проигрыш.
---
awk, да я это в порядке размышлений - не моя тема.
---
ildarovich, Через попытку в пять раз медленней получается...
---
awk, Аналогичная ситуация и с отладчиком в 7.7. Код который выполняется в подключенной отладке медленнее чем обычное предприятие.
---
ildarovich, Замерил 999 раз на данных:

```1c
    Дерево = Новый Соответствие;
    Дерево["К1"] = Новый Соответствие;
    Дерево["К1"]["Л1"] = 10;
    Дерево["К1"]["Л2"] = 10;
    Дерево["К2"] = Новый Соответствие;
    Дерево["К2"]["Л1"] = 10;
    Дерево["К2"]["Л2"] = 10;
    Массив = Новый Массив();
    Массив.Добавить(Новый Массив());
    Массив[0].Добавить("К1");
    Массив[0].Добавить("Л1");
    Массив.Добавить(Новый Массив());
    Массив[1].Добавить("Л1");
    Массив[1].Добавить("К1");
```

Ни разу попытка не обогнала если...
---
Скорее всего съест. Но есть варианты, которые кстати применяются для уменьшения конкуренции на запись.

```1c
    Процедура ДобавитьКлючиВТз(СоответствиеКлючей,СтрТз)
    ТзКлючей=СоответствиеКлючей[СтрТз[ПервоеПоле]];

    Если ТзКлючей=неопределено Тогда
    //Создадим тз с нужными колонками и одной строкой
    ТзКлючей=ТзКлючейДляЗаполнения.СкопироватьКолонки();
    ТзКлючей.Добавить(КолонкиДляИндекса)
    КонецЕсли;
    СтрКлючей=ТзКлючей.Добавить();
    ЗаполнитьЗначенияСвойств(СтрКлючей,СтрТз)
    КонецПроцедуры

    Функция НайтиКлючи(ПервоеПоле,СтрТз,НайденнаяСтрока)

    ТзКлючей=СоответствиеКлючей[СтрТз[ПервоеПоле]];

    Если ТзКлючей=неопределено Тогда

    возврат ложь

    КонецЕсли;
    Структура=Новый Структура(КолонкиДляИндекса);
    ЗаполнитьЗначенияСвойств(Структура,СтрТз)

    Строки=ТзКлючей.НайтиСтроки(Структура);
    Если Строки.Количество()=0 Тогда
    возврат ложь
    КонецЕсли;

    НайденнаяСтрока=Строки[0];
    возврат истина

    КонецФункции // НайтиКлючи
    // ()
```

КолонкиДляИндекса,ПервоеПоле,ТзКлючейДляЗаполнения можно сделать переменными модуля, что бы не таскать их через параметры
---
Serginio, это полумеры. Как я понял, решение [][][][][] - это быстрое бескомпромиссное соответствие-соответствия-соответствия. Многоуровневое дерево, на каждом уровне которого ветвление по одному ключу.
---
Ты посчитай затраты на составления дерева, [][][][][] при генерации исключения в попытке, как правильно заметил awk.
В 1С все нужно проверять, а потом утверждать.
 Да и индексированная таблица с удалением по идее не должна сильно сливать Соответствию. А даже быть быстрее, за счет того, что не нужно программно строить Соответсвие.
---
Serginio, Понятие сливает есть понятие относительное, зависит от условий. Прикрепил тест скорости поиска по ТЗ различными способами. В обсуждаемой обработке как раз используется соответствие и метод найти ТЗ. Сорри, если неверно понял предмет спора и влез с оффтопом.
---
Ну вот в (391) ildarovich уже выложил вылизанные алгоритмы. И по сути они мало отстают друг от друга.
Было интересно, как именно оптимально эти алгоритмы использовать учитывая интерпретатор 1С например в (350)
А так алгоритмы известны давным давно смотри (339)
---
ildarovich, Там видать просто пытались использовать соответствие не для тех целей. (токо сейчас заметил ссылку). По статье надо подумать...
---
ildarovich, Правда, пока мне непонятно как красиво обрабатывать отсутствие ключа в очередном измерении.
Можно добавлять какой-либо редко используемый символ к представлению каждого измерения.
[k1][k2][][][k5] - z1;z2;;;z5;
---
Silenser, - эх! - не получится - слишком много памяти съест - ведь продолжать придется все возможные ветки.
---
ildarovich, Можно проверить. Результаты в 393 показывают, что все не так уж и плохо, при увеличении длины ключа.
---
в 341 как раз вылизано все, что только возможно. При этом от первоначального варианта скорость увеличена в 2 раза.
---
Serginio, я уже ответил awk, но здесь отвечу короче. Чтобы вылизать (341) и выжать все, что возможно, нужно использовать ХэшМап, то есть повторить большую часть алгоритма соединения по ХэшМап. То есть требовать соответствий СТРОК (указателей, по своей сути) таблиц значений от свертки - это заставлять стартовать его после того, как прибежит ХэшМап. Может он тогда когда-нибудь выиграть?
Потому что свертка работает с содержимым таблиц, а не с хранящими это содержимое структурами.
---
Ты возьми и сравни (341), а не говори голословно. У меня он на некоторых наборах даже обходит Хэш. Но его тоже можно оптимизировать. Сам по себе алгоритм слияния для полного соединения самый быстрый, но в условиях 1С выполняется больше операторов интерпретатора.

Так увеличение дает код

```1c
    Для каждого стр из Тз Цикл
```

перед

```1c
    Индекс=Индекс+1;
    Стр=Тз[Индекс];
```

```1c
    Значение=Стр[ИндексКолонки]
```

перед

```1c
    Значение=Стр[ИмяПоля]
```

```1c
    Одинаковые.Вставить
```

перед

```1c
    Результат.Одинаковые.Вставить
```

 Кроме того по алгоритму слияния одно и тоже значение может сравниваться несколько раз

Поэтому сравнивать лучше

```1c
    Сравнение.Сравнить(Значение1,Значение2)
```

перед

```1c
    Сравнение.Сравнить(СтрТ1[Поле],СтрТ2[Поле])
```
---
Serginio, я бы с удовольствием сравнил и эту реализацию, но не могу в точности воссоздать условия, так как в моей тестовой обертке вывод идет в таблицу значений из двух колонок: ключ - знак. Я пошел другим путем: аккуратно и старательно переписал ваш метод слияния на этот случай. Считаете, что я где-то ошибся?
Сейчас попробую запустить в точности ваш код без приведения результата к одному виду (ОК = Нет).
Я вообще все циклы после отладки записываю в одну строку - это стандартный прием оптимизации, исключающий к тому же влияние разного стиля написания.
---
Подправил, но не проверял

```1c
    Функция ПолучитьИдексПоля(Тз,имяПоля)

    Колонки=Тз.Колонки;
    Колонка=Колонки.Найти(ИмяПоля);

    Если Колонка=неопределено Тогда
    тз.ВыбратьСтроку();

    ВызватьИсключение "Не найдена колонка "+ИмяПоля;
    КонецЕсли;

    возврат Колонки.Индекс(Колонка);
    КонецФункции

    Функция СравнитьТаблицы(Т1,Т2,Колонки, Ключ) Экспорт

    Ответ=Новый Соответствие;
    ИндексКлючаТ1=ПолучитьИдексПоля(Т1,Ключ);
    ИндексКлючаТ2=ПолучитьИдексПоля(Т2,Ключ);

    Индекс2=0;
    КолСтрВТз2=Т2.Количество();
    Сравнение = Новый СравнениеЗначений;

    Если Индекс2 <> КолСтрВТз2 Тогда
    СтрТ2=Т2[Индекс2];
    Знач2=СтрТ2[ИндексКлючаТ2];
    КонецЕсли;

    Для каждого СтрТ1 Из т1 Цикл

    Если Индекс2 = КолСтрВТз2 Тогда
    Ответ[СтрТ1[ИндексКлючаТ1]]=-1;
    Продолжить;
    КонецЕсли;
    Знач1=СтрТ1[ИндексКлючаТ1];

    Пока истина Цикл
    РезультатСравнения = Сравнение.Сравнить(Знач1,Знач2);
    Если РезультатСравнения < 0 Тогда
    Ответ[Знач1]=-1;

    прервать
    ИначеЕсли РезультатСравнения > 0 Тогда
    Ответ[Знач2]=1;
    Индекс2=Индекс2+1;
    Если Индекс2 <> КолСтрВТз2 Тогда
    СтрТ2=Т2[Индекс2];
    Знач2=СтрТ2[ИндексКлючаТ2];
    Иначе
    прервать
    КонецЕсли;

    Иначе

    КолонкиИдентичны=истина;
    сч2=0;
    Для каждого Колонка из Колонки Цикл
    если СтрТ1[Колонка]<>СтрТ2[Колонка] Тогда
    Ответ[Знач1]=0;
    прервать
    КонецЕсли;

    КонецЦикла;

    Индекс2=Индекс2+1;
    Если Индекс2 <> КолСтрВТз2 Тогда
    СтрТ2=Т2[Индекс2];
    Знач2=СтрТ2[ИндексКлючаТ2];
    КонецЕсли;

    прервать
    КонецЕсли;
    КонецЦикла;
    КонецЦикла;

    Пока Индекс2 <> КолСтрВТз2 Цикл
    СтрТ2=Т2[Индекс2];
    Ответ[СтрТ2[ИндексКлючаТ2]]=1;
    Индекс2=Индекс2+1;
    КонецЦикла;

    Возврат Ответ;
    КонецФункции
```
---
Serginio, - ДА!!!!

Проверил ваш код, потом посмотрел, где теряю и переписал свой. Оказалось - можно выиграть на Строка1 = Таблица1[Индекс1]; Ключ1 = Строка1[0] вместо Ключ1 = Таблица1[Индекс1][0]. Причем я
1) эту подсказку видел, но, каюсь, не думал, что будет так существенно быстрее;
2) у меня так было сначала и написано, а я решил проверить гипотезу сокращения количества операторов и забыл вернуть как было.
Теперь результаты поменялись:
Слияние 1 - это код из (361). Результаты работы везде совпадают (ОК = Да). Правда на 50000 Слияние опять проигрывает, но
---
Прикрепи обработку. Наверняка и соответствие и индексированную таблицу можно ускорить
---
Serginio, дерзайте, у меня тут еще есть идея как зависимость двух индексов учесть в слиянии. У меня так сделано в Минимализмах - думаю, еще несколько процентов можно будет со слияния сбросить. Единственно, у меня в этом варианте на массивы переписано, и мне код перекладки не нравится. Я решил вернуться к ответу-соответствию по ХэшМапу и слиянию как уже выкладывал в комметариях. Но сейчас нет времени самому возвращать тот вариант - сделаю это позже.
---
1. Сначала сравнить количество строк в обоих таблицах.
2. Сравниваем первую и последнюю строку.
3. Так как количество строк и первая, последняя строки совпадают, то может быть только "сдвижка" (какого-то значения нет, но позже появляется лишнее значение, в итоге к концу Тз выходим на тоже значение) по первой колонке и изменения значений по остальным.
4 Дальше итерационно пробегаем подобно методу "деление отрезка пополам": сравниваем серединную строку, следом у каждого полученного куска опять сравниваем серединную строку и т.д. В конце концов мы попадем на участок, где есть сдвижка и там значения первой колонки будут отличаться, либо будут отличаться значения остальных колонок.

По сути так работает индекс. Хотя вызов функции- достаточно накладная операция...
---
в (3) написан ПРАВИЛЬНЫЙ ответ (разве что рекурсию заменить циклами). Только избыточное изобретательство не позволяет это понять.
---
IvanAlekseev, понять это мешает отсутствие кода. Сами попробуете запрограммировать эту идею - тогда и скажете "ой". Я дихотомию в похожей задаче использовал - посмотрите мою статью "Компаратор оборотов баз" - там есть готовая обработка. Уверенно могу сказать, что тут такое не прокатит.
---
Вопрос разбивается на две части:

1. Поиск оптимального ИНСТРУМЕНТА. Другими словами, некоторые думают так: 1с косячно реализовала функционал, и мы пытаемся найти тот объект, в котором для данной задачи косяки минимально проявились. Думаю при таком ощущении мира более грамотно необходимый функционал реализовать на С, и скорость повысится ~1000 раз (сам делал замеры на реальной задаче).

2. Поиск оптимального АЛГОРИТМА. Так вот, если мы ищем именно алгоритм, то в (3) представлен самый быстрый алгоритм.

Код писать лень, поскольку он очевиден: заводим МассивОтложенныхЗадач, в котором последовательной парой храним первый и последний индекс строк сравниваемых Тз (предварительно проверяем равенство первой и последней строки и добавляем в МассивОтложенныхЗадач 0 и КоличествоСтрок-1). В цикле без условия (Пока Истина Цикл) с помощью переменной хранящей последний индекс МассиваОтложенныхЗадач получаем текущий "кусок", сравниваем центральную строку текущего "куска", удаляем только что исследованный "кусок" и добавляем два других (на которые разбивается отрезок делением пополам, если первый и последний индекс не отличаются на 1). Как только индекс отложенных задач дойдет до -2 - прерываем цикл (отложенные задачи исчерпались).

Накладные расходы- определение центрального индекса (полусумма крайних индексов "куска") и добавление/удаление индексов в МассивОтложенныхЗадач. За эти расходы мы получаем благо- можем прервать обработку, так как пользуемся упорядочиванием Тз по первой колонке. Ничего быстрее с точки зрения АЛГОРИТМА быть не может.
---
IvanAlekseev,
1. Индекс так не работает.
2. Это вариант Nested Loop, не в самой быстрой реализации.
---
опишите критерии оптимальности
например так
http://www.forum.mista.ru/topic.php?id=506669%29
---
В запросе создаем две временные таблицы на основании запроса с номерами строк ТЗ. Далее поля таблицы индексируем, после этого два запроса делаем в котором выводим различные строки и все. Лучше по любому чем циклом перебирать.
---
tsmgeorg@gmail.com,
можно и построчно сравнить отсортированные тз
без лишних найти()

если тз больше 10000 строк и бд в файловом то не лучше. не дождетесь вообще результата работы такого запроса.
если толстый клиент + сервер 1с и сервер субд на физически разных компьютерах то тоже будет медленнее.
---
МихаилМ, А здесь и не будет выполнятся метод найти) Я имею сначала Таблица1 Лефт джойн Таблица2, и Таблица2 Лефт джойн Таблица 1 и выводить записи где Таблица2 и Таблица1 естьнулл, ну или выводить все и вывести признак (ЗАписьРавна = истина или ложь), а потом объединить их вот в результате и получим таблицу сравнения))) Естественно этот метод можно доработать, а не перебор циклом. А результат этого запроса или в ТЗ или обработка запроса вот и все.
---
количество строк думаю не более 100. Надо указать различия, т.е основной у нас является 1 колонка. Сначала надо сравнить есть ли различия в ней. Т.е выводим строки которых нет либо в первой ТЗ либо во Второй ТЗ.
(8) tsmgeorg@gmail.com, а что значит индексируем? я просто большой чайник в этом
---
Nadushka74, это значит индексы создавать.
индексы нужны для ускорения работы (поиск - например),... вобщем немалая это тема.
в запросе это на закладке "Индекс" (при типе запроса - создание временной таблице) - задаются поля для которых индексы создавать надо.
---
Nadushka74, переделайте функцию чтобы агрегировало различия, а не возвращало лишь результат, что различны.
---
Nadushka74, а не могли бы уточнить по поводу значений в первой колонке. Может ли такое быть, чтобы в одной таблице в этой колонке были бы в разных строках два одинаковых значения? То есть играет ли она роль "ключа" в этой таблице, то есть значение в ней однозначно определяет остальные данные строки. А то из Надо указать различия, т.е основной у нас является 1 колонка. Сначала надо сравнить есть ли различия в ней. То есть выводим строки которых нет либо в первой ТЗ либо во Второй ТЗ.можно подумать и так и так.

А от того, допустимы ли одинаковые значения в первой колонке, либо вообще - одинаковые строки в одной таблице, зависит выбор метода решения. Если одинаковых значений в первой колонке одной таблицы быть не может, то за основу нужно брать сортировку слиянием из комментария (14). Отсортированность по первой колонке поможет решить задачу сравнения быстрее. Если одинаковые значения встречаются, то отсортированность вообще не нужна и нужно взять за основу предложение из (4) и развивать в сторону универсальности решение (18).
---
https://ru.wikipedia.org/wiki/%C0%EB%E3%EE%F0%E8%F2%EC_%F1%EE%E5%E4%E8%ED%E5%ED%E­8%FF_%F1%EB%E8%FF%ED%E8%E5%EC_%F1%EE%F0%F2%E8%F0%EE%E2%E0%ED­%ED%FB%F5_%F1%EF%E8%F1%EA%EE%E2
---
Хочется увидеть нормальный и быстро работающий код, соответствующий условию задачи.
---
Вот вариант для 7 ки отсюда http://files.rsdn.ru/19608/C1InDelphi.zip
аналогично можно и для 8 ки

```1c
    //*******************************************
    Перем СлучайноеЧисло;
    Функция ПолучитьСлучайноеЧисло( МинЗначение, МаксЗначение )
    СлучайноеЧисло = 16807 * СлучайноеЧисло;
    СлучайноеЧисло = СлучайноеЧисло - Цел( СлучайноеЧисло / 2147483647 ) * 2147483647;
    Если ( ( МаксЗначение - МинЗначение + 1 ) > 1 ) Тогда
    Возврат МинЗначение + Цел( ( МаксЗначение - МинЗначение + 1 ) * СлучайноеЧисло / 2147483648 )
    Иначе Возврат ( СлучайноеЧисло / 2147483648 )
    КонецЕсли
    КонецФункции

    //=================================
    Функция ИзСтрокиСРазделителями(Стр,Разделитель)
    Сз=СоздатьОбъект("СписокЗначений");
    ТемпСтр=СокрЛП(Стр);
    поз=Найти(ТемпСтр,Разделитель);
    Пока Поз>0 Цикл
    Сз.ДобавитьЗначение(СокрЛП(Лев(ТемпСтр,Поз-1)));
    ТемпСтр=Сред(ТемпСтр,Поз+1);
    поз=Найти(ТемпСтр,Разделитель);
    КонецЦикла;
    Если ПустаяСтрока(ТемпСтр)=0 Тогда
    Сз.ДобавитьЗначение(СокрЛП(ТемпСтр));
    КонецЕсли;
    Возврат Сз
    КонецФункции

    Процедура ДобавитьРеквизиты(Тз,ТзТемп,Индекс,Резулт,СписокПолей)
    Для сч=1 По СписокПолей.РазмерСписка() Цикл
    Поле=СписокПолей.ПолучитьЗначение(сч);
    Значение=ТзТемп.ПолучитьЗначение(ТзТемп.НомерСтроки,Поле);
    Тз.УстановитьЗначение(индекс,Поле,Значение);

    КонецЦикла;
    Тз.ПолучитьСтрокуПоНомеру(Индекс);
    Тз.Резулт=Резулт;
    КонецПроцедуры

    Функция ТзРавны(Тз1,тз2,СписокПолей)
    Для сч=1 По СписокПолей.РазмерСписка() Цикл
    Поле=СписокПолей.ПолучитьЗначение(сч);
    Если не (Тз1.ПолучитьЗначение(Тз1.НомерСтроки,Поле)=Тз2.ПолучитьЗначение(Тз2.НомерСтроки,Поле)) Тогда
    Возврат 0;
    КонецЕсли;

    КонецЦикла;
    Возврат 1;
    КонецФункции
    Функция Сравнение(ТзДляСравнений,Тз1,Тз2,СписокПолей,СпзСписокПолей)

    Если ТзРавны(Тз1,тз2,СпзСписокПолей)=1 Тогда
    Возврат 0;
    КонецЕсли;
    ДобавитьРеквизиты(ТзДляСравнений,Тз1,1,-1,СпзСписокПолей);
    ДобавитьРеквизиты(ТзДляСравнений,Тз2,2,1,СпзСписокПолей);

    ТзДляСравнений.Сортировать(СписокПолей);
    ТзДляСравнений.ПолучитьСтрокуПоНомеру(1);
    Возврат ТзДляСравнений.Резулт;

    КонецФункции

    Процедура СравнитьТз(Тз1,Тз2,СписокПолей,ТзДляСравнений)

    СпзСписокПолей=ИзСтрокиСРазделителями(СписокПолей,",");
    Таб=СоздатьОбъект("таблица");
    //Таб.ИсходнаяТаблица("Таблица");
    Таб.ВывестиСекцию("Шапка");

    Тз1.Сортировать(СписокПолей);
    Тз2.Сортировать(СписокПолей);
    Тз2.ВыбратьСтроки();
    Тз1.ВыбратьСтроки();

    Фл1=Тз1.ПолучитьСтроку();
    Фл2=Тз2.ПолучитьСтроку();
    Пока (Фл1=1) И (Фл2=1) Цикл
    ФлСравнения=Сравнение(ТзДляСравнений,тз1,Тз2,СписокПолей,СпзСписокПолей);
    Если ФлСравнения=-1 Тогда
    // Выводим Тз1.СравниваемыйЭлемент нен В Тз2 нет такого элемента

    Таб.ВывестиСекцию("ЕстьТолькоВТз1");
    Фл1=Тз1.ПолучитьСтроку();
    // Получаем новую строку Тз1,

    ИначеЕсли ФлСравнения=1 Тогда
    // Выводим Тз2.СравниваемыйЭлемент нен В Тз1 нет такого элемента

    Таб.ВывестиСекцию("ЕстьТолькоВТз2");
    Фл2=Тз2.ПолучитьСтроку();
    иначе
    //Строки равны

    // ТзРазличий.НоваяСтрока();
    Таб.ВывестиСекцию("ТзРавны");

    Фл1=Тз1.ПолучитьСтроку();
    Фл2=Тз2.ПолучитьСтроку();
    КонецЕсли;

    КонецЦикла;

    //====== Проверяем остатки неравных строк

    Если (Фл1=0) и (Фл2=1) Тогда

    Пока Фл2=1 Цикл

    //Оставшиеся строки По тз2
    Таб.ВывестиСекцию("ЕстьТолькоВТз2");
    Фл2=Тз2.ПолучитьСтроку();

    КонецЦикла;
    //Выводим ОстаткиТз2

    ИначеЕсли (Фл1=1) и (Фл2=0) Тогда
    //Выводим ОстаткиТз1

    Пока фл1=1 Цикл
    //Оставшиеся строки По тз1
    Таб.ВывестиСекцию("ЕстьТолькоВТз1");

    Фл1=Тз1.ПолучитьСтроку();
    КонецЦикла;
    КонецЕсли;

    Таб.Показать();
    КонецПроцедуры

    Функция ПолучитьТзДляСравнения()
    Тз=СоздатьОбъект("ТаблицаЗначений");
    Тз.НоваяКолонка("Поле1","Число",10,0);
    Тз.НоваяКолонка("Поле2","Число",10,0);
    Тз.НоваяКолонка("Результат","Число",10,0);

    Для сч=1 По 100 Цикл
    Тз.НоваяСтрока();
    Тз.Поле1=ПолучитьСлучайноеЧисло( 1, 10 );
    Тз.Поле2=ПолучитьСлучайноеЧисло( 1, 10 );
    Тз.Результат=1;
    КонецЦикла;
    Тз.Свернуть("Поле1,Поле2","Результат");
    Возврат тз;
    КонецФункции // ТестДляГруппировкиДанных()

    Процедура Сформировать()
    ТзДляСравнений=СоздатьОбъект("ТаблицаЗначений");
    ТзДляСравнений.НоваяКолонка("Поле1","Число");
    ТзДляСравнений.НоваяКолонка("Поле2","Число");
    ТзДляСравнений.НоваяКолонка("Резулт","число",2,0);

    //Создадим Две Строки в Таблице сравнений для заполнения по номеру строки
    ТзДляСравнений.НоваяСтрока();
    ТзДляСравнений.НоваяСтрока();

    Тз1=ПолучитьТзДляСравнения();
    Тз2=ПолучитьТзДляСравнения();

    СравнитьТз(Тз1,Тз2,"Поле1,Поле2",ТзДляСравнений)
    КонецПроцедуры

    //*******************************************
```

СлучайноеЧисло=67865
---
Serginio, (18) YanTsys, (20) _censored, ни один вариант пока не является законченным решением для данной задачи.
(17) не на 8-ке и сортировка делается всех полей, а в задаче говорится о сортировке только по первому полю.
(18) очень не универсально (чтобы сравнить нужные таблицы имена полей нужно явно записать в код) и после свертки различия не выделены.
(20) не учтена особая роль первой колонки, имена колонок вшиты в запрос, таблицы значений не введены в запрос.

Вроде бы как (17) ближе всего к тому, что нужно, но это пока не законченное решение. И насколько оно оптимально?

(26) Кажется сомнительным такой способ проверки на полное совпадение. Будет показывать некоторые разные ТЗ как одинаковые. Лучше сериализовать (ЗначениеВСтрокуВнутр) и сериализации сравнивать.
Через Найти(Отбор) долго будет работать на большом количестве строк. То есть не оптимально. Ну и также не совсем для условий задачи.
---
ildarovich, ну вот а как по вашему обработка сама определит по каким колонкам автору нужно делать сравнение а по каким нет?
Если нужно сделать сравнение по всем колонкам, то разве тут у кого-то не хватит знаний чтобы загнать в строку имена колонок через запятую?
И что значит различия не выделены? Если ИсточникСтроки=-1 то строка с уникальными колонками есть только в первой таблице, если +1 то строка с этими уникальными колонками есть только во второй таблице, если ноль значит строка с такими значениями есть в обеих таблицах...
---
YanTsys, сама обработка не определит - тут спора нет.
То, что загнать имена колонок через запятую не хватит знаний - у кого-то может или знаний не хватит или желания полуфабрикат использовать. Решение должно быть универсальным и готовым к использованию.

Должны выводиться только отличия - сказано однозначно. Так что строки с нулем нужно убирать. А если могут быть одинаковые строки, то может быть и +2 и +3 и -2 и -3 и так далее, что будет означать, что во второй таблице таких строк на две больше, на три больше, на одну меньше и т.п.

А вот то, что основной у нас является 1 колонканатолкнуло меня на мысль о том, что в ответе должно быть три состояния: строка (идентифицированная по первой колонке) удалена, строка добавлена, строка изменена.
---
ildarovich, кстати если бы только +2 и -3 то ладно бы... очевидно может быть хуже -1+1-1 = -1 хотя на самом деле есть одинаковые строки процедура покажет что такая строка была только в первой таблице :(
Кстати где на форуме смайлики? Программирование оно ведь тоже вызывает эмоции...!!!
---
1. В одной из ТЗ поменять знак числовых полей на обратный.
2. Соединить ТЗ в одну.
3. В объединенной ТЗ выполнить операцию свернуть.
На выходе имеем ТЗ в которой будут суммы расхождения значений первой и второй исходной ТЗ

P.S. Знак числовых полей можно менять не в исходной второй ТЗ а в процессе объединения двух ТЗ
---
YanTsys, Если в 1 таблице есть строки Иваново 30, Иваново 20. А во второй Иваново 50 - то в в твоем случае будет косяк. Таблицы не равны, а мы будем думать что равны...
---
Мой вариант попроще :) да еще и позволяет некоторые поля не сравнивать а находить расхождение

```1c
    //ТЗ1,ТЗ2=> исходные ТаблицаЗначений;

    ТЗ3=ТЗ1.Скопировать();
    ТЗ3.Колонки.Добавить("ИсточникСтроки");
    ТЗ3.ЗаполнитьЗначения(-1,"ИсточникСтроки");
    Для Каждого СтрокаТЗ2 из ТЗ2 Цикл
    СтрокаТЗ3=ТЗ3.Добавить();
    СтрокаТЗ3.ИсточникСтроки=1;
    Для каждого Колонка из ТЗ2.Колонки Цикл
    Если Колонка.Имя="Сумма1" или Колонка.Имя="Сумма2" Тогда
    СтрокаТЗ3[Колонка.Имя]=-СтрокаТЗ2[Колонка.Имя];
    Иначе
    СтрокаТЗ3[Колонка.Имя]=СтрокаТЗ2[Колонка.Имя];
    КонецЕсли;
    КонецЦикла;
    КонецЦикла;
    ТЗ3.Свернуть("ИмяКолонки1,ИмяКолонки2,ИмяКолонки3,ИмяКолонки4","Сумма1,Сумма2,ИсточникСтроки");
```

В итоговой таблице если ИсточникСтроки=-1 то строка есть только в ТЗ1 если ИсточникСтроки=+1 то исходная строка есть только в ТЗ2 если ИсточникСтроки=0 то все поля кроме тех которые мы решили не сравнивать а находить сумму расхождения т.е. кроме Сумма1 и Сумма2 одинаковые а вот в полях Сумма1 и Сумма2 будет сумма отклонения этих полей
---
Такой вариант хорош только для числовых полей. Мой универсален и не зависит от типа поля. Единственно, что для семерки для ссылок при сортировке нужно добавлять *. Для 8и достаточко класса сравнение

```1c
    Функция ПолучитьСтрокуСортПоля(Колонка,Тз,ЕстьСпр,ЕстьДок)
    перем Тип,Длина,Точность;
    поле=Тз.ПолучитьПараметрыКолонки(Колонка,тип,Длина,Точность);

    Тип=Врег(Тип);

    Если Найти(Тип,"СПРАВОЧНИК")=1 Тогда
    ЕстьСпр=1;
    Резулт=Колонка+"*"
    ИначеЕсли Найти(Тип,"ДОКУМЕНТ")=1 Тогда
    ЕстьДок=1;
    Резулт=Колонка+"*"
    Иначе
    Резулт=Колонка;
    КонецЕсли;

    Возврат Резулт
    КонецФункции // ПолучитьСтрокуСортПоля
```
---
Serginio, и кстати автор отдельно оговорил что типы полей в ТЗ совпадают, следовательно разные типы сравнивать не придется в принципе... а значения одинакового типа функция свернуть в ТЗ и сама прекрасно сравнивает на равенство...
---
Наиболее оптимальный и самый быстрый по моему мнению в (18), т.к. все вычисления происходят в оперативной памяти, код простой, понятный и легко модифицируемый под конкретные нужды. Конечно можно наворотить и более универсальную функцию, но основная идея метода (18) не должна измениться
---
Мой вариант будет аналогично (18):

```1c
    ТЗ3=ТЗ1.Скопировать();
    ТЗ3.Колонки.Добавить("Отклонение1");
    ТЗ3.Колонки.Добавить("Отклонение2");
    ТЗ3.ЗагрузитьКолонку(ТЗ3.ВыгрузитьКолонку("Сумма1"),"Отклонение1");
    ТЗ3.ЗагрузитьКолонку(ТЗ3.ВыгрузитьКолонку("Сумма2"),"Отклонение2");

    Для Каждого СтрокаТЗ2 из ТЗ2 Цикл
    СтрокаТЗ3=ТЗ3.Добавить();
    Для каждого Колонка из ТЗ2.Колонки Цикл
    СтрокаТЗ3[Колонка.Имя]=-СтрокаТЗ2[Колонка.Имя];
    Если Колонка.Имя="Сумма1" Тогда
    СтрокаТЗ3.Отклонение1=-СтрокаТЗ2[Колонка.Имя];
    ИначеЕсли Колонка.Имя="Сумма2"
    СтрокаТЗ3.Отклонение2=СтрокаТЗ2[Колонка.Имя];
    КонецЕсли;
    КонецЦикла;
    КонецЦикла;
    ТЗ3.Свернуть("ИмяКолонки1,ИмяКолонки2,ИмяКолонки3,ИмяКолонки4","Сумма1,Сумма2,Отклонение1,Отклонение2");
```

   ну а далее обход, отклонение будет в Отклонение1,Отклонение2,
начальные значения в ТЗ, соответственно (Сумма1+Отклонение1)/2 и (Сумма1-Отклонение1)/2 ...аналогично, для второй колонки
---
Если таблицы отсортированы, то можно запросом:

```1c
    ВЫБРАТЬ
    ВложенныйЗапрос.НомерСтроки,
    ВложенныйЗапрос.Колонка1,
    ...
    ВложенныйЗапрос.КолонкаN,
    СУММА(ВложенныйЗапрос.Индикатор) КАК Индикатор
    ИЗ
    (ВЫБРАТЬ
    ТЗ1.НомерСтроки,
    ТЗ1.Колонка1,
    ...
    ТЗ1.КолонкаN,
    1 КАК Индикатор
    ИЗ
    ТЗ1 КАК ТЗ1

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

    ВЫБРАТЬ
    ТЗ2.НомерСтроки,
    ТЗ2.Колонка1,
    ...
    ТЗ2.КолонкаN,
    -1
    ИЗ
    ТЗ1 КАК ТЗ1) КАК ВложенныйЗапрос

    СГРУППИРОВАТЬ ПО
    ВложенныйЗапрос.НомерСтроки,
    ВложенныйЗапрос.Колонка1,
    ...
    ВложенныйЗапрос.КолонкаN

    ИМЕЮЩИЕ
    СУММА(ВложенныйЗапрос.Индикатор) <> 0
```

И потом:

Если Выборка.Следующий() Тогда
 //ТЗ различны
Иначе
 //ТЗ одинаковы
КонецЕсли
---
_censored, так не получится, автор просит не просто проверить равенство таблиц а найти различные строки, в вашем варианте все испортит НомерСтроки если отличается НомерСтроки то они у вас уже будут считаться различными даже если все остальное совпадет...
---
Алгоритм подразумевает сортировку и сравнение нужных полей.
(23) Нужно сравнивать не только числа, но и строки, ссылки итд.
Я дал алгоритм который используется в БД, а именно Merge Join. Плох он или хорош решать тому, кто будет выбирать между алгоритмами. Например http://www.forum.mista.ru/topic.php?id=715574&page=1
---
Я немного не понял, мой вариант позволяет числовые поля проверить на сумму расхождения, либо если числовые поля не выделять в алгоритме он их как и остальные просто проверит на равенство...

Например пишем вот так:

```1c
    //ТЗ1,ТЗ2=> исходные ТаблицаЗначений;

    ТЗ3=ТЗ1.Скопировать();
    ТЗ3.Колонки.Добавить("ИсточникСтроки");
    ТЗ3.ЗаполнитьЗначения(-1,"ИсточникСтроки");
    Для Каждого СтрокаТЗ2 из ТЗ2 Цикл
    СтрокаТЗ3=ТЗ3.Добавить();
    СтрокаТЗ3.ИсточникСтроки=1;
    Для каждого Колонка из ТЗ2.Колонки Цикл
    Если Колонка.Имя="Сумма2" Тогда
    СтрокаТЗ3[Колонка.Имя]=-СтрокаТЗ2[Колонка.Имя];
    Иначе
    СтрокаТЗ3[Колонка.Имя]=СтрокаТЗ2[Колонка.Имя];
    КонецЕсли;
    КонецЦикла;
    КонецЦикла;
    ТЗ3.Свернуть("Сумма1,ИмяКолонки1,ИмяКолонки2,ИмяКолонки3,ИмяКолонки4","Сумма2,ИсточникСтроки");
```

И в результате строки в которых Сумма1 отличается будут выводится как разные строки.

А что ваш вариант еще делает кроме сравнения на равенство с нечисловыми полями?
---
18 Очень часто мне приходилось сравнивать разные базы по номенклатуре по наименованию.
Такой подход помогал находить одинаковые или близкие строки, что помогало их идентифицировать как одинаковые и прописывать одинаковый идентификатор для синхронизации между данными.
Ну и посмотри в 14 где есть ссылка на Merge Join. Вспоминаем сортировку слиянием.
---
Для ТЗ с идентичными колонками использую простой вариант:

```1c
    Процедура Сравнение(ТЗ1,ТЗ2)

    ИмяОбщейКолонки = "ОбщСтрока";
    ИмяОбщейСуммы = "ОбщСумма";
    ИмяКоличестваСимволов = "ОбщКолСимволов";
    СтрСортровки = "ОбщКолСимволов,ОбщСумма,ОбщСтрока";

    ЕстьТЗ1 = ИнициализацияТЗ(ТЗ1, ИмяОбщейКолонки, ИмяОбщейСуммы, ИмяКоличестваСимволов, СтрСортровки);
    ЕстьТЗ2 = ИнициализацияТЗ(ТЗ2, ИмяОбщейКолонки, ИмяОбщейСуммы, ИмяКоличестваСимволов, СтрСортровки);

    Если (ТЗ1.Итог(ИмяОбщейСуммы)=ТЗ2.Итог(ИмяОбщейСуммы))И(ТЗ1.Количество()=ТЗ2.Количество())
    И(ТЗ1.Итог(ИмяКоличествасимволов)=ТЗ2.Итог(ИмяКоличествасимволов)) Тогда // полное совпадение
    Сообщить("Таблицы идентичны");
    Иначе
    Если ТЗ1.Количество()>ТЗ2.Количество() Тогда
    СравнениеКолонокТЗ(ТЗ1,ТЗ2,ИмяКоличестваСимволов,ИмяОбщейСуммы,ИмяОбщейКолонки);
    Иначе
    СравнениеКолонокТЗ(ТЗ2,ТЗ1,ИмяКоличестваСимволов,ИмяОбщейСуммы,ИмяОбщейКолонки);
    КонецЕсли;
    КонецЕсли;

    КонецПроцедуры

    Процедура СравнениеКолонокТЗ(ТЗИсточник,ТЗПриемник,ИмяКол1,ИмяКол2,ИмяКол3)
    // можно вместо ИмяКол1,ИмяКол2,ИмяКол3,.... передавать массив колонок

    Для каждого Стр Из ТЗИсточник Цикл
    Отбор = Новый Структура("+ИмяКол1+","+ИмяКол2+","+ИмяКол3,Стр[ИмяКол1],Стр[ИмяКол2],Стр[ИмяКол3]);
    мОтбор = ТЗПриемник.НайтиСтроки(Отбор);
    Если мОтбор.ВГраница()=-1 Тогда
    Сообщить("Стр."+Стр.НомерСтроки+" отсутствует в "+ТЗПриемник);
    Иначе
    Для каждого мСтр Из мОтбор Цикл
    // примитивный вариант:
    Если Стр[ИмяКол1]<>мСтр[ИмяКол1] Тогда
    Сообщить("Стр."+Стр.НомерСтроки+"не равна строке "+мСтр.НомерСтроки+" : "+ИмяКол1);
    // ... если нужно - проходимся по нужным колонкам и сравниваем
    КонецЕсли;
    // ..... и т.д.
    КонецЦикла;
    Сообщить("Таблицы идентичны");
    КонецЕсли;
    КонецЦикла;

    КонецПроцедуры

    Функция ИнициализацияТЗ(ТЗ, ИмяОбщейКолонки, ИмяОбщейСуммы, ИмяКоличестваСимволов, СтрСортировки=Неопределено)

    ТипЧисло = Тип("Число");
    ТЗ.Колонки.Добавить(ИмяОбщейКолонки,Тип("Строка"));
    ТЗ.Колонки.Добавить(ИмяОбщейСуммы,ТипЧисло);
    ТЗ.Колонки.Добавить(ИмяКоличестваСимволов,ТипЧисло);
    ТЗ.Колонки.Добавить("НомерСтроки",ТипЧисло);
    Если НЕ(СтрСортировки=Неопределено) Тогда
    ТЗ.Сортировать(СтрСортировки);
    КонецЕсли;

    СчСтр = 1;
    Для каждого Стр Из ТЗ Цикл
    Стр.НомерСтроки = СчСтр;
    СчСтр = СчСтр +1;

    ЗначениеОбщейКолонки = "; ЗначениеОбщейСуммы = 0; ЗначениеОбщегоКоличестваСимволов = 0;
    Для каждого Кол Из ТЗ.Колонки Цикл
    Если Кол.Имя<>ИмяОбщейКолонки Тогда
    Если Кол.ТипЗначения = ТипЧисло Тогда
    ЗначениеОбщейСуммы = ЗначениеОбщейСуммы + Стр[Кол.Имя];
    Иначе
    ЗначениеОбщейКолонки = ЗначениеОбщейКолонки + " + Стр[Кол.Имя];
    ЗначениеОбщегоКоличестваСимволов = ЗначениеОбщегоКоличестваСимволов + СтрДлина(Стр[Кол.Имя]);
    КонецЕсли;
    КонецЕсли;
    КонецЦикла;
    Стр[ИмяОбщейКолонки] = ЗначениеОбщейКолонки;
    Стр[ИмяОбщейСуммы] = ЗначениеОбщейСуммы;
    Стр[ИмяКоличестваСимволов] = ЗначениеОбщегоКоличестваСимволов;
    КонецЦикла;
    ТЗ.Сортировать(ИмяОбщейКолонки+","+ИмяКоличестваСимволов+","+ИмяОбщейСуммы); // возможно и не стоит

    Возврат Истина;
    // Можем при необходимости возвращать массивы имен колонок, участвующих в "ЗначениеОбщ..." и их итоговые суммы
    // для ускорения последующих действий с данными
    КонецФункции
```
---
Нужно найти расхождения или просто определить что они различаются?
---
artemka, до 10 поста не осилил прочитать?
---
Замечу. В ветке предложено много интересных алгоритмов.
Вариантов сравнения ТЗ может быть очень много. Зависит от конкретных условий и целей. За универсальность платим скоростью исполнения. Зачастую достаточно не доводить до сравнения двух ТЗ, а решить задачу еще до получения этих таблиц.
---
МимохожийОднако, философии тут и так хватает, а мне конкретика (быстрая универсальная функция сравнения двух отсортированных по первой колонке и одинаковых по структуре колонок таблиц значений с выводом различий) нужна, однако...
---
самый примитив - доп колонка , её заполнение и свёртка, это ежели не запросом делать
Одно только не ясно - свёрнуты ли до того как исходные тз
---
Тсрпё, Свертка не всегда возможна - не все типы можно сравнить при свертке. Она только по примитивным типам работает.
А так- да, я регулярно использую именно свертку когда нужно сравнить количество товаров в накладной и количество отгруженных со склада товаров...
---
А если вот так ?
 * картинки неудачно вставились , вобщем идея в том чтобы после объединения таблиц получить числовое поле которое можно выразить в виде ax+by=c где a и b - это коэфициенты соответствующих таблиц , c - результат суммирования при сворачивании таблицы , x и y соответственно количество одинаковых строк в 1 и 2 таблице
---
подзадачи для алгоритма сравнения сворачиванием

- более быстрый алгоритм объединения (UNION) двух (и более) тз( тк можно сравнивать таким образом много тз).
- для сравнения с учетом позиции строки - быстрая нумерация для последующей свертки.
---
+ 35

более быстрый , чем добавление строк с заполнением.
---
Раз таблицы отсортированы по первой колонке - это уже ключ к разгадке этой тайны :-).
Но вообще надо отсортировать последовательно по всем колонкам. Если надо - запомнить с помощью колонки НомерСтроки начальный порядок строк.
А дальше:
1) МаксСтрока = Макс (КолвоСтрок1, КолвоСтрок2)
2) Текстрока1 = 1; ТекСтрока2 = 1
3) Пока ТекСтрока1<МаксСтрока и ТекСтрока2 < МаксСтрока Цикл
4) Сравниваем значения в первой строке
Если ТЗ[ТекСтрока1] = ТЗ[ТекСтрока2] Тогда ищем расхождения по этой строке
Если ТЗ[ТекСтрока1] > ТЗ[ТекСтрока2] Тогда ТекСтрока2++
Если ТЗ[ТекСтрока2] > ТЗ[ТекСтрока1] Тогда ТекСтрока1++

Таким образом мы найдем все различающиеся строки.

А вообще надо знать смысл таблиц, тогда можно с помощью функции свертки найти разницу.
---
adamx, Вот пример со сверткой:
1) Делаем доп. колонку "Доп"
2) Заполняем её для первой таблицы значением 1;
3) Заполняем её для второй таблицы значением -1;
4) Дописываем вторую таблицу в конец первой.
5) Сворачиваем первую таблицу по всем колонкам, кроме последней. Последнюю суммируем.
6) Там где в последней колонке сумма получилась 0 - значит эти строки совпадают.
В итоге имеем массив совпадающих строк, но их местоположение надо искать.
---
ну, по-уму, суммировать нужно не только доп колонку, но и все числовые (скопировав их в доп колонки и инвертировать значения в них), тогда можно будет точно узнать , что изменилось.