Функции языка Transact-SQL могут быть агрегатными или скалярными. Эти типы функций рассматриваются в этой статье.
Агрегатные функции
Агрегатные функции выполняют вычисления над группой значений столбца и всегда возвращают одно значение результата этих вычислений. Язык Transact-SQL поддерживает несколько обычных агрегатных функций:
Вычисляет среднее арифметическое значение данных, содержащихся в столбце. Значения, над которыми выполняется вычисление, должны быть числовыми.
MIN и MAX
Определяют максимальное и минимальное значение из всех значений данных, содержащихся в столбце. Значения могут быть числовыми, строковыми или временными (дата/время).
Вычисляет общую сумму значений в столбце. Значения, над которыми выполняется вычисление, должны быть числовыми.
COUNT
Подсчитывает количество значений, отличных от null в столбце. Функция count(*) является единственной агрегатной функцией, которая не выполняет вычисления над столбцами. Эта функция возвращает количество строк (независимо от того, содержат ли отдельные столбцы значения null).
COUNT_BIG
Аналогична функции count, с той разницей, что возвращает значение данных типа BIGINT.
Использование обычных агрегатных функций в инструкции SELECT будет рассматриваться в одной из следующих статей.
Скалярные функции
Скалярные функции Transact-SQL используются в создании скалярных выражений. (Скалярная функция выполняет вычисления над одним значением или списком значений, тогда как агрегатная функция выполняет вычисления над группой значений из нескольких строк.) Скалярные функции можно разбить на следующие категории:
Эти типы функций рассматриваются в последующих разделах.
Числовые функции
Числовые функции Transact-SQL
Функция
Синтаксис
Описание
Пример использования
ABS
ABS(n)
Возвращает абсолютное значение (т. е. отрицательные значения возвращаются, как положительные) числового выражения n.
Обратные тригонометрические функции, вычисляющие арккосинус, арксинус, арктангенс значения n (для ATN2 вычисляется арктангенс n/m). Исходные значения n, m и результат имеют тип данных FLOAT.
Тригонометрические функции, вычисляющие косинус, синус, тангенс, котангенс значения n. Результат имеет тип данных FLOAT.
Функция DEGREES преобразует радианы в градусы, RADIANS соответственно наоборот.
Округляет число до большего целого значения.
Округляет до меньшего целого значения.
Возвращает значение π (3,1415).
Возвращает произвольное число типа FLOAT в диапазоне значений между 0 и 1.
Возвращает количество строк таблицы, которые были обработаны последней инструкцией Transact-SQL, исполненной системой. Возвращаемое значение имеет тип BIGINT.
Функции даты
Функции даты вычисляют соответствующие части даты или времени выражения или возвращают значение временного интервала. Поддерживаемые в Transact-SQL функции даты и их краткое описание приводятся в таблице ниже:
Функции даты Transact-SQL
Функция
Синтаксис
Описание
Пример использования
GETDATE
GETDATE()
Возвращает текущую системную дату и время.
Возвращает указанную в параметре item часть даты date в виде целого числа.
Возвращает указанную в параметре item часть даты date в виде строки символов.
Вычисляет разницу между двумя частями дат dat1 и dat2 и возвращает целочисленный результат в единицах, указанных в аргументе item.
Прибавляет n-е количество единиц, указанных в аргументе item к указанной дате date. (Значение аргумента n также может быть отрицательным.)
Строковые функции
Строковые функции манипулируют значениями столбцов, которые обычно имеют символьный тип данных. Поддерживаемые в Transact-SQL строковые функции и их краткое описание приводятся в таблице ниже:
Строковые функции Transact-SQL
Функция
Синтаксис
Описание
Пример использования
ASCII, UNICODE
ASCII(char), UNICODE(char)
Преобразовывает указанный символ в соответствующее целое число кода ASCII.
Преобразовывает код ASCII (или Unicode если NCHAR) в соответствующий символ.
Возвращает начальную позицию вхождения подстроки str1 в строку str2. Если строка str2 не содержит подстроки str1, возвращается значение 0
Возвращает целое число от 0 до 4, которое является разницей между значениями SOUNDEX двух строк str1 и str2. Метод SOUNDEX возвращает число, которое характеризует звучание строки. С помощью этого метода можно определить подобно звучащие строки. Работает только для символов ASCII.
Возвращает количество первых символов строки str, заданное параметром length для LEFT и последние length символов строки str для функции RIGHT.
Возвращает количество символов (не количество байт) строки str, указанной в аргументе, включая конечные пробелы.
Функция LOWER преобразовывает все прописные буквы строки str1 в строчные. Входящие в строку строчные буквы и иные символы не затрагиваются. Функция UPPER преобразовывает все строчные буквы строки str в прописные.
Функция LTRIM удаляет начальные пробелы в строке str, RTRIM соответственно удаляет пробелы в конце строки.
Возвращает строку в кодировке Unicode с добавленными ограничителями, чтобы преобразовать строку ввода в действительный идентификатор с ограничителями.
Возвращает начальную позицию первого вхождения шаблона p в заданное выражение expr, или ноль, если данный шаблон не обнаружен.
Заменяет все вхождения подстроки str2 в строке str1 подстрокой str3.
Повторяет i раз строку str.
Выводит строку str в обратном порядке.
Возвращает четырехсимвольный код soundex, используемый для определения похожести двух строк. Работает только для символов ASCII.
Удаляет из строки str1 length-символов, начиная с позиции a, и вставляет на их место строку str2.
Извлекает из строки str, начиная с позиции a, подстроку длиной length.
Системные функции
Системные функции языка Transact-SQL предоставляют обширную информацию об объектах базы данных. Большинство системных функций использует внутренний числовой идентификатор (ID), который присваивается каждому объекту базы данных при его создании. Посредством этого идентификатора система может однозначно идентифицировать каждый объект базы данных.
В следующей таблице приводятся некоторые из наиболее важных системных функций вместе с их кратким описанием:
Системные функции Transact-SQL
Функция
Синтаксис
Описание
Пример использования
CAST
CAST (w AS type [(length)]
Преобразовывает выражение w в указанный тип данных type (если это возможно). Аргумент w может быть любым действительным выражением.
Возвращает длину столбца col объекта базы данных (таблицы или представления) obj.
Эквивалент функции CAST, но аргументы указываются по-иному. Может применяться с любым типом данных.
Возвращает текущие дату и время.
Возвращает имя текущего пользователя.
Возвращает число байтов, которые занимает выражение z.
Возвращает 1, если использование значений null в базе данных dbname отвечает требованиям стандарта ANSI SQL.
Возвращает значение выражения expr, если оно не равно NULL; в противном случае возвращается значение value.
Определяет, имеет ли выражение expr действительный числовой тип.
Создает однозначный идентификационный номер ID, состоящий из 16-байтовой двоичной строки, предназначенной для хранения значений типа данных UNIQUEIDENTIFIER.
Создает идентификатор GUID, больший, чем любой другой идентификатор GUID, созданный ранее этой функцией на указанном компьютере. (Эту функцию можно использовать только как значение по умолчанию для столбца.)
Возвращает значение null, если значения выражений expr1 и expr2 одинаковые.
Возвращает информацию о свойствах сервера базы данных.
Возвращает ID текущего пользователя.
Возвращает идентификатор пользователя username. Если пользователь не указан, то возвращается идентификатор текущего пользователя.
Возвращает имя пользователя с указанным идентификатором id. Если идентификатор не указан, то возвращается имя текущего пользователя.
Функции метаданных
По большому счету, функции метаданных возвращают информацию об указанной базе данных и объектах базы данных. В таблице ниже приводятся некоторые из наиболее важных функций метаданных вместе с их кратким описанием:
Функции метаданных Transact-SQL
Функция
Синтаксис
Описание
Пример использования
COL_NAME
COL_NAME (tab_id, col_id)
Возвращает имя столбца с указанным идентификатором col_id таблицы с идентификатором tab_id.
Возвращает информацию об указанном столбце.
Возвращает значение свойства property базы данных database.
Возвращает идентификатор базы данных db_name. Если имя базы данных не указано, то возвращается идентификатор текущей базы данных.
Возвращает имя базы данных, имеющей идентификатор db_id. Если идентификатор не указан, то возвращается имя текущей базы данных.
Возвращает имя индексированного столбца таблицы table. Столбец указывается идентификатором индекса i и позицией no столбца в этом индексе.
Возвращает свойства именованного индекса или статистики для указанного идентификационного номера таблицы, имя индекса или статистики, а также имя свойства.
Возвращает имя объекта базы данных, имеющего идентификатор obj_id.
Возвращает идентификатор объекта obj_name базы данных.
Возвращает информацию об объектах из текущей базы данных.
Лекция 17. Общая характеристика оператора SELECT и организация списка ссылок на таблицы в разделе FROM
17.1. Введение
Несмотря на то, что язык SQL является полным языком баз данных, включающим множество разнообразных средств определения схемы, ограничения и поддержки целостности базы данных, поддержки администрирования, заполнения и модификации таблиц базы данных, поддержки разработки приложений и т. д., для подавляющего большинства пользователей этот язык остается языком запросов, т. е. языком, позволяющим формулировать произвольно сложные и точные декларативные запросы к базе данных.
Как отмечалось в конце предыдущей лекции, структура стандарта языка SQL фактически не позволяет описать одну часть языка (в частности, средства запросов) в отрыве от других частей. Тем не менее, полагая, что средства выборки данных составляют наиболее интересную и практически значимую часть языка, мы выделили для их рассмотрения несколько отдельных лекций.
Напомним, что в этом курсе мы ограничиваемся базовым подмножеством SQL:1999 и SQL:2003 («прямым SQL») и даже это подмножество описываем не в полном объеме стандарта. Кроме того, в данной лекции мы не будем точно придерживаться порядка введения понятий и синтаксических конструкций, принятого в стандарте языка. Мы начнем с некоторой общей картины, дающей представление об операторе выборки, а затем будем постепенно уточнять ее.
17.2. Скалярные выражения
Скалярное выражение 115) – это выражение, вырабатывающее результат некоторого типа, специфицированного в стандарте. Скалярные выражения являются основой языка SQL, поскольку, хотя это реляционный язык, все условия, элементы списков выборки и т. д. базируются именно на скалярных выражениях. В SQL:1999 имеется несколько разновидностей скалярных выражений. К числу наиболее важных разновидностей относятся численные выражения; выражения со значениями-строками символов; выражения со значениями даты-времени; выражения со значениями-временными интервалами; булевские выражения. Мы не будем слишком глубоко вникать в тонкости, но тем не менее приведем некоторые базовые спецификации и пояснения.
Прежде чем перейти к конкретным видам скалярных выражений, рассмотрим некоторые наиболее общие языковые конструкции, на которых эти выражения базируются.
17.2.1. Общие синтаксические правила построения скалярных выражений
В SQL:2003 имеются девять разновидностей выражений в соответствии с девятью категориями типов данных, значения которых вырабатываются при вычислении выражения
Как уже отмечалось в начале этого раздела, мы ограничимся обсуждением первых пяти разновидностей выражений. В основе построения этих видов выражений лежит первичное выражение, определяемое следующим синтаксическим правилом:
17.2.2. Численные выражения
Численное выражение – это выражение, значение которого относится к числовому типу данных. Вот формальный синтаксис численного выражения:
Следует обратить внимание на то, что в численных выражениях SQL первичная составляющая ( numeric_primary ) является либо первичным выражением (см. выше), либо вызовом функции с численным значением ( numeric_value_function ). Из этого, в частности, следует, что в численные выражения могут входить выражения с переключателем и операции преобразования типов. Вызовы функций с численным значением определяются следующими синтаксическими правилами:
Мы достаточно подробно обсуждали функции определения позиции и длины по отношению к символьным и битовым строкам при рассмотрении соответствующих типов данных; здесь приводится только уточненный синтаксис их вызова. Функция EXTRACT извлечения поля из значений дата-время или интервал позволяет получить в виде точного числа с масштабом 0 значение любого поля (года, месяца, дня и т. д.). Какой конкретный тип точных чисел будет выбран – определяется в реализации. Функции ABS и MOD возвращают абсолютное значение числа и остаток от деления одного целого значения на другое соответственно.
17.2.3. Выражения, значениями которых являются символьные или битовые строки
Выражения символьных и битовых строк – это выражения, значениями которых являются символьные или битовые строки. Соответствующие конструкции определяются следующим синтаксисом:
Если не вдаваться в тонкости, смысл выражений символьных и битовых строк понятен из описания синтаксиса: единственная применимая для построения выражений операция – это конкатенация, производящая «склейку» строк-операндов. Более важно то, что первичной составляющей выражения над строками может быть как первичное скалярное выражение (см. выше), так и вызов функций, возвращающих строчные значения. Репертуар и синтаксис вызова таких функций определяются следующими правилами:
17.2.4. Выражения даты-времени
К выражениям даты-времени мы относим выражения, вырабатывающие значения типа дата-время и интервал. Выражения даты-времени определяются следующими синтаксическими правилами:
Как видно из описания синтаксиса, сами выражения строятся очень просто – на основе обычных арифметических операций. Снова более интересны первичные составляющие – вызовы функций, возвращающих значение дата-время. Эти вызовы определяются следующим синтаксисом:
Синтаксис выражений со значениями типа интервал определяется следующими правилами:
Как видно из приведенных правил, выражения со значениями типа интервал устроены очень просто; почти вся содержательная информация была приведена при обсуждении соответствующего типа данных. Стоит только заметить, что квалификатор интервала указывается для того, чтобы явно специфицировать единицу измерения интервала. Поддерживается только одна функция ABS (абсолютное значение), аргументом которой является выражение со значением типа интервал.
17.2.5. Булевские выражения
17.2.6. Выражения с переключателем
Выражения с переключателем в некотором смысле ортогональны рассмотренным выше видам выражений, поскольку разные выражения с переключателем могут вырабатывать значения разных типов в зависимости от типа данных элементов. Поскольку мы еще вообще не рассматривали этот вид выражений, обсудим их более подробно. Как обычно, начнем с синтаксиса:
эквивалентно выражению с поисковым переключателем
Выражение NULLIF (V1, V2) эквивалентно следующему выражению с переключателем:
Выражение COALESCE (V1, V2) эквивалентно следующему выражению с переключателем:
116 Другие варианты появляются во встраиваемом и динамическом SQL, а также расширении языка, предназначенного для написания кода хранимых процедур, триггеров, методов определяемых пользователями типов и т.д. В любом случае беззнаковое значение известно до начала компиляции любой содержащей его конструкции языка SQL.
Определяемые пользователем скалярные функции могут быть детерминированными или недетерминированными. Детерминированная функция всегда возвращает один и тот же результат при вызове с конкретным набором входных параметров. Недетерминированная функция может возвращать разные результаты при вызове с конкретным набором входных параметров.
Не следует помечать функцию как детерминированную, если она не всегда выдает одинаковые выходные значения при передаче одинаковых входных значений и при одинаковом состоянии базы данных. Не следует определять функцию как детерминированную, если в действительности она таковой не является. Это может привести к искажению индексированных представлений и вычисляемых столбцов. Определить функцию как детерминированную можно, задав для свойства IsDeterministic значение true.
Параметры, возвращающие табличные значения
Пример скалярной функции среды CLR
Ниже приводится простой пример скалярной функции, получающей доступ к данным и возвращающей целочисленное значение.
После сохранения в файле с именем FirstUdf.cs этот код можно скомпилировать в сборку следующим образом:
/t:library указывает, что результатом компиляции должна быть библиотека, а не исполняемый объект. Исполняемые объекты нельзя регистрировать в SQL Server.
Ниже приведены запрос Transact-SQL и образец вызова для регистрации сборки и определяемой пользователем функции.
Обратите внимание, что имя функции в Transact-SQL не обязательно должно соответствовать имени общего статического целевого метода.
В этой статье приводятся общие сведения о функции встраивания скалярных пользовательских функций, которая входит в состав набора функций интеллектуальной обработки запросов. Эта функция повышает производительность запросов, вызывающих скалярные пользовательские функции, в SQL Server (начиная с SQL Server 2019 (15.x)).
Скалярные пользовательские функции T-SQL
Пользовательские функции, которые реализованы на языке Transact-SQL и возвращают одно значение, называются скалярными пользовательскими функциями T-SQL. Это элегантное решение для обеспечения повторного использования и модульности кода запросов Transact-SQL. Некоторые вычисления (например, сложные бизнес-правила) проще выражать в императивной форме пользовательских функций. Такие функции позволяют конструировать комплексную логику, не имея опыта в написании сложных SQL-запросов. Дополнительные сведения о пользовательских функциях см. в разделе Создание определяемых пользователем функций (ядро СУБД).
Производительность скалярных пользовательских функций
Как правило, производительность скалярных пользовательских функций оказывается невысокой по указанным ниже причинам:
Итеративные вызовы. Пользовательские функции вызываются итеративно — однократно в соответствующем кортеже. Постоянные переключения контекста при вызове функций требуют дополнительных ресурсов. Особенно это проявляется в случаях, когда запросы Transact-SQL выполняются в определении пользовательской функции.
Отсутствие оценки затрат. Во время оптимизации оцениваются только реляционные операторы, но не скалярные. До появления скалярных пользовательских функций скалярные операторы, как правило, были нетребовательны к ресурсам и не нуждались в оценке. Достаточно было учитывать небольшое увеличение загрузки ЦП. Ниже представлены сценарии, в которых фактические затраты значительны, но по-прежнему не принимаются во внимание в полной мере.
Выполнение, ориентированное на интерпретацию. Пользовательские функции оцениваются как пакеты инструкций, но инструкции выполняются поочередно. Каждая инструкция компилируется отдельно, а затем скомпилированный план кэшируется. Хотя такая стратегия кэширования позволяет избежать повторной компиляции и немного сэкономить время, каждая инструкция выполняется изолированно. Перекрестная оптимизация инструкций не производится.
Последовательное выполнение. В SQL Server не допускается параллелизм внутри запросов, вызывающих пользовательские функции.
Автоматическое встраивание скалярных пользовательских функций
Цель встраивания скалярных пользовательских функций заключается в повышении производительности запросов, которые вызывают скалярные пользовательские функции T-SQL, являющиеся основным узким местом.
Эта новая функция автоматически преобразует скалярные пользовательские функции в скалярные выражения или скалярные вложенные запросы, которые подставляются в вызывающий запрос вместо оператора пользовательской функции. Затем выражения и вложенные запросы оптимизируются. В итоге в плане запроса не будет оператора пользовательской функции, но логика функции сохраняется в виде представлений или встроенных функций с табличными значениями.
Пример 1. Скалярная пользовательская функция с одной инструкцией
Обратите внимание на следующий запрос.
Теперь запрос можно изменить так, чтобы в нем вызывалась эта пользовательская функция.
По изложенным выше причинам запрос с пользовательской функцией выполняется медленно. Однако благодаря встраиванию скалярных пользовательских функций скалярное выражение из тела пользовательской функции подставляется непосредственно в запрос. Результаты выполнения этого запроса показаны в приведенной ниже таблице.
Запрос:
Запрос без пользовательской функции
Запрос с пользовательской функцией (без встраивания)
Запрос со встраиванием скалярной пользовательской функции
Время выполнения:
1,6 секунды
29 минут 11 секунд
1,6 секунды
Эти показатели получены для базы данных CCI размером 10 ГБ (использующей схему TPC-H), которая размещена на компьютере с двумя процессорами (12 ядер), 96 ГБ ОЗУ и дисками SSD. В них было учтено время компиляции и выполнения с холодным кэшем процедур и буферным пулом. Использовалась конфигурация по умолчанию. Другие индексы не создавались.
Пример 2. Скалярная пользовательская функция с несколькими инструкциями
Теперь рассмотрим запрос, вызывающий эту пользовательскую функцию.
План выполнения для этого запроса в SQL Server 2017 (14.x); (уровень совместимости 140 и ниже) выглядит так:
Как видно из плана, в SQL Server применяется простая стратегия: для каждого кортежа в таблице CUSTOMER вызывается пользовательская функция и выводятся результаты. Такой подход примитивен и неэффективен. Благодаря встраиванию подобные пользовательские функции преобразуются в эквивалентные скалярные вложенные запросы, которые подставляются в вызывающий запрос вместо пользовательской функции.
Для этого же запроса план со встроенной пользовательской функцией выглядит так:
Как уже говорилось, в плане запроса теперь нет оператора пользовательской функции, но логика функции сохраняется в виде представлений или встроенных функций с табличными значениями. Изучив план, можно заметить следующее.
В зависимости от сложности логики в пользовательской функции план запроса также может быть больше и сложнее. Как видите, операции внутри пользовательской функции теперь прозрачны, поэтому оптимизатор запросов может оценить затраты и оптимизировать эти операции. Кроме того, так как в плане больше нет пользовательской функции, полностью устраняются накладные расходы, связанные с ее итеративными вызовами.
Требования к встраиваемым скалярным пользовательским функциям
Скалярную пользовательскую функцию T-SQL можно встраивать, если выполняются все перечисленные ниже условия:
1 Встраивание инструкций SELECT с накоплением или агрегированием переменных (например, SELECT @val += col1 FROM table1 ) не поддерживается.
2 Рекурсивные пользовательские функции встраиваются только до определенной глубины.
3 Зависимыми от времени являются такие встроенные функции, результаты которых зависят от текущего системного времени. Примером встроенной функции с побочными эффектами служит функция, которая может изменять некоторое внутреннее глобальное состояние. Такие функции возвращают разные результаты в зависимости от внутреннего состояния.
4 Ограничение добавлено в накопительный пакет обновления 2 для SQL Server 2019 (15.x)
5 Ограничение добавлено в накопительный пакет обновления 4 для SQL Server 2019 (15.x)
6 Ограничение добавлено в накопительный пакет обновления 5 для SQL Server 2019 (15.x)
7 Ограничение добавлено в накопительном пакете обновления 6 для SQL Server 2019 (15.x)
8 Ограничение добавлено в накопительном пакете обновления 11 для SQL Server 2019 (15.x)
Сведения о последних исправлениях и изменениях встраивания скалярных пользовательских функций T-SQL см. в статье базы знаний Устранение проблем встраивания скалярных пользовательских функций в SQL Server 2019.
Проверка возможности встраивания пользовательской функции
Свойство is_inlineable является производным от конструкций в определении пользовательской функции. При этом оно не проверяет, является ли пользовательская функция фактически встраиваемой во время компиляции. Дополнительные сведения см. в разделе Условия встраивания.
Значение 1 указывает, что она встраиваемая, а значение 0 — не встраиваемая. Это свойство также имеет значение 1 для всех встроенных функций с табличными значениями. Для остальных модулей значение будет равно 0.
Если скалярная пользовательская функция является встраиваемой, это не означает, что она обязательно будет встроенной. Целесообразность встраивания определяется в SQL Server для каждого конкретного запроса и каждой пользовательской функции. Ниже приведены примеры ситуаций, в которых невозможно встраивание пользовательских функций:
Если определение пользовательской функции состоит из тысяч строк кода, возможно, SQL Server не будет встраивать ее.
Пользовательская функция подписана с помощью сертификата. Поскольку подписи могут добавляться и удаляться после создания пользовательской функции, решение о возможности встраивания принимается при компиляции запроса, ссылающегося на скалярную пользовательскую функцию. Например, большинство системных функций подписано с помощью сертификатов. Для определения подписанных объектов можно использовать sys.crypt_properties.
Проверка применения встраивания
Если все предварительные условия соблюдены и сервер SQL Server решает выполнить встраивание, пользовательская функция преобразуется в реляционное выражение. По плану запроса можно легко выяснить, произошло ли встраивание.
Включение встраивания скалярных пользовательских функций
Рабочие нагрузки можно автоматически сделать подходящими для встраивания скалярных пользовательских функций, включив для базы данных уровень совместимости 150. Для этого можно использовать Transact-SQL. Пример:
Для использования этой функции других изменений в пользовательские функции или запросы вносить не требуется.
Отключение встраивания скалярных пользовательских функций без изменения уровня совместимости
Встраивание скалярных пользовательских функций можно отключить в области базы данных, инструкции или пользовательской функции, сохранив уровень совместимости базы данных 150 или более высокий. Чтобы отключить встраивание в области базы данных, выполните следующую инструкцию в контексте соответствующей базы данных.
Чтобы снова включить встраивание для базы данных, выполните следующую инструкцию в контексте соответствующей базы данных:
Указание запроса USE HINT имеет приоритет над конфигурацией, областью действия которой является база данных, или уровнем совместимости.
После выполнения этой инструкции данная пользовательская функция никогда не будет встраиваться в вызывающие ее запросы. Чтобы повторно включить встраивание для пользовательской функции, выполните следующую инструкцию.
Важные примечания
Как уже говорилось в этой статье, при встраивании скалярных пользовательских функций запрос с такими функциями преобразуется в запрос с эквивалентными скалярными вложенными запросами. Из-за этого пользователи могут заметить отличия в поведении в описанных ниже ситуациях.