A1sQ.ExistsQ
Назначение: проверить, есть ли записи по условию, без перегонки всего набора.
Как: функция автоматически добавляет ПЕРВЫЕ 1 в начало ВЫБРАТЬ и выполняет запрос.
Результат: Истина / Ложь за миллисекунды даже на больших таблицах.
Как: функция автоматически добавляет ПЕРВЫЕ 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 |
|---|---|
|
|