A1sCode библиотека 1С

A1sQ.ExistsQ

Назначение: проверить, есть ли записи по условию, без перегонки всего набора.
Как: функция автоматически добавляет ПЕРВЫЕ 1 в начало ВЫБРАТЬ и выполняет запрос.
Результат: Истина / Ложь за миллисекунды даже на больших таблицах.
Идеально для: валидаций, предусловий перед записью, проверок прав и «есть ли документы за период». быстроэкономноиндекс‑дружелюбно

Сигнатура

Function ExistsQ(QueryText, Value1 = Undefined, Value2 = Undefined, Value3 = Undefined,
                 Value4 = Undefined, Value5 = Undefined, Value6 = Undefined,
                 Value7 = Undefined, Value8 = Undefined, TempTablesManager = Undefined) Export

Параметры

  • QueryText — текст запроса 1С или имя объекта метаданных (например, Справочник.Номенклатура).
  • Value1..Value8 — позиционные значения для параметров запроса по порядку появления &Парам.
  • TempTablesManager — менеджер временных таблиц (опционально).

Возвращаемое значение

Булево: есть хотя бы одна запись — Истина, иначе — Ложь.

Как работает (пошагово)

// 1) Анализируем текст
OptimizedText = QueryText;

// 2) Если запрос начинается с "ВЫБРАТЬ" и нет "ПЕРВЫЕ"
Если Верх(Лев(СтрЗаменить(QueryText, " ", ""), 7)) = "ВЫБРАТЬ"
 И СтрНайти(Верх(QueryText), "ПЕРВЫЕ") = 0 Тогда

    // 3) Вставляем "ПЕРВЫЕ 1" сразу после ВЫБРАТЬ
    OptimizedText = СтрЗаменить(QueryText, "ВЫБРАТЬ", "ВЫБРАТЬ ПЕРВЫЕ 1");
КонецЕсли;

// 4) Выполняем и проверяем наличие результата
Возврат IsNotEmptyResult(OptimizedText, Value1, Value2, Value3, Value4, Value5, Value6, Value7, Value8);
Важно: преобразование текстовое и намеренно простое — без глубокого парсинга. Работает в >90% практических кейсов.

Примеры

1) Проверка уникальности артикула

Если A1sQ.ExistsQ("ВЫБРАТЬ * ИЗ Справочник.Номенклатура ГДЕ Артикул = &Артикул", НовыйАртикул) Тогда
    Сообщить("Товар с таким артикулом уже существует!");
    Возврат;
КонецЕсли;

2) Права доступа

Если НЕ A1sQ.ExistsQ("ВЫБРАТЬ * ИЗ Справочник.Пользователи ГДЕ Ссылка = &Польз И НЕ Заблокирован",
                      ТекущийПользователь()) Тогда
    ВызватьИсключение("Доступ запрещен!");
КонецЕсли;

3) Есть ли документы за период

Если A1sQ.ExistsQ("ВЫБРАТЬ * ИЗ Документ.ПоступлениеТоваровУслуг
                    ГДЕ Дата МЕЖДУ &НачДата И &КонДата",
                    НачалоПериода, КонецПериода) Тогда
    ОбработатьДокументыЗаПериод(НачалоПериода, КонецПериода);
Иначе
    Сообщить("За указанный период документов не найдено");
КонецЕсли;

Что именно преобразуется

// Было
ВЫБРАТЬ *
ИЗ Справочник.Номенклатура
ГДЕ НЕ ПометкаУдаления
// Стало
ВЫБРАТЬ ПЕРВЫЕ 1 *
ИЗ Справочник.Номенклатура
ГДЕ НЕ ПометкаУдаления

Сложный запрос

// Было
ВЫБРАТЬ
    Номенклатура.Ссылка,
    Номенклатура.Наименование
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Родитель = &Родитель
    И НЕ Номенклатура.ПометкаУдаления
// Стало
ВЫБРАТЬ ПЕРВЫЕ 1
    Номенклатура.Ссылка,
    Номенклатура.Наименование
ИЗ
    Справочник.Номенклатура КАК Номенклатура
