Практикум 8. Оптимізація процедури проведення
Тепер, коли ви вже досить добре знайомі з мовою запитів, ми нарешті можемо перейти до однієї з найважливіших тем –“ до оптимізації документа <strong>ОказаниеУслуги</strong>, і, зокрема, до повної зміни його обробника події <strong>ОбработкаПроведения</strong>.Оптимізація процедури проведення документа ОказаниеУслуги. Особливості використання посилальних даних. Оптимізація документа ОказаниеУслуги
Оптимізація процедури проведення документа ОказаниеУслуги
Особливості використання посилальних даних
Оптимізація документа ОказаниеУслуги
Оптимізація процедури проведення документа ОказаниеУслуги
Тепер, коли ви вже досить добре знайомі з мовою запитів, ми нарешті можемо перейти до однієї з найважливіших тем → до оптимізації документа ОказаниеУслуги, і, зокрема, до повної зміни його обробника події ОбработкаПроведения.
Навіщо це потрібно?
По-перше, припустімо, що керівництво фірми вирішило нарешті завершити «експерименти» з ручного введення вартості витрачуваних матеріалів і перейти на автоматичний розрахунок вартості витрачуваних матеріалів «за середнім».
По-друге, в обробнику події ОбработкаПроведения ми використовуємо звернення до реквізиту ВидНоменклатуры довідника Номенклатура «через крапку». Таке звернення може суттєво уповільнити швидкість виконання процедури за великих обсягів табличної частини документа.
Тому зміни, які ми вносимо до документа ОказаниеУслуги, матимуть дві цілі:
- визначення вартості витрачуваних матеріалів під час проведення документа;
- підвищення швидкості виконання процедури.
Особливості використання посилальних даних
Терміном посилальні дані ми позначатимемо дані, що зберігаються в базі даних, доступ до яких можливий за допомогою об'єктів вбудованої мови виду Ссылка:СправочникСсылка.<имя>, ДокументСсылка. <имя> тощо.
Щоб подальший виклад був зрозумілішим, побудуємо пояснення на прикладі отримання посилання на вид номенклатури під час проведення документа ОказаниеУслуги.
Не всі дані, що зберігаються в базі даних, є посилальними. Це пов'язано з тим, що в моделі даних 1С:Підприємство 8.1 існує поділ на дані, що представляють об'єктні сутності (довідники, плани рахунків, документи тощо), і дані, що представляють необ'єктні сутності (регістри відомостей, регістри накопичення тощо).
З погляду системи деяка сукупність об'єктних даних визначається не лише значеннями своїх полів, а й самим фактом свого існування. Іншими словами, видаливши з бази деяку сукупність об'єктних даних, ми не зможемо повернути систему в той самий стан, який був до видалення. Навіть якщо ми знову створимо ту саму сукупність об'єктних даних із тими самими значеннями полів, з погляду системи це буде ІНША сукупність об'єктних даних. Кожну таку сукупність об'єктних даних, унікальну з погляду системи, називають об'єктом бази даних. Щоб система могла відрізняти один об'єкт бази даних від іншого, кожен об'єкт бази даних (сукупність об'єктних даних) має внутрішній ідентифікатор. Різні об'єкти бази даних завжди матимуть різні внутрішні ідентифікатори. Цей ідентифікатор зберігається разом з іншими даними об'єкта у спеціальному полі Ссылка.
Необ'єктні дані зберігаються у вигляді записів і з погляду системи визначаються виключно значеннями своїх полів. Таким чином, видаливши деякий запис і записавши після цього новий, із точно такими самими значеннями всіх полів, ми отримаємо той самий стан бази даних, який був до видалення.
Таким чином, оскільки ми можемо однозначно вказати на кожен об'єкт бази даних, у нас з'являється можливість зберігати такий покажчик у полях інших таблиць бази даних, вибирати його в полі введення, указувати в параметрах запиту під час пошуку за посиланням тощо. У всіх цих випадках якраз і використовуватиметься об'єкт вбудованої мови виду Ссылка. Фактично цей об'єкт зберігає лише внутрішній ідентифікатор, що міститься в полі Ссылка.
Наприклад, якщо взяти наш документ ОказаниеУслуги, то в полі, що зберігає реквізит табличної частини Номенклатура, насправді міститься внутрішній ідентифікатор, що вказує на елемент довідника Номенклатура.

Коли в обробнику події ОбработкаПроведения документа ОказаниеУслуги ми присвоюємо значення реквізиту Номенклатура табличної частини будь-якій змінній, ми маємо справу з об'єктом ДокументОбъект.ОказаниеУслуги. Цей об'єкт містить у собі значення всіх реквізитів документа та реквізитів його табличних частин. Тому звернення
![]()
призводить до того, що ми просто читаємо дані, що зберігаються в оперативній пам'яті:

Проте, коли ми звертаємося до виду номенклатури як до реквізиту того елемента довідника, посилання на який зазначено в табличній частині документа
![]()
відбувається таке:

Оскільки в об'єкті ДокументОбъект.ОказаниеУслуги є лише посилання на елемент довідника Номенклатура і більше жодних даних про цей елемент немає, система візьме це посилання та звернеться за ним до кешу об'єктів у надії знайти там дані того об'єкта, посилання на який вона має. Якщо кеш об'єктів не матиме потрібних даних, він звернеться до бази даних, щоб прочитати всі дані об'єкта, посиланням на який він володіє. Після того як усі дані, що зберігаються в реквізитах потрібного елемента довідника та в реквізитах його табличних частин, буде прочитано до кешу об'єктів, кеш об'єктів поверне запитуване посилання, що зберігається в реквізиті ВидНоменклатуры довідника Номенклатура.
Як неважко здогадатися, подібне звернення до бази даних потребує більше часу, ніж просте читання з оперативної пам'яті. Під час інтерактивного заповнення документа такі затримки мізерні порівняно зі швидкістю роботи користувача. Однак під час виконання великої кількості розрахунків (наприклад, під час проведення великих документів, що містять кілька тисяч рядків) різниця в часі може бути досить відчутною.
З усього сказаного вище можна зробити такий висновок: якщо алгоритм проведення документа використовує лише ті дані, які присутні в реквізитах документа (і його табличних частин), цілком достатньо використовувати конструктор рухів документа (як це було в нас у випадку з документом ПриходнаяНакладная). Якщо ж в алгоритмі проведення потрібно аналізувати додаткові реквізити об'єктів, посилання на які містяться в документі, а також використовувати результати розрахунку підсумків регістрів, слід використовувати запити для швидшої вибірки даних із бази даних.
Те саме справедливо щодо виконання будь-яких ділянок програми, критичних щодо продуктивності. Механізм запитів краще «читає» інформаційну базу та може за один раз вибрати всі необхідні дані, тому, наприклад, у типових рішеннях ви практично не побачите використання об'єкта СправочникВыборка.<имя>.
Оптимізація документа ОказаниеУслуги
1. Перше, що ми зробимо для оптимізації документа ОказаниеУслуги, — видалимо реквізит табличної частини Стоимость, який нам надалі не знадобиться.
2. Також слід видалити відповідну колонку з табличного поля, розташованого у формі.
3. Після цього можна повністю видалити вміст обробника події ОбработкаПроведения у модулі документа та створити в ньому заготовку процедури проведення. Текст запиту, що виконується в режимі оперативного проведення, відрізнятиметься від запиту, що виконується за неоперативного проведення. Тому формування тексту запиту ми включимо в умову Если …¦ Иначе …¦ КонецЕсли:

4. Спочатку складемо запит, який виконуватиметься під час оперативного проведення документа. Установимо курсор перед крапкою з комою, з контекстного меню викличемо конструктор запиту,

на запитання «Створити новий запит?» погодимося, розкриємо таблицю ПереченьНоменклатуры табличної частини документа ОказаниеУслуги і виберемо з неї поля:
- Номенклатура;
- Количество;
- Номенклатура.ВидНоменклатуры;
- Сумма.
Ці поля знадобляться нам для задання значень вимірів регістрів та їхніх ресурсів. Крім того, поле ВидНоменклатуры знадобиться нам для аналізу того, чим є номенклатура, зазначена в документі: матеріалом чи послугою.
5. Для зазначення значень ресурсу Стоимость регістрів СтоимостьМатериалов і Продажи нам знадобиться розрахувати поточну вартість номенклатури як частку вартості залишку цього матеріалу та його залишкової кількості.
Тому додамо до списку вибраних таблиць ще дві:
- РегистНакопления.СтоимостьМатериалов.Остатки;
- РегистНакопления.ОстаткиМатериалов.Остатки.

6. Для цих віртуальних таблиць нам потрібно буде задати однакові параметри. Вони включатимуть момент часу, на який мають бути отримані залишки цих регістрів, і умову отримання даних.
Умова отримання даних указує, що залишки мають бути отримані лише за тими позиціями номенклатури, які містяться в документі, що проводиться (перед виконанням запиту ми передамо в параметр СписокНоменклатурыДокумента список усіх позицій номенклатури, що містяться в документі, який проводиться).

7. Після того як буде задано параметри обох віртуальних таблиць регістрів накопичення, виберемо з них поля СтоимостьОстаток і КоличествоОстаток:

8. Тепер згадаємо про те, що документи ОказаниеУслуги можуть бути проведені як в оперативному, так і в неоперативному режимі.
Оскільки в оперативному режимі нам знадобиться контролювати залишки списуваної номенклатури на складі, виберемо ще раз віртуальну таблицю регістра накопичення ОстаткиМатериаловОстатки і перейменуємо її на ОстаткиМатериаловОстаткиНаСкладе.

9. Для цієї віртуальної таблиці ми також зазначимо параметр МоментВремени, а в умові напишемо, що матеріал має бути в списку номенклатури, а склад має дорівнювати складу, зазначеному в документі:

10. Тепер із цієї віртуальної таблиці ми виберемо поле КоличествоОстаток:

11. На цьому формування списку вибраних полів завершено, і ми можемо перейти до задання умов зв'язку між вибраними таблицями.
12. Кожну з віртуальних таблиць необхідно пов'язати з таблицею документа таким чином, щоб для всіх записів таблиці документа добиралися наявні записи віртуальної таблиці, причому номенклатура в таблиці документа має дорівнювати матеріалу з віртуальної таблиці:

Вибрані таблиці

Умови зв'язку між таблицями
13. Тепер перейдемо на вкладку Додатково і встановимо прапорець Для зміни. Речення ДЛЯ ЗМІНИ дає змогу завчасно заблокувати читання зазначених даних (які можуть читатися транзакцією іншого з'єднання) вже під час зчитування, щоб виключити взаємні блокування під час запису. Це речення дає можливість зазначити в запиті ті таблиці, зчитувані дані яких передбачається змінити.
14. Оскільки ми плануємо виконати запис регістрів накопичення ОстаткиМатериалов, зазначимо таблиці цих регістрів як таблиці для зміни:

15. Перейдемо на вкладку Умови і задамо умови відбору з таблиці документа лише рядків документа, що проводиться (посилання на нього буде передано в параметр запиту Ссылка):

16. Перейдемо на вкладку Псевдоніми та задамо такі псевдоніми полів:
- НоменклатураВидНоменклатуры → ВидНоменклатуры;
- КоличествоОстаток1 → КоличествоНаСкладе.
17. Натиснемо ОК і подивимося, який текст запиту сформував конструктор:

У цьому запиті немає нічого складного, за винятком, можливо, трьох лівих з'єднань із таблицею табличної частини документа та використання ключового речення ДЛЯ ЗМІНИ, значення якого вже було пояснено.
18. Текст запиту для випадку неоперативного проведення документа буде практично таким самим, за винятком того, що в ньому буде відсутнє третє ліве з'єднання і, відповідно, поле КоличествоОстатокНаСкладе, оскільки перевірку залишків у цьому випадку ми виконувати не будемо:


19. Тепер додамо до тексту обробника задання параметрів запиту:

Зверніть увагу, що для формування списку номенклатури документа ми використовуємо метод ВыгрузитьКолонку() об'єкта ДокументТабличнаяЧасть.Оказание Услуги.ПереченьНоменклатуры.
20. Після цього додамо отримання результату запиту та цикл його обходу:

21. Тепер, перш ніж почати формування рухів за регістрами, нам потрібно перевірити наявність на складі достатньої кількості номенклатури в циклі обходу результату запиту:

22. І на завершення, після перевірки залишків на складі, перед самим кінцем циклу, додамо формування рухів за регістрами накопичення:

23. А відразу після циклу додамо запис рухів регістрів:

24. Запустимо 1С:Підприємство в режимі налагодження та перевіримо роботу нового обробника події ОбработкаПроведения, перепровівши всі документи ОказаниеУслуги.
![ГШ Конструктор запроса _ П -.її- База данных [ 1;Д Справочники Й' 1 Документы ЁЬМ ПриходнаяНаклад В- г/ 0 казаниеУ слуги — Ссылка - ПометкаУдал - Номер - Дата - Проведен ЁЪ— Склад [+] — Клиент ЁЬ— Мастер ф=1=] - Представлени! - МоментВреме! Й [.Л Перечисле](/img/materialy_praktikum_8_optimizatsiya_protsedury_provedeniya/256.png)