Полезные мелочи при работе с карточками ч.5 (Пересохраним записи справочника)

3 4

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

С чего у нас начинается работа? С определения справочника, который надо пересохранить и новый реквизит в нем (хотя, если вы поставили его на автозаполнение при сохранении, даже это можно опустить). Итак, здесь нет ничего сложного:

1. Создаем новый Сценарий:

Не важно, как мы его назовем, главное, чтобы потом было несложно его найти. На моем примере рассматривается справочник Вида Электронных Документов, куда был добавлен новый параметр "TabStr", который превращает в строку внесенные в таблицу записи.

2. Занесем в Текст Код:

Ref = CreateReference('ВЭД')   // Указываем нужный нам справочник
  // Создать предтставление
  View = Ref.CreateView('Главное')  // или иное, если вам это требуется
  // Окрыть справочник на выбор
  View.ViewMode =  vmSelect 
  // Разрешить множественный выбор
  View.MultiSelection = TRUE
  View.MainForm.Show
  // Если выбрали записи, то получить ИД выбранных значений
  if View.MainForm.Result = mrOK
    RecCount = View.SelectedRecordCount 
    Index = 0
    
    Progress = CreateProgress(''; RecCount)
    Progress.Show

    while Index < RecCount 
      ЗаписьВЭД = References.ВЭД.GetObjectByID(View.SelectedRecordsID(Index))
      
      ЗаписьВЭД.SYSREQ_NAME = ЗаписьВЭД.SYSREQ_NAME  // Это главное - переприсвоения имени достаточно чтобы пересохранить запись
      
// ниже идет блок работы с моей переменной "TabStr", то, ради чего я это вообще делаю
      Perem = ''
      foreach Str in ЗаписьВЭД.DetailDataSet(2)
        Perem = AddSubString(Str.Requisites('ТипЭлДокТ2').DisplayText; Perem; '; ')
      endforeach
  
      if Assigned(Perem)
        ЗаписьВЭД.Requisites("TabStr").Value = Perem
      endif
// Выше идет блок работы с моей переменной "TabStr", то, ради чего я это вообще делаю

      ЗаписьВЭД.Save
      ЗаписьВЭД = nil
      
      Index = Index + 1  
      Progress.Next 
    endwhile          
    Progress.Hide
  endif

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

Ref = CreateReference('ВЭД')       // Указываем справочник
  View = Ref.CreateView('Главное')
  View.ViewMode =  vmSelect 
  View.MultiSelection = TRUE
  View.MainForm.Show
  if View.MainForm.Result = mrOK
    RecCount = View.SelectedRecordCount 
    Index = 0
    Progress = CreateProgress(''; RecCount)
    Progress.Show
    while Index < RecCount 
      ЗаписьВЭД = References.ВЭД.GetObjectByID(View.SelectedRecordsID(Index))   
      ЗаписьВЭД.SYSREQ_NAME = ЗаписьВЭД.SYSREQ_NAME 
      ЗаписьВЭД.Save
      ЗаписьВЭД = nil
      Index = Index + 1  
      Progress.Next 
    endwhile          
    Progress.Hide
  endif

Как мы видим - ничего сложного. Теперь проверим работу скрипта:

Выделяем нужные нам записи или вообще все и жмем "Выбрать"

Пара секунды на выбранные элементы справочника и все готово - выбранные записи были пересохранены.

P.S.: Конечно, если вам надо пересохранить несколько десятков тысяч таких записей, время на это будет потрачено куда больше, так же как и пересохранение записей, в которых очень много вычислений после сохранения. В таких случаях лучше создавать скрипт с использованием SQL-сценария, но это уже другая статья... 

У меня все. Всем удачного дня!

Михаил Тарасов

Бывает.... так бывает... иногда... что пересохранять надо сотни тысяч записей одного справочника...

Бывает... иногда... правда, правда... что сохранение 1 записи длится несколько секунд... В рамках 1ой записи - это нормально. Пользователь этих секунд не заметит. Но вот если запускать прогрузку по этому справочнику, то можно вешаться...

Поэтому, предложенный метод конечно хороший. Но, всего лишь один из...

Помимо него, можно сделать тупо SQL Update. Если алгоритм простой и это можно не сильно напрягаясь уложить в SQL вычисление.

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

Ну и да, просьба придерживаться устоявшейся терминологии. Не параметр, а реквизит. Параметры - они в ТМ и в МД. А те штуки, которые сохраняются в базу каждая в свою колонку - это реквизиты. 

Ах, да... Ещё момент. Если ваш реквизит всегда вычисляемый (а такие бывают), то ок, можно заполнять его во время сохранения (хотя во время изменения реквизитов, от которых зависит заполняемый логичнее). Но если этот реквизит создан для пользователя, то оставлять этот код в событии сохранения - ошибка...

Михаил Тарасов: обновлено 06.11.2018 в 12:23
Андрей Дозоров

Еще немного в копилку бывает... иногда... Бывает иногда нужно обновить\заполнить десятки млн записей (а то и больше), и даже самый быстрый вариант с тупым SQL update работает непозволительно долго (не успевает за ночь отработать), вызывает блокировку всей таблицы (этот умный SQL сам решает что для update такого количества записей ему почему то не хватает блокировки страниц, ему нужна вся таблица), что равноценно недоступности системы (пользователь ничего сохранить не сможет), а журнал транзакций начинает стремится к размеру БД а то и больше и не хочет автоматически урезаться.

В этот момент происходит коллапс, пользователи кричат (работать же надо как то), админы кричат (почему DIRECTUM съел все дисковое пространство), а разработчик бежит отменять этот update. Вот только этот update не хочет просто так отменяться, оказывается откат транзакции дело очень не быстрое и не понятно что было бы лучше, подождать пока запрос доработает, или ждать пока выполнится откат транзакции и снова все повторять...

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

Как лечили - тупой SQL update сделали более умным, сделали курсор с update нескольких тысяч записей внутри.

Тарас Асачёв

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

Александр Чугунов

А я просто оставлю здесь ссылку на статью Массовое изменение реквизитов записей справочников
И лишние события при пересохранении таки лучше отключать, быстрее работать будет. 

Авторизуйтесь, чтобы написать комментарий