ГДЕ
    Номенклатура.Родитель = &Родитель
    И НЕ Номенклатура.ПометкаУдаления

Производительность

Идея простая: вместо формирования полного набора, СУБД останавливается на первой подходящей записи. Это экономит диск/CPU/память/сеть.

Процедура ДемонстрацияПроизводительности() Экспорт

    // Допустим, 1 000 000 позиций в справочнике.
    Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();

    // Медленно: без ПЕРВЫЕ 1 формируется весь набор.
    Рез1 = A1sQ.IsNotEmptyResult("
    |ВЫБРАТЬ
    |   Номенклатура.Ссылка,
    |   Номенклатура.Наименование,
    |   Номенклатура.Артикул
    |ИЗ Справочник.Номенклатура КАК Номенклатура
    |ГДЕ НЕ Номенклатура.ПометкаУдаления");

    T1 = ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало;

    Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();

    // Быстро: ExistsQ вставит ПЕРВЫЕ 1 и мгновенно вернет Истина/Ложь.
    Рез2 = A1sQ.ExistsQ("
    |ВЫБРАТЬ
    |   Номенклатура.Ссылка,
    |   Номенклатура.Наименование,
    |   Номенклатура.Артикул
    |ИЗ Справочник.Номенклатура КАК Номенклатура
    |ГДЕ НЕ Номенклатура.ПометкаУдаления");

    T2 = ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало;

    A1sS.Print("Медленный способ: " + ЧислоВСтроку(T1) + " мс");
    A1sS.Print("Быстрый способ: " + ЧислоВСтроку(T2) + " мс");
    Если T2 > 0 Тогда
        A1sS.Print("Ускорение в " + ЧислоВСтроку(T1 / T2) + " раз");
    КонецЕсли;

КонецПроцедуры

Практика

  • Перед записью новых данных — проверяйте коллизии ключей.
  • В расписаниях/обработках — быстро решайте «делать/пропускать» без тяжелых выборок.
  • При интеграциях — сокращайте объем передаваемых данных до минимума.

Особенности и ограничения

✅ Работает с:
• Простыми SELECT‑запросами
• Сложными JOIN‑ами и подзапросами
• GROUP BY / HAVING
• Параметрами запроса

❌ Не преобразует:
• Запросы, где уже есть ПЕРВЫЕ N
• UNION (ОБЪЕДИНИТЬ)
• INTO (ПОМЕСТИТЬ)
• UPDATE / INSERT / DELETE (не SELECT)

⚠️ Замечания:
• Вставка "ПЕРВЫЕ 1" — простая текстовая замена первого "ВЫБРАТЬ"
• Экзотические запросы могут потребовать ручной оптимизации

FAQ

Можно ли передавать именованные параметры?
Функция принимает до 8 позиционных значений. Для сложных случаев используйте подготовку запроса через A1sQ.ExecuteQ.
Что, если запрос не начинается с «ВЫБРАТЬ»?
Преобразование не применяется. Функция выполнит проверку как есть (например, при передаче имени объекта метаданных).
«ПЕРВЫЕ 1» уже есть — это не сломает запрос?
Нет. В этом случае замены не будет, запрос уйдет без изменений.
  • A1sQ.FirstRow — получить первую строку по условию.
  • A1sQ.ExecuteQ — тонкая настройка и выполнение.
  • A1sQ.QT — декларативная сборка запросов.

До / После

Без A1sС A1s
// Проверка существования без оптимизации
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Ссылка ИЗ Справочник.Пользователи ГДЕ Ссылка = &U";
Запрос.УстановитьПараметр("U", Пользователь);
Рез = Запрос.Выполнить().Выбрать();
Если Рез.Следующий() Тогда
    // есть
КонецЕсли;
// Быстро с ExistsQ (вставит ПЕРВЫЕ 1)
Если A1sQ.ExistsQ("ВЫБРАТЬ Ссылка ИЗ Справочник.Пользователи ГДЕ Ссылка = &U",
                   Пользователь) Тогда
    // есть
КонецЕсли;