Есть независимый непериодический регистр сведений, в который нужно записать данные из таблицы значений, с определенной логикой в том случае, если есть совпадение по реквизиту.

Вопрос в том, можно ли придумать что-то получше, чем поштучный запрос записей из БД?

Поясню на примере.

Допустим, есть измерение И, ресурс Р и реквизит В. Таблица значений содержит только И и Р.

Если И в таблице и в регистре отличаются, или же это новая запись - нужно обновить В.

Тогда запись выглядит примерно так:

```1c
    СтрокаРегистра = РегистрыСведений.<ИмяРегистра>.СоздатьМенеджерЗаписи();
    Для каждого СтрокаТЗ из ТЗ Цикл
    СтрокаРегистра.И = СтрокаТЗ.И;
    СтрокаРегистра.Прочитать();
    Если СтрокаРегистра.Р <> СтрокаТЗ.Р Тогда
    СтрокаРегистра.Р = СтрокаТЗ.Р;
    СтрокаРегистра.В = ТекущаяДата();
    СтрокаРегистра.Записать();
    КонецЕсли;
    КонецЦикла;
```

Нормально так делать? Или есть варианты получше?

Еще есть такая задача: записям регистра, которые отсутствуют в таблице, обнулить И и выставить В. Как это сделать, я понимаю еще меньше. Не приходит на ум ничего, кроме поштучного перебора записей из регистра с поиском в таблицей.

Средствами классических СУБД обе задачи решаются с помощью UPDATE + JOIN. Но в 1С так нельзя, потому что SQL-запросы возможны только для чтения, а писать в базу они не могут.

Вот ищу что-то похожее. Чтоб не по одной штуке.

Подскажете?
---
Можно избавиться от поштучного Прочитать():
```1c
    Запрос = Новый Запрос(
    "ВЫБРАТЬ
    | ТЗ.Измерение1,
    | ТЗ.Ресурс1
    |ПОМЕСТИТЬ ВТ
    |ИЗ
    | &ТЗ КАК ТЗ
    |;
    |
    |////////////////////////////////////////////////////////////­////////////////////
    |ВЫБРАТЬ
    | ВТ.Измерение1,
    | ВТ.Ресурс1
    |ИЗ
    | ВТ КАК ВТ
    | ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИмяРегистра КАК ИмяРегистра
    | ПО ВТ.Измерение1 = ИмяРегистра.Измерение1 И ВТ.Ресурс1 = ИмяРегистра.Ресурс1
    |ГДЕ
    | ИмяРегистра.Измерение1 ЕСТЬ NULL");
    Запрос.УстановитьПараметр("ТЗ", ТЗ);
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
    Запись = РегистрыСведений.ИмяРегистра.СоздатьМенеджерЗаписи();
    Запись.Измерение1 = Выборка.Измерение1;
    Запись.Ресурс1 = Выборка.Ресурс1;
    Запись.Реквизит1 = ТекущаяДата();
    Запись.Записать();
    КонецЦикла;
```
---
Подскажете? Легко!
Подсказка: "Да, только поштучный перебор".
Ибо всего одно измерение.
---
придумать что-то получше
а каковы критерии? "Лучше" не абсолютное понятие. Быстрее выполняется? Быстрее программируется? Более понятно вашему преемнику? Менее понятно вашему преемнику? Или это конкурсное задание и результат должен больше понравиться проверяющему?
Вообще 1с вещь чисто прикладная, поэтому если результат получен правильный за приемлемое время, то он хороший и лучше уже никому не нужно...
---
Похоже, я не совсем ясно выразился.

"Лучше" в данном случае - это так: если мне нужно получить много строк, я их получаю за один запрос, а потом поштучно разбираю, вместо того, чтобы каждую строку получать отдельным запросом.
---
Можно избавиться от поштучного Прочитать():
```1c
    Запрос = Новый Запрос(
    "ВЫБРАТЬ
    | ТЗ.Измерение1,
    | ТЗ.Ресурс1
    |ПОМЕСТИТЬ ВТ
    |ИЗ
    | &ТЗ КАК ТЗ
    |;
    |
    |////////////////////////////////////////////////////////////­////////////////////
    |ВЫБРАТЬ
    | ВТ.Измерение1,
    | ВТ.Ресурс1
    |ИЗ
    | ВТ КАК ВТ
    | ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ИмяРегистра КАК ИмяРегистра
    | ПО ВТ.Измерение1 = ИмяРегистра.Измерение1 И ВТ.Ресурс1 = ИмяРегистра.Ресурс1
    |ГДЕ
    | ИмяРегистра.Измерение1 ЕСТЬ NULL");
    Запрос.УстановитьПараметр("ТЗ", ТЗ);
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
    Запись = РегистрыСведений.ИмяРегистра.СоздатьМенеджерЗаписи();
    Запись.Измерение1 = Выборка.Измерение1;
    Запись.Ресурс1 = Выборка.Ресурс1;
    Запись.Реквизит1 = ТекущаяДата();
    Запись.Записать();
    КонецЦикла;
```
---
Можно избавитьсяШел 2025 год, а 1Сники так и не хотели знать про ответственное чтение. Стабильность.
---
Можно избавиться от поштучного Прочитать():
Спасибо, попробую.

Скажите, а правильно ли я понимаю, что в 1С нельзя одной стандартной командой обновить значение ячейки у существующей записи регистра сведений?

Вместо этого всегда нужно:
* получить всю существующую строку (запись) целиком
* в полученные данные внести исправления
* записать исправленные данные, причем они перетрут существующие

Т.е. у 1С для регистров нет UPDATE (обновить), есть только SELECT (выбрать) + исправить + REPLACE (записать поверх).
---
Скажите, а правильно ли я понимаю, что в 1С нельзя одной стандартной командой обновить значение ячейки у существующей записи регистра сведений? Правильно. Потому что в Регистре нет ячеек в принципе.
---
Т.е. у 1С для регистров нет UPDATEУ 1С для регистров много чего есть
---
1. Заголовок по ссылке:
"Новые режимы записи ПОДЧИНЕННЫХ регистров сведений и регистров накопления"

В головном сообщении здесь:
"Есть НЕЗАВИСИМЫЙ непериодический регистр сведений"

2. Не есть, а будет неизвестно когда:
 Планируется в версии 8.3.26
---
Не есть, а будет неизвестно когда:Уже есть. Где-то видосик даже у 1С на ВК выкладен с демкой. Там чел с сурьозным видом такой важный весь вещает, как там у него миллионстотыщ записев во мгновение оков хренак - и перезаписалось. Реально.
---
будет неизвестно когдаО, это прелестное, спасительное, защитное незнание...

Версия 8.3.26.1498 вышла 28.11.24
---
Версия 8.3.26.1498 вышла 28.11.24Ага, мы на нее переехали в конце мартов.
---
А нельзя ли избежать явного перечисления колонок таблицы значений при загрузке во временную таблицу, если нужно отправить её туда целиком?

Попробовал звездочкой:

```1c
    ВЫБРАТЬ
    | ТЗ.*
    |ПОМЕСТИТЬ ВТ
    |ИЗ
    | &ТЗ КАК ТЗ
    |;
```

Не получилось:

...: Ошибка при вызове метода контекста (Выполнить)
 Результат = Запрос.Выполнить();
по причине:
{(6, 5)}: Тип не может быть выбран в запросе
<<?>>&ТЗ КАК ТЗ
---
Нельзя. Поля любых таблиц должны быть описаны заранее.
---
С регистром вышло. Регистр - не таблица?
---
Таблица. Полностью описанная.
---
А таблица значений - не полностью описанная? Там все колонки явно перечисляются при создании.
---
А компилятор запроса в курсе про то описание, которое содержится в твоей оперативной памяти?
Ведь в данном случае твоя ТЗ для запроса - просто подставляемый параметр, не более. Это не источник данных.
---
Описание содержится в коде. Он не в оперативной памяти, а в модуле объекта конфигурации.
---
Описание содержится в коде. А если не содержится?
а в модуле объекта конфигурации. А SQL сервер будет разбираться в твоем модуле объекта?

UPD. Понимаешь ли, мясорубка работает с мясом. Она не работает с твоими представлениями о мясе в твоей голове, она не работает с твоим холодильником или с мясником и коровой. Ей необходимо мясо.
---
А если не содержится?

Если явно описана в коде, значит, содержится.

 А SQL сервер будет разбираться в твоем модуле объекта?

Ему необязательно это делать, потому что можно передать ему известную структуру таблицы.
---
можно передать ему известную структуру таблицы
```1c
    Таблица = Новый ТаблицаЗначений;
    Таблица.Колонки.Добавить("Колонка1", Новый ОписаниеТипов("Массив"));
```
Хай, сервер СУБД! Ты глянь чо у меня есть!
---
Я тоже так думал https://forum.infostart.ru/forum9/topic259901/#message2631239 но 1С это умеет.
---
Нельзя. Поля любых таблиц должны быть описаны заранее.
Выяснилось, что на самом деле можно, просто для этого все поля таблицы значений должны быть типизированы.

А ты написал чушь и потом еще принялся умничать, пытаясь ее обосновать.
---
А ты написал чушь и потом еще принялся умничать, пытаясь ее обосновать.К чему пришли-то в итоге? А то с 24-й вроде платформы появился апдейт независимых регистров. А с 26-й - и зависимых тоже.
---
К чему пришли-то в итоге?
Тут был спор не по основному вопросу, а насчет того, можно ли при передаче таблицы значений в SQL-запрос вместо списка колонок указать звездочку. Оказалось, что можно, если таблица полностью типизирована.

А то с 24-й вроде платформы появился апдейт независимых регистров. А с 26-й - и зависимых тоже

В 24-й метод Записать() принимает логическое значение - либо перетирать, либо игнорировать.

Про 26-ю тут привели ссылку, вроде как появился режим обновления:

 В методы
РегистрСведенийНаборЗаписей.<Имя регистра сведений>.Записать(<Замещение>)
и
РегистрНакопленийНаборЗаписей.<Имя регистра сведений>.Записать(<Замещение>)
в качестве параметра Замещение может передаваться одно из значений перечисления РежимЗамещения.

В перечисление РежимЗамещения к существующим элементам (Добавление, Замещение, Слияние, Удаление) добавляется новый элемент Обновление(Update). В отличие от режима Слияние в режиме Обновление производится только обновление существующих записей регистра, совпадающих по значениям ключевых полей с записями набора записей. Добавление новых записей в регистр не производится, что существенно ускоряет процедуру записи.

Я так и не понял, речь идет только про зависимые регистры, как сказано в заголовке статьи, или про любые. Вероятно, второе, но наверняка сказать не могу.
---
Я так и не понял, речь идет только про зависимые регистры, как сказано в заголовке статьи, или про любые.У тебя там нет что-ли файла update? Вот секция для 8.3.25.
Для набора записей независимого регистра сведений реализованы дополнительные режимы записи:
Слияние - в этом случае в информационной базе происходит обновление записей, совпадающих по ключевым полям с записываемым набором записей, а остальные записи набора просто добавляются в регистр.
Удаление - в этом случае происходит удаление из информационной базы записей, совпадающих по ключевым полям с записями, присутствующими в наборе записей.
Реализовано системное перечисление РежимЗамещения, описывающий возможные варианты записи набора записей регистра сведений. Для метода РегистрСведенийНаборЗаписей.Записать() реализована возможность указывать значение типа системного перечисления РежимЗамещения в качестве значения параметра Замещать (наряду со значением типа Булево).

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

Источник: file:///C:/Program%20Files/1cv8/8.3.26.1540/docs/ru/V8Update.htm#700b8265-2bb9-11ee-963f-0050569f678a
---
У тебя там нет что-ли файла update? Вот секция для 8.3.25.

У меня на работе пока не обновились даже до 25-й, это не от меня зависит.

За текст спасибо, теперь понятно, что в моем случае лучше всего подходит вариант Слияние. Это типа как в обычной СУБД INSERT ON DUPLICATE KEY UPDATE.

