بنقرة واحدة
query-patterns
When writing new 1C queries and parameters
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
When writing new 1C queries and parameters
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
When writing or reviewing BSL, apply 1C standards
При написании или ревью BSL применять стандарты 1С
Orchestrator: routing work and agent phases
Оркестратор: маршрутизация работы и фаз агентов
BSL LSP navigation: definitions, refs, call graph
Rules for using RLM tools for project search and navigation in 1C/BSL
| name | query-patterns |
| description | When writing new 1C queries and parameters |
| alwaysApply | false |
Key principle: Every database query is a network round-trip. Minimizing the number of queries and the amount of returned data is the priority.
For N iterations: N * (network latency + execution time). 1000 elements * 5 ms = 5 seconds just waiting on the network.
ITS standard: "Database queries - restriction on using queries in a loop".
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Ссылка,
| Номенклатура.Наименование КАК Наименование,
| Номенклатура.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
| Номенклатура.СтавкаНДС КАК СтавкаНДС
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
|ГДЕ
| Номенклатура.Ссылка В (&МассивНоменклатуры)";
Запрос.УстановитьПараметр("МассивНоменклатуры", МассивНоменклатуры);
Результат = Запрос.Выполнить();
Выборка = Результат.Выбрать();
СоответствиеДанных = Новый Соответствие;
Пока Выборка.Следующий() Цикл
СоответствиеДанных.Вставить(Выборка.Ссылка,
Новый Структура("Наименование, ЕдиницаИзмерения, СтавкаНДС",
Выборка.Наименование, Выборка.ЕдиницаИзмерения, Выборка.СтавкаНДС));
КонецЦикла;
Для Каждого СтрокаТоваров Из Документ.Товары Цикл
ДанныеНоменклатуры = СоответствиеДанных.Получить(СтрокаТоваров.Номенклатура);
Если ДанныеНоменклатуры <> Неопределено Тогда
СтрокаТоваров.ЕдиницаИзмерения = ДанныеНоменклатуры.ЕдиницаИзмерения;
КонецЕсли;
КонецЦикла;
Split into stages: readability, step-by-step debugging, indexing intermediate results.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК ДокументСсылка,
| Реализация.Контрагент КАК Контрагент,
| Реализация.СуммаДокумента КАК Сумма
|ПОМЕСТИТЬ втРеализации
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Дата МЕЖДУ &ДатаНачала И &ДатаОкончания
| И Реализация.Проведен
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| втРеализации.Контрагент КАК Контрагент,
| СУММА(втРеализации.Сумма) КАК ОбщаяСумма,
| КОЛИЧЕСТВО(РАЗЛИЧНЫЕ втРеализации.ДокументСсылка) КАК КоличествоДокументов
|ПОМЕСТИТЬ втИтогиПоКонтрагентам
|ИЗ
| втРеализации
|
|СГРУППИРОВАТЬ ПО
| втРеализации.Контрагент
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| втИтоги.Контрагент КАК Контрагент,
| втИтоги.ОбщаяСумма КАК ОбщаяСумма,
| втИтоги.КоличествоДокументов КАК КоличествоДокументов,
| ВзаиморасчетыОстатки.СуммаОстаток КАК Задолженность
|ИЗ
| втИтогиПоКонтрагентам КАК втИтоги
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки КАК ВзаиморасчетыОстатки
| ПО втИтоги.Контрагент = ВзаиморасчетыОстатки.Контрагент
|
|УПОРЯДОЧИТЬ ПО
| ОбщаяСумма УБЫВ";
Rules: prefix вт (standard); ИНДЕКСИРОВАТЬ ПО for join fields; use МенеджерВременныхТаблиц to manage lifecycle.
Injecting values into text: vulnerability, inability to cache the DBMS execution plan, formatting errors.
// Правильно — через параметры
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ Товары.Наименование
|ИЗ Справочник.Номенклатура КАК Товары
|ГДЕ Товары.ВидНоменклатуры = &ВидНоменклатуры
| И Товары.Цена >= &МинимальнаяЦена";
Запрос.УстановитьПараметр("ВидНоменклатуры", Перечисления.ВидыНоменклатуры.Товар);
Запрос.УстановитьПараметр("МинимальнаяЦена", 1000);
NULL + 100 = NULL, NULL > 0 = FALSE. An unhandled NULL leads to incorrect calculations and lost rows in conditions.
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Номенклатура,
| ЕСТЬNULL(ОстаткиТоваров.КоличествоОстаток, 0) КАК Остаток,
| ЕСТЬNULL(ОстаткиТоваров.СуммаОстаток, 0) КАК СуммаОстатка
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК ОстаткиТоваров
| ПО Номенклатура.Ссылка = ОстаткиТоваров.Номенклатура
|ГДЕ
| ЕСТЬNULL(ОстаткиТоваров.КоличествоОстаток, 0) > 0";
// ПЛОХО: ГДЕ ОстаткиТоваров.КоличествоОстаток > 0
// → NULL > 0 = FALSE → rows without stock will disappear (LEFT JOIN turns into INNER)
Virtual tables (Остатки, Обороты, СрезПоследних) are parameterized DBMS functions. Parameters inside mean an optimal plan. Parameters in WHERE mean the DBMS will first calculate all data, then filter it. The difference is by orders of magnitude.
ITS standard: "Using virtual tables".
// Правильно — параметры внутри виртуальной таблицы
Запрос.Текст =
"ВЫБРАТЬ
| Остатки.Номенклатура КАК Номенклатура,
| Остатки.Склад КАК Склад,
| Остатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрНакопления.ТоварыНаСкладах.Остатки(
| &ДатаОстатков,
| Номенклатура В (&СписокНоменклатуры)
| И Склад = &Склад
| ) КАК Остатки";
// ПЛОХО: СУБД вычислит остатки по ВСЕЙ номенклатуре на ВСЕХ складах, потом отфильтрует
"ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
|ГДЕ Остатки.Номенклатура В (&СписокНоменклатуры)"
Passing an array of data into a temporary table through Запрос.УстановитьПараметр("ВТ", ТаблицаЗначений) is one query instead of a loop.
ТаблицаДанных = Новый ТаблицаЗначений;
ТаблицаДанных.Колонки.Добавить("Штрихкод", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(13)));
ТаблицаДанных.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 3)));
Для Каждого СтрокаИмпорта Из ДанныеИмпорта Цикл
НоваяСтрока = ТаблицаДанных.Добавить();
НоваяСтрока.Штрихкод = СтрокаИмпорта.Штрихкод;
НоваяСтрока.Количество = СтрокаИмпорта.Количество;
КонецЦикла;
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("ДанныеИмпорта", ТаблицаДанных);
Запрос.Текст =
"ВЫБРАТЬ
| Данные.Штрихкод КАК Штрихкод,
| Данные.Количество КАК Количество
|ПОМЕСТИТЬ втДанныеИмпорта
|ИЗ
| &ДанныеИмпорта КАК Данные
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Штрихкоды.Номенклатура КАК Номенклатура,
| втДанные.Количество КАК Количество
|ИЗ
| втДанныеИмпорта КАК втДанные
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрСведений.ШтрихкодыНоменклатуры КАК Штрихкоды
| ПО втДанные.Штрихкод = Штрихкоды.Штрихкод";
| Method | When to use |
|---|---|
Выбрать()/Следующий() | Sequential processing, memory savings |
Выбрать(ПоГруппировкам) | Hierarchical data |
Выгрузить() → ТаблицаЗначений | Random access, search, passing to another procedure, data < 10,000 rows |
// Выборка — данные загружаются порциями
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// обработка
КонецЦикла;
// Выгрузка — всё в память
ТаблицаРезультат = Запрос.Выполнить().Выгрузить();
НайденнаяСтрока = ТаблицаРезультат.Найти(ИскомаяНоменклатура, "Номенклатура");
Index: fields in WHERE conditions, fields in join conditions (ON/ON), sorting fields.
An index on a temporary table is not added "just in case". It is justified when the temporary table is large and later repeatedly participates in joins/selections, or when there is a benchmark/query plan showing a gain. For small one-off temp tables, an index can cost more than the subsequent read.
Запрос.Текст =
"ВЫБРАТЬ
| ДанныеЗаказов.Номенклатура КАК Номенклатура,
| ДанныеЗаказов.Количество КАК Количество
|ПОМЕСТИТЬ втЗаказы
|ИЗ
| &ТаблицаЗаказов КАК ДанныеЗаказов
|
|ИНДЕКСИРОВАТЬ ПО
| Номенклатура
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| втЗаказы.Номенклатура,
| втЗаказы.Количество КАК Заказано,
| ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК НаСкладе
|ИЗ
| втЗаказы
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(,
| Номенклатура В (ВЫБРАТЬ втЗаказы.Номенклатура ИЗ втЗаказы)) КАК Остатки
| ПО втЗаказы.Номенклатура = Остатки.Номенклатура";
In the configurator/EDT: Attribute -> Properties -> «Index».
Excess fields: unnecessary traffic, inability to use a covering index, fragility when adding attributes.
// Correct — explicit list of fields
"ВЫБРАТЬ
| Контрагенты.Ссылка,
| Контрагенты.Наименование,
| Контрагенты.ИНН
|ИЗ Справочник.Контрагенты КАК Контрагенты"
A query without a limit can return millions of rows and exhaust memory.
"ВЫБРАТЬ ПЕРВЫЕ 100
| Товары.Наименование, Товары.Код
|ИЗ Справочник.Номенклатура КАК Товары
|ГДЕ Товары.Наименование ПОДОБНО &СтрокаПоиска
|УПОРЯДОЧИТЬ ПО Товары.Наименование"
For displaying lists, use dynamic lists — they implement pagination automatically.
DISTINCT requires sorting/hashing all rows. If duplicates are caused by an unnecessary JOIN, fix the query.
// Правильно — подзапрос вместо JOIN + РАЗЛИЧНЫЕ
"ВЫБРАТЬ Контрагенты.Ссылка, Контрагенты.Наименование
|ИЗ Справочник.Контрагенты КАК Контрагенты
|ГДЕ Контрагенты.Ссылка В
| (ВЫБРАТЬ РАЗЛИЧНЫЕ Реализация.Контрагент
| ИЗ Документ.РеализацияТоваровУслуг КАК Реализация
| ГДЕ Реализация.Дата >= &ДатаНачала)"
If a field has a composite type (e.g. "Recorder"), the DBMS makes a LEFT JOIN to all tables of the composite type. ВЫРАЗИТЬ(Поле КАК Тип) limits the JOIN to one table.
ITS standard: "Using the ВЫРАЗИТЬ construct in queries".
// Правильно — JOIN только с одной таблицей
"ВЫБРАТЬ
| Движения.Период,
| ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.РеализацияТоваровУслуг).Контрагент КАК Контрагент,
| Движения.Количество
|ИЗ РегистрНакопления.ТоварыНаСкладах КАК Движения
|ГДЕ Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг"
// ПЛОХО: Движения.Регистратор.Контрагент без ВЫРАЗИТЬ
// Если Регистратор может быть 20 видами документов — 20 LEFT JOIN!
ПО filters the right table BEFORE the join - left rows without a match remain with NULLГДЕ filters AFTER - rows with NULL are discarded, turning LEFT JOIN into INNER JOIN// Правильно — фильтр правой таблицы в параметрах виртуальной таблицы / в ON
"ВЫБРАТЬ
| Номенклатура.Наименование,
| ЕСТЬNULL(Цены.Цена, 0) КАК Цена
|ИЗ Справочник.Номенклатура КАК Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних(&ДатаЦен,
| ВидЦены = &ВидЦены) КАК Цены
| ПО Номенклатура.Ссылка = Цены.Номенклатура"
// Все товары в результате, даже без цены
// ПЛОХО: ГДЕ Цены.ВидЦены = &ВидЦены
// → строки без цены (NULL) отфильтруются — LEFT JOIN стал INNER JOIN
These checks do not replace optimization; they catch style/safety defects before performance measurements.
| Check | Rule |
|---|---|
| Names | Nested queries, temporary tables, and SKD datasets have meaningful names. Do not leave ВложенныйЗапрос, ВТ1, НаборДанных1 in production code. |
| Composite types | For dereferencing a composite-type attribute, use ВЫРАЗИТЬ(... КАК ...) and/or ССЫЛКА so you do not get implicit JOINs to all possible types. |
| Predefined values | For empty references and predefined values, use ЗНАЧЕНИЕ(...) when the value is part of the model rather than an external parameter. |
| Extra fields | Do not select fields that are not used later. |
| String constants | Pass values that change from call to call as parameters, not as string literals in the query text. |
| RLS | Do not remove РАЗРЕШЕННЫЕ for performance without an explicit security decision. |
| Calculations | First calculate complex computed fields in ВЫБРАТЬ/VT, then filter by the ready-made field if that reduces repeated calculations. |
ПО vs ГДЕ | Keep join conditions in ПО; keep result filtering conditions in ГДЕ. Mix them only with a deliberate reason. |
| Dot dereference | Avoid long dotted chains in ГДЕ; move the needed values into VT or use an explicit join. |
| Query construction | Do not build arbitrary query text by concatenation in business logic. Dynamic fragments must be predefined, marked, and constrained by a whitelist of tables/fields. |
| Long query | Move a long query into a separate function that returns only the text. Execution, parameters, and result handling remain separate. |
| Order | First set Запрос.Текст, then call Запрос.УстановитьПараметр(...). |
| Empty result | Do not make a separate РезультатЗапроса.Пустой() check before a normal traversal if the first Выборка.Следующий() is enough. |
| Boolean fields | For a boolean result, use boolean algebra (Количество > 0 КАК ЕстьОстаток) rather than ВЫБОР ... ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ. |
АВТОНОМЕРЗАПИСИ | Do not build business logic on the expectation that record autonumbering is guaranteed to start from one and have no gaps. |
IN (...) is not a universal solutionFor small parameter lists, IN (&List) is fine. For large sets, subqueries, and critical sections, check the variant using a temporary table and an explicit join: this makes it easier to control the plan, indexes, and data volume.
Do not rewrite IN mechanically. Change the query shape only when there is an expected gain, readability, or evidence from the plan/measurement.
ИТОГИ generates additional total rows. They are needed only when hierarchically traversing Выбрать(ПоГруппировкам). For a flat selection, use СГРУППИРОВАТЬ ПО, without ИТОГИ.
// Flat selection of totals by counterparties — СГРУППИРОВАТЬ ПО, without ИТОГИ
"ВЫБРАТЬ Контрагент, СУММА(Сумма) КАК Итог
|ИЗ ...
|СГРУППИРОВАТЬ ПО Контрагент"