В язык запросов 1С:Предприятия версии 8.1.10 и более поздних добавлена обобщенная форма оператора "В"/ "НЕ В". В разделе поясняются особенности выполнения этого оператора, учет которых позволит повысить производительность запросов.
Оператор вхождения имеет вид:
Копировать в буфер обмена<Выражение> В (<Список значений>)
Оператор вхождения является одним из операторов сравнения, определенных в языке запросов 1С:Предприятия, и принимает значение "Истина", если значение его левого операнда присутствует в списке значений из правого операнда. Список значений может задаваться в виде:
Если список значений задан вложенным запросом, то возможно использование обобщенной формы оператора вхождения - группового оператора вхождения:
Копировать в буфер обмена(<Выражение 1>, ..., <Выражение N>) В (<Вложенный запрос>)
Вложенный запрос должен определять выборку из N колонок.
Групповой оператор вхождения принимает значение "Истина", если запись (<Выражение 1>, ..., <Выражение N>) совпадает с одной из записей выборки, определяемой вложенным запросом. Например, пусть левым операндом является список:
Копировать в буфер обмена(1, 2, 3)
Если результатом вложенного запроса является выборка:
Копировать в буфер обмена1, 1, 1 2, 2, 2 2, 2, 3
то результатом группового оператора вхождения будет "Ложь", а если результатом вложенного запроса является выборка:
Копировать в буфер обмена1, 1, 1 1, 2, 3 2, 2, 3
то результатом группового оператора вхождения будет "Истина".
Наряду с оператором вхождения ("В") в языке запросов предусмотрен оператор не вхождения ("НЕ В"), результатом которого является отрицание оператора "В" с такими же операндами. Все, сказанное про особенности выполнения оператора "В", относится и к оператору "НЕ В".
В отличие от простого оператора вхождения, аналог которого имеется в большинстве СУБД (оператор IN языка SQL), групповой оператор вхождения не имеет точных аналогов в SQL. Поэтому при использовании группового оператора вхождения важно учитывать механизм его перевода в SQL. Оператор вида:
Копировать в буфер обмена(<Выражение 1>, ..., <Выражение N>) В ( ВЫБРАТЬ <Колонка 1>, ..., <Колонка N> ИЗ <Источники> ГДЕ <Условие> )
при выполнении его на СУБД будет иметь вид:
Копировать в буфер обменаEXISTS( SELECT 1 FROM <Источники> WHERE (<Условие>) AND <Выражение 1> = <Колонка 1> AND ... AND <Выражение N> = <Колонка N> )
Если вложенный запрос содержит агрегатные функции и/или раздел СГРУППИРОВАТЬ ПО, то групповой оператор вхождения вида:
Копировать в буфер обмена(<Выражение 1>, ..., <Выражение N>) В ( ВЫБРАТЬ <Колонка 1>, ..., <Колонка N> ИЗ <Источники> ГДЕ <Условие 1> СГРУППИРОВАТЬ ПО <Список группировки> ИМЕЮЩИЕ <Условие 2> )
на языке SQL будет записан так:
Копировать в буфер обменаEXISTS( SELECT 1 FROM <Источники> WHERE <Условие 1> GROUP BY <Список группировки> HAVING (<Условие 2>) AND <Выражение 1> = <Колонка 1> AND ... AND <Выражение N> = <Колонка N> )
Скорость выполнения запроса, содержащего групповой оператор вхождения, зависит от возможностей оптимизатора планов запросов используемой СУБД по эффективной реализации оператора EXISTS с вложенным запросом. Поэтому точно предсказать, будет ли данный запрос выполняться эффективно, в общем случае затруднительно. Однако, можно сформулировать рекомендации, следование которым поможет строить эффективно выполняющиеся запросы.
Использование в запросе группового оператора вхождения не приведет к снижению эффективности, если вложенный запрос будет строить выборку из таблиц с небольшим количеством записей, которое не может неограниченно расти в процессе эксплуатации системы и роста объема накопленной базы данных.
Необходимо учитывать, что запрос, вложенный в оператор EXISTS будет выполнен многократно при проверке условия отбора для каждой записи. Поэтому, если таблицы, используемые во вложенном запросе, могут содержать большое количество записей, то поиск в них необходимо ускорить посредством определения подходящих индексов:
ВЫБРАТЬ Док.Номенклатура.Представление КАК НоменклатураПредставление, Док.Номенклатура.ЕдиницаХраненияОстатков.Представление КАК ЕдиницаХраненияОстатковПредставление, Док.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры, Док.СерияНоменклатуры КАК СерияНоменклатуры, Док.Качество КАК Качество, СУММА(ВЫРАЗИТЬ(Док.Количество * Док.Коэффициент / Док.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент КАК ЧИСЛО(15,3))) КАК ДокументКоличество, ЕСТЬNULL(МАКСИМУМ(Остатки.КоличествоОстаток), 0) КАК ОстатокКоличество ИЗ Документ.РасходныйОрдерНаТовары.Товары КАК Док ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыКПередачеСоСкладов.Остатки(, Склад = &Склад И (Номенклатура, ХарактеристикаНоменклатуры, Качество) В ( ВЫБРАТЬ Док.Номенклатура, Док.ХарактеристикаНоменклатуры, Док.Качество ИЗ Документ.РасходныйОрдерНаТовары.Товары КАК Док ГДЕ Док.Ссылка = &ДокументСсылка И НЕ Док.Номенклатура.Комплект ) И &ДокументПередачи = ДокументПередачи) КАК Остатки ПО Док.Номенклатура = Остатки.Номенклатура И Док.ХарактеристикаНоменклатуры = Остатки.ХарактеристикаНоменклатуры И Док.Качество = Остатки.Качество И ((НЕ Док.СерияУказываетсяПриОтпускеСоСклада И Остатки.СерияНоменклатуры = Док.СерияНоменклатуры) ИЛИ (Док.СерияУказываетсяПриОтпускеСоСклада И Остатки.СерияНоменклатуры = &ПустаяСерия)) ГДЕ Док.Ссылка = &ДокументСсылка СГРУППИРОВАТЬ ПО Док.Номенклатура, Док.ХарактеристикаНоменклатуры, Док.СерияНоменклатуры, Док.Качество, &ДокументПередачи ИМЕЮЩИЕ ЕСТЬNULL(МАКСИМУМ(Остатки.КоличествоОстаток), 0) < СУММА(ВЫРАЗИТЬ(Док.Количество * Док.Коэффициент / Док.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент КАК ЧИСЛО(15,3)))
Следующий запрос отличается тем, что в групповой оператор вхождения добавлено поле "Склад", по которому в таблице "Документ.РасходныйОрдерНаТовары.Товары" имеется индекс. В результате время выполнения запроса существенно уменьшилось.
Копировать в буфер обменаВЫБРАТЬ Док.Номенклатура.Представление КАК НоменклатураПредставление, Док.Номенклатура.ЕдиницаХраненияОстатков.Представление КАК ЕдиницаХраненияОстатковПредставление, Док.ХарактеристикаНоменклатуры КАК ХарактеристикаНоменклатуры, Док.СерияНоменклатуры КАК СерияНоменклатуры, Док.Качество КАК Качество, СУММА(ВЫРАЗИТЬ(Док.Количество * Док.Коэффициент / Док.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент КАК ЧИСЛО(15,3))) КАК ДокументКоличество, ЕСТЬNULL(МАКСИМУМ(Остатки.КоличествоОстаток), 0) КАК ОстатокКоличество ИЗ Документ.РасходныйОрдерНаТовары.Товары КАК Док ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыКПередачеСоСкладов.Остатки(, (Склад, Номенклатура, Качество, ХарактеристикаНоменклатуры) В ( ВЫБРАТЬ Док.Ссылка.Склад, Док.Номенклатура, Док.Качество, Док.ХарактеристикаНоменклатуры ИЗ Документ.РасходныйОрдерНаТовары.Товары КАК Док ГДЕ Док.Ссылка = &ДокументСсылка И НЕ Док.Номенклатура.Комплект ) И &ДокументПередачи = ДокументПередачи) КАК Остатки ПО Док.Ссылка.Склад = Остатки.Склад И Док.Номенклатура = Остатки.Номенклатура И Док.Качество = Остатки.Качество И Док.ХарактеристикаНоменклатуры = Остатки.ХарактеристикаНоменклатуры И ((НЕ Док.СерияУказываетсяПриОтпускеСоСклада И Остатки.СерияНоменклатуры = Док.СерияНоменклатуры) ИЛИ (Док.СерияУказываетсяПриОтпускеСоСклада И Остатки.СерияНоменклатуры = &ПустаяСерия)) ГДЕ Док.Ссылка = &ДокументСсылка СГРУППИРОВАТЬ ПО Док.Номенклатура, Док.ХарактеристикаНоменклатуры, Док.СерияНоменклатуры, Док.Качество, &ДокументПередачи ИМЕЮЩИЕ ЕСТЬNULL(МАКСИМУМ(Остатки.КоличествоОстаток), 0) < СУММА(ВЫРАЗИТЬ(Док.Количество * Док.Коэффициент / Док.Номенклатура.ЕдиницаХраненияОстатков.Коэффициент КАК ЧИСЛО(15,3)))
Если запрос, вложенный в групповой оператор вхождения, сложный, содержит агрегатные функции, использует таблицы с большим количеством записей, то вероятно, что исполнение группового оператора вхождения будет крайне не эффективным. В этом случае целесообразно: