1c-query-optimization · git:20260225.16f6008 · 2026-02-25 · sha256 48800b416ee89e07
1c-query-optimization git:20260225.16f6008A
Immutable. This exact content is served forever at /api/v1/blob/48800b416ee89e07.
--- name: 1c-query-optimization description: "Advanced query patterns for 1C: temporary tables, joins, DCS optimization. Use for complex queries beyond basic rules in project_rules.mdc." --- # 1C Query Optimization Skill (Advanced Patterns) This skill provides **advanced** query patterns beyond basic rules in `project_rules.mdc`. For basic query rules (formatting, aliases, parameters, no queries in loops) — see `project_rules.mdc`. ## Validate Query via EDT-MCP Если EDT-MCP сервер подключен (инструменты `1c-edt` доступны), проверяй оптимизированный запрос через `validate_query` — убедись, что после рефакторинга нет синтаксических/семантических ошибок. Для запросов СКД: `dcsMode=true`. ## When to Use Invoke this skill when: - Working with complex multi-step data processing - Optimizing joins and subqueries - Implementing DCS reports - Processing large datasets in portions ## Temporary Tables Use temporary tables for: - Complex multi-step data processing - Joining data from multiple sources - Reusing intermediate results ### Join vs Subquery ```bsl // ✅ PREFERRED: Join (usually faster) "ВЫБРАТЬ | Заказы.Ссылка КАК Заказ, | Контрагенты.ИНН КАК ИНН |ИЗ | Документ.ЗаказКлиента КАК Заказы | ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Контрагенты КАК Контрагенты | ПО Заказы.Контрагент = Контрагенты.Ссылка" // ⚠️ AVOID: Subquery in SELECT (N+1 problem) "ВЫБРАТЬ | Заказы.Ссылка КАК Заказ, | (ВЫБРАТЬ К.ИНН ИЗ Справочник.Контрагенты КАК К | ГДЕ К.Ссылка = Заказы.Контрагент) КАК ИНН |ИЗ | Документ.ЗаказКлиента КАК Заказы" ``` ### Avoid Aggregation in Subqueries ```bsl // ❌ SLOW: Subquery with aggregation "ВЫБРАТЬ | Номенклатура.Ссылка, | (ВЫБРАТЬ СУММА(Остатки.Количество) ...) КАК Остаток |ИЗ | Справочник.Номенклатура КАК Номенклатура" // ✅ FAST: Join with pre-aggregated data "ВЫБРАТЬ | Номенклатура.Ссылка КАК Номенклатура, | ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток |ИЗ | Справочник.Номенклатура КАК Номенклатура | ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки | ПО Номенклатура.Ссылка = Остатки.Номенклатура" ``` ## Virtual Table Parameters Use virtual table parameters instead of WHERE for better performance: ```bsl // ✅ CORRECT: Uses virtual table parameters (fast) "ВЫБРАТЬ | Остатки.Номенклатура КАК Номенклатура, | Остатки.КоличествоОстаток КАК Остаток |ИЗ | РегистрНакопления.ТоварыНаСкладах.Остатки( | &Дата, | Номенклатура В (&СписокНоменклатуры)) КАК Остатки" // ❌ WRONG: Virtual table then filter (slow) "ВЫБРАТЬ | Остатки.Номенклатура КАК Номенклатура, | Остатки.КоличествоОстаток КАК Остаток |ИЗ | РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата) КАК Остатки |ГДЕ | Остатки.Номенклатура В (&СписокНоменклатуры)" ``` ## DCS (Data Composition System) Optimization ### Efficient DCS Queries 1. **Use parameters in query text:** ```bsl // Pass parameters to virtual table РегистрНакопления.Остатки.Остатки(&Период, Склад = &Склад) ``` 2. **Limit data at source:** ```bsl // Add conditions in DataSet query, not in DCS settings ГДЕ Период >= &НачалоПериода ``` 3. **Use ЕСТЬNULL for outer joins:** ```bsl ЕСТЬNULL(Остатки.Количество, 0) КАК Количество ``` ## Composite Type Dereferencing (ITS Standard) Avoid dereferencing composite type reference fields directly — the system creates queries for **ALL** possible types. ```bsl // ❌ SLOW: Dereferences ALL registrar types (can be hundreds) "ВЫБРАТЬ | ТоварыНаСкладах.Регистратор.Дата КАК ДатаДокумента |ИЗ | РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах" // ✅ FAST: Use ВЫРАЗИТЬ to specify exact type "ВЫБРАТЬ | ВЫРАЗИТЬ(ТоварыНаСкладах.Регистратор КАК Документ.ПоступлениеТоваровУслуг).Дата КАК ДатаДокумента |ИЗ | РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах" // ✅ For multiple known types, use ВЫБОР/КОГДА "ВЫБРАТЬ | ВЫБОР | КОГДА ТоварыНаСкладах.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг | ТОГДА ВЫРАЗИТЬ(ТоварыНаСкладах.Регистратор КАК Документ.ПоступлениеТоваровУслуг).Дата | КОГДА ТоварыНаСкладах.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг | ТОГДА ВЫРАЗИТЬ(ТоварыНаСкладах.Регистратор КАК Документ.РеализацияТоваровУслуг).Дата | КОНЕЦ КАК ДатаДокумента |ИЗ | РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах" ``` ## Use ПРЕДСТАВЛЕНИЕ for Display (ITS Standard) When you only need text representation, use `ПРЕДСТАВЛЕНИЕ()` to avoid extra joins: ```bsl // ❌ Creates additional subquery for Справочник.Склады "ВЫБРАТЬ | ТоварыНаСкладах.Склад.Наименование |ИЗ | РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах" // ✅ Optimal: No extra join "ВЫБРАТЬ | ПРЕДСТАВЛЕНИЕ(ТоварыНаСкладах.Склад) КАК СкладПредставление |ИЗ | РегистрНакопления.ТоварыНаСкладах КАК ТоварыНаСкладах" ``` ## Avoid Joins with Subqueries (ITS Standard) Never use subqueries in JOIN — use temporary tables instead: ```bsl // ❌ WRONG: Join with subquery "ВЫБРАТЬ ... |ИЗ | Документ.Заказ КАК Заказы | ЛЕВОЕ СОЕДИНЕНИЕ ( | ВЫБРАТЬ Товары.Заказ, СУММА(Товары.Сумма) КАК Сумма | ИЗ Документ.Заказ.Товары КАК Товары | СГРУППИРОВАТЬ ПО Товары.Заказ | ) КАК ИтогиТоваров | ПО Заказы.Ссылка = ИтогиТоваров.Заказ" // ✅ CORRECT: Use temporary table "ВЫБРАТЬ | Товары.Ссылка КАК Заказ, | СУММА(Товары.Сумма) КАК Сумма |ПОМЕСТИТЬ ИтогиТоваров |ИЗ | Документ.Заказ.Товары КАК Товары |СГРУППИРОВАТЬ ПО | Товары.Ссылка |ИНДЕКСИРОВАТЬ ПО | Заказ |; |ВЫБРАТЬ ... |ИЗ | Документ.Заказ КАК Заказы | ЛЕВОЕ СОЕДИНЕНИЕ ИтогиТоваров КАК ИтогиТоваров | ПО Заказы.Ссылка = ИтогиТоваров.Заказ" ``` ## Avoid Joins with Virtual Tables (ITS Standard) Extract virtual table results to temporary table before joining: ```bsl // ⚠️ May be slow: Direct join with virtual table "ВЫБРАТЬ ... |ИЗ | Справочник.Номенклатура КАК Номенклатура | ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата,) КАК Остатки | ПО Номенклатура.Ссылка = Остатки.Номенклатура" // ✅ BETTER: First extract to temporary table "ВЫБРАТЬ | Остатки.Номенклатура КАК Номенклатура, | Остатки.КоличествоОстаток КАК Остаток |ПОМЕСТИТЬ ВТОстатки |ИЗ | РегистрНакопления.ТоварыНаСкладах.Остатки(&Дата,) КАК Остатки |ИНДЕКСИРОВАТЬ ПО | Номенклатура |; |ВЫБРАТЬ ... |ИЗ | Справочник.Номенклатура КАК Номенклатура | ЛЕВОЕ СОЕДИНЕНИЕ ВТОстатки КАК Остатки | ПО Номенклатура.Ссылка = Остатки.Номенклатура" ``` ## Avoid OR in WHERE — Use ОБЪЕДИНИТЬ ВСЕ (ITS Standard) `OR` in `WHERE` prevents index usage. Split into UNION queries: ```bsl // ❌ SLOW: OR prevents index usage "ВЫБРАТЬ | Товары.Ссылка |ИЗ | Справочник.Номенклатура КАК Товары |ГДЕ | Товары.Артикул = &Артикул | ИЛИ Товары.Код = &Код" // ✅ FAST: Two indexed queries with UNION "ВЫБРАТЬ | Товары.Ссылка |ИЗ | Справочник.Номенклатура КАК Товары |ГДЕ | Товары.Артикул = &Артикул | |ОБЪЕДИНИТЬ ВСЕ | |ВЫБРАТЬ | Товары.Ссылка |ИЗ | Справочник.Номенклатура КАК Товары |ГДЕ | Товары.Код = &Код" ``` ## ОБЪЕДИНИТЬ vs ОБЪЕДИНИТЬ ВСЕ (ITS Standard) Prefer `ОБЪЕДИНИТЬ ВСЕ` when no duplicate rows expected: ```bsl // ⚠️ SLOWER: ОБЪЕДИНИТЬ performs additional grouping "ВЫБРАТЬ ... ИЗ Документ.Приход |ОБЪЕДИНИТЬ |ВЫБРАТЬ ... ИЗ Документ.Расход" // ✅ FASTER: ОБЪЕДИНИТЬ ВСЕ skips grouping "ВЫБРАТЬ ... ИЗ Документ.Приход |ОБЪЕДИНИТЬ ВСЕ |ВЫБРАТЬ ... ИЗ Документ.Расход" ``` ## Index Alignment (ITS Standard) Ensure query conditions match available indexes: **Index requirements:** 1. Index must contain **all fields** from the condition 2. Fields must be at the **beginning** of the index 3. Fields must be **consecutive** (no gaps) ```bsl // Given index: (Организация, Контрагент, Дата) // ✅ Index will be used — fields are at the beginning "ГДЕ Организация = &Орг И Контрагент = &Контр" // ❌ Index NOT used — skipped first field "ГДЕ Контрагент = &Контр И Дата = &Дата" // ⚠️ Partial use — gap in fields "ГДЕ Организация = &Орг И Дата = &Дата" ``` **Creating additional indexes:** - Set "Индексировать" = "Индексировать с доп. упорядочиванием" for frequently filtered attributes - Add `ИНДЕКСИРОВАТЬ ПО` for temporary tables used in joins --- **Reference**: [ITS Query Optimization Standards](https://its.1c.ru/db/v8std/browse/13/-1/26/28) **Remember**: Verify metadata attributes exist using `search_metadata` before writing queries.