Непонятно только, как люди в 1С жили без этого столько времени :-o
---
Непонятно только, как люди в 1С жили без этого столько времени :-oАга, я 10 лет назад статью писал даже на эту тему:
https://infostart.ru/1c/articles/319883/
---
Ого, вот это мощно!
Но на эту статью я могу только полюбоваться (как и многие). Чтобы такое делать самому, нужен большущий опыт работы с 1С.

Интересно, кстати, где у 1С проходит грань, за которой начинаются официально запрещенные операции с базой, если у неё все же есть пресловутый метод ПолучитьСтруктуруХраненияБазыДанных().

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

Как бы ответ.
---
Можно. Также, чтобы таблицу значений можно было передать в запрос как параметр, ее колонки должны быть типизированны.
Ваша ошибка, скорее всего, из-за того, что в запрос передаете таблицу значений с формы (ДанныеФормыКоллекция) - ее нужно превратить в "настоящую" таблицу значений. Используйте е метод Выгрузить() или глобальный метод РеквизитФормыВЗначение()
---
Важен такой момент: нужно выбирать из регистра полные данные, чтобы не надо было потом второй раз за ними ходить с помощью .Прочитать(). Правда, учитывая, что пишут ниже, с версии 8.3.25 необходимым становится вытащить из базы только измерения, а не все данные.

В общем, получился вот такой код. Оставлю тут, может, пригодится потом.

```1c
    // Получаем из регистра сведений данные, кроме указанных.

    УказанныеДаннные = ...; // Таблица значений
    Запрос = Новый Запрос();
    Запрос.УстановитьПараметр("ТаблицаЗначений", УказанныеДанные);
    Запрос.Текст =
    "ВЫБРАТЬ *
    |ПОМЕСТИТЬ ВременнаяТаблица
    |ИЗ &ТаблицаЗначений КАК Д // Дальше не используется, нужно для синтаксиса
    |;
    |
    |////////////////////////////////////////////////////////////­////////////////////
    |ВЫБРАТЬ
    | Р.* // С 8.3.25 тут необходимо выбирать только измерения,
    | // а все остальные данные - по необходимости
    |ИЗ
    | РегистрСведений.ТакойТо КАК Р
    | ЛЕВОЕ СОЕДИНЕНИЕ ВременнаяТаблица КАК ВТ
    | ПО ВТ.Измерение = Р.Измерение
    |ГДЕ
    | ВТ.КакаяНибудьЕщеКолонка ЕСТЬ NULL
    | // Тут можно писать и другие условия
    |";

    Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц; // без этого ошибка

    Результат = Запрос.Выполнить();
    ДанныеКромеУказанных = Результат.Выгрузить();

    // Теперь данные можно исправлять и прямо так, без вызова .Прочитать(), записывать в регистр
    Для каждого СтрокаДанных из ДанныеКромеУказанных Цикл

    СтрокаРегистра = РегистрыСведений.ТакойТо.СоздатьМенеджерЗаписи();
    ЗаполнитьЗначенияСвойств(СтрокаРегистра, СтрокаДанных);

    // Тут делаем с данными, что нужно.

    // СтрокаРегистра.Прочитать() вызывать не нужно, благодаря * в запросе

    СтрокаРегистра.Записать();

    // С 8.3.25 можно использовать СтрокаРегистра.Записать(РежимыЗаписи.Слияние),
    // если нужен аналог INSERT ON DUPLICATE KEY UPDATE
    КонецЦикла;
```
---
Да, дело было именно в типизации.

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

Добавил тип, и теперь звездочка работает:

```1c
    ВЫБРАТЬ *
    |ПОМЕСТИТЬ ВТ
    |ИЗ &ТаблицаЗначений КАК Д // Дальше не используется, нужно для синтаксиса
    ;
```

 SlavaKron, спасибо, что помогли!

Текст ошибки "Тип не может быть выбран в запросе", хоть и содержит упоминание типов, звучит туманно. Не помешала бы конкретика в виде конкретных полей, у которых тип не установлен. Иначе без подсказок шансы разобраться практически нулевые.