Работа с SQL и Apache Calcite#

Конфигурация#

Режим Standalone#

При запуске кластера в режиме Standalone до запуска скрипта ignite.sh или ignite.bat переместите подкаталоги optional/ignite-calcite и optional/ignite-slf4j в каталог libs. В этом случае контент папки, в которой находится модуль, добавится к classpath.

Конфигурация Maven#

Если для управления зависимостями в вашем проекте вы используете Maven, добавьте следующую зависимость (замените параметр ${ignite.version} на необходимую вам версию DataGrid).

XML#
<dependency>
    <groupId>com.sbt.ignite</groupId>
    <artifactId>ignite-calcite</artifactId>
    <version>${ignite.version}</version>
</dependency>

Конфигурация SQL-движков#

Чтобы включить SQL-движок, явно добавьте экземпляр CalciteQueryEngineConfiguration в свойство SqlConfiguration.QueryEnginesConfiguration.

Ниже приведен пример конфигурации двух SQL-движков (H2 и Calcite), где движок на Calcite является движком по умолчанию:

<bean class="org.apache.ignite.configuration.IgniteConfiguration">
    <property name="sqlConfiguration">
        <bean class="org.apache.ignite.configuration.SqlConfiguration">
            <property name="queryEnginesConfiguration">
                <list>
                    <bean class="org.apache.ignite.indexing.IndexingQueryEngineConfiguration">
                        <property name="default" value="false"/>
                    </bean>
                    <bean class="org.apache.ignite.calcite.CalciteQueryEngineConfiguration">
                        <property name="default" value="true"/>
                    </bean>
                </list>
            </property>
        </bean>
    </property>
    ...
</bean>
IgniteConfiguration cfg = new IgniteConfiguration().setSqlConfiguration(
    new SqlConfiguration().setQueryEnginesConfiguration(
        new IndexingQueryEngineConfiguration(),
        new CalciteQueryEngineConfiguration().setDefault(true)
    )
);

Направление запросов в SQL-движок#

Обычно все запросы направляются в SQL-движок, сконфигурированный по умолчанию. Если в queryEnginesConfiguration сконфигурировано более одного движка, конкретный движок для исполнения отдельных запросов или для всего соединения можно выбрать при конфигурировании способа подключения к базе.

JDBC#

Используйте параметр queryEngine для выбора SQL-движка для JDBC-соединения.

Пример

jdbc:ignite:thin://xxx.x.x.x:10800?queryEngine=calcite

где queryEngine=calcite — используемый движок.

ODBC#

Для ODBC-соединения SQL-движок можно сконфигурировать при помощи свойства QUERY_ENGINE.

Пример

[IGNITE_CALCITE]
DRIVER={Apache Ignite};
SERVER=xxx.x.x.x;
PORT=10800;
SCHEMA=PUBLIC;
QUERY_ENGINE=CALCITE

Подсказка QUERY_ENGINE#

Используйте подсказку QUERY_ENGINE, чтобы выбрать определенный движок для отдельных запросов.

SQL#
SELECT /*+ QUERY_ENGINE('calcite') */ fld FROM table;

SQL Reference#

DDL#

DDL-команды (Data Definition Language, язык описания данных) совместимы со старым движком на основе H2.

DML#

Новый SQL-движок наследует в основном синтаксис DML (Data Manipulation Language, язык манипулирования данными) от фреймворка Apache Calcite framework.

В большинстве случаев синтаксис команд совместим со старым SQL-движком. Но существуют и некоторые различия между DML-диалектами в движке на основе H2 и движке на основе Calcite. Например, изменился синтаксис команды MERGE.

Для получения дополнительной информации обратитесь к SQL-справочнику Apache Calcite.

Поддерживаемые функции#

SQL-движок на основе Calcite на данный момент поддерживает пользовательские функции и операторы, которые описаны ниже.

Агрегатные функции:

Имя

Синтаксис

Описание

COUNT

COUNT([ALL | DISTINCT] value) или COUNT(*)

Возвращает количество входных строк или ненулевых (non-null) значений

SUM

SUM([ALL | DISTINCT] numeric)

Возвращает сумму числовых входных значений

AVG

AVG([ALL | DISTINCT] numeric)

Возвращает среднее значение числовых входных значений

MIN

MIN([ALL | DISTINCT] value)

Возвращает минимальное входное значение

MAX

MAX([ALL | DISTINCT] value)

Возвращает максимальное входное значение

ANY_VALUE

ANY_VALUE([ALL | DISTINCT] value)

Возвращает одно произвольное входное значение

LISTAGG

LISTAGG(value[, separator]) WITHIN GROUP (ORDER BY sortExpr)

Объединяет значения из группы в заданном порядке

GROUP_CONCAT

GROUP_CONCAT(value[, separator] [ORDER BY sortExpr])

Объединяет значения из группы, опционально используя разделитель и сортировку

STRING_AGG

STRING_AGG(value, separator [ORDER BY sortExpr])

Объединяет строковые значения из группы с помощью разделителя

ARRAY_AGG

ARRAY_AGG(value [ORDER BY sortExpr])

Собирает входные значения в массив

ARRAY_CONCAT_AGG

ARRAY_CONCAT_AGG(arrayValue [ORDER BY sortExpr])

Объединяет массивы из входных строк в один массив

EVERY

EVERY(condition)

Возвращает значение TRUE, если каждое входное условие возвращает значение TRUE

SOME

SOME(condition)

Возвращает значение TRUE, если хотя бы одно входное условие возвращает значение TRUE

BIT_AND

BIT_AND(integer)

Агрегирует целые числа с помощью побитового AND

BIT_OR

BIT_OR(integer)

Агрегирует целые числа с помощью побитового OR

BIT_XOR

BIT_XOR(integer)

Агрегирует целые числа с помощью побитового XOR

Конструкция FILTER в агрегатных функциях

aggregateFunction(…) FILTER (WHERE condition)

Применяет агрегатную функцию только к тем входным строкам, которые удовлетворяют условию

Строковые функции и предикаты:

Имя

Синтаксис

Описание

UPPER

UPPER(string)

Преобразует строку в верхний регистр

LOWER

LOWER(string)

Преобразует строку в нижний регистр

INITCAP

INITCAP(string)

Преобразует каждое слово так, чтобы первая буква была заглавной, а остальные — строчными

TO_BASE64

TO_BASE64(string)

Кодирует строку в формате Base64

FROM_BASE64

FROM_BASE64(string)

Декодирует строку из формата Base64

MD5

MD5(string)

Возвращает MD5-хеш строки в виде шестнадцатеричной строки

SHA1

SHA1(string)

Возвращает SHA-1-хеш строки в виде шестнадцатеричной строки

SUBSTRING

SUBSTRING(string FROM start [FOR length])

Возвращает подстроку, начиная с указанной позиции

LEFT

LEFT(string, length)

Возвращает указанное количество символов с левого края строки

RIGHT

RIGHT(string, length)

Возвращает указанное количество символов с правого края строки

REPLACE

REPLACE(string, search[, replacement])

Заменяет все вхождения искомой строки на указанную строку

TRANSLATE

TRANSLATE(string, fromString, toString)

Заменяет символы из одного набора соответствующими символами из другого набора

CHR

CHR(integer)

Возвращает символ, который соответствует указанному коду (code point)

CHAR_LENGTH

CHAR_LENGTH(string)

Возвращает количество символов в строке

CHARACTER_LENGTH

CHARACTER_LENGTH(string)

Эквивалентно CHAR_LENGTH

LENGTH

LENGTH(string)

Псевдоним для функции CHAR_LENGTH(string)

||

string || string

Объединяет две строки

CONCAT

CONCAT(string, string) или CONCAT(string[, string]…)

Объединяет строки

OVERLAY

OVERLAY(string1 PLACING string2 FROM start [FOR length])

Заменяет часть строки другой строкой

POSITION

POSITION(substring IN string [FROM start])

Возвращает позицию первого вхождения подстроки

ASCII

ASCII(string)

Возвращает код первого символа строки

REPEAT

REPEAT(string, count)

Возвращает строку, которая повторяется указанное количество раз

SPACE

SPACE(count)

Возвращает строку, которая состоит из указанного количества пробелов

STRCMP

STRCMP(string1, string2)

Сравнивает две строки и возвращает результат сравнения в виде целого числа

SOUNDEX

SOUNDEX(string)

Возвращает фонетическое представление строки

DIFFERENCE

DIFFERENCE(string1, string2)

Возвращает оценку сходства значений SOUNDEX двух строк

REVERSE

REVERSE(string)

Возвращает строку с символами в обратном порядке

TRIM

TRIM([{BOTH | LEADING | TRAILING} chars FROM] string)

Удаляет символы из начала, конца или с обеих сторон строки

LTRIM

LTRIM(string)

Удаляет пробелы в начале строки

RTRIM

RTRIM(string)

Удаляет пробелы в конце строки

LIKE

string LIKE pattern [ESCAPE escapeChar]

Проверяет, соответствует ли строка SQL-шаблону LIKE

SIMILAR TO

string SIMILAR TO pattern [ESCAPE escapeChar]

Проверяет, соответствует ли строка шаблону регулярного выражения SQL

Функции и операторы регулярных выражений:

Имя

Синтаксис

Описание

~

string ~ pattern

Проверяет, соответствует ли строка регулярному выражению POSIX с учетом регистра

~*

string ~* pattern

Проверяет, соответствует ли строка регулярному выражению POSIX без учета регистра

!~

string !~ pattern

Проверяет, что строка не соответствует регулярному выражению POSIX с учетом регистра

!~*

string !~* pattern

Проверяет, что строка не соответствует регулярному выражению POSIX без учета регистра

REGEXP_REPLACE

REGEXP_REPLACE(string, regexp, replacement[, position[, occurrence[, matchType]]])

Заменяет подстроки, которые соответствуют регулярному выражению

REGEXP_SUBSTR

REGEXP_SUBSTR(string, regexp[, position[, occurrence]])

Возвращает подстроку, которая соответствует регулярному выражению

Числовые и математические функции:

Имя

Синтаксис

Описание

MOD

MOD(numeric1, numeric2) or numeric1 % numeric2

Возвращает остаток от деления

BITAND

BITAND(integer1, integer2)

Возвращает результат побитовой операции AND двух целых чисел

BITOR

BITOR(integer1, integer2)

Возвращает результат побитовой операции OR двух целых чисел

BITXOR

BITXOR(integer1, integer2)

Возвращает результат побитовой операции XOR двух целых чисел

EXP

EXP(numeric)

Возвращает число Эйлера, возведенное в указанную степень

POWER

POWER(numeric1, numeric2)

Возвращает число, возведенное в степень другого числа

LN

LN(numeric)

Возвращает натуральный логарифм

LOG10

LOG10(numeric)

Возвращает десятичный логарифм

ABS

ABS(numeric)

Возвращает абсолютное значение (модуль числа)

RAND

RAND([seed])

Возвращает случайное значение типа double

RAND_INTEGER

RAND_INTEGER([seed,] bound)

Возвращает случайное целое число от 0 (включительно) до bound (исключая)

ACOS

ACOS(numeric)

Возвращает арккосинус

ACOSH

ACOSH(numeric)

Возвращает обратный гиперболический косинус

ASIN

ASIN(numeric)

Возвращает арксинус

ASINH

ASINH(numeric)

Возвращает обратный гиперболический синус

ATAN

ATAN(numeric)

Возвращает арктангенс

ATANH

ATANH(numeric)

Возвращает обратный гиперболический тангенс

ATAN2

ATAN2(numeric1, numeric2)

Возвращает угол, который был вычислен по прямоугольным координатам

SQRT

SQRT(numeric)

Возвращает квадратный корень

CBRT

CBRT(numeric)

Возвращает кубический корень

COS

COS(numeric)

Возвращает косинус

COSH

COSH(numeric)

Возвращает гиперболический косинус

COT

COT(numeric)

Возвращает котангенс

COTH

COTH(numeric)

Возвращает гиперболический котангенс

DEGREES

DEGREES(numeric)

Преобразует радианы в градусы

RADIANS

RADIANS(numeric)

Преобразует градусы в радианы

ROUND

ROUND(numeric[, scale])

Округляет число до указанной точности (scale)

SIGN

SIGN(numeric)

Возвращает знак числового значения

SIN

SIN(numeric)

Возвращает синус

SINH

SINH(numeric)

Возвращает гиперболический синус

TAN

TAN(numeric)

Возвращает тангенс

TANH

TANH(numeric)

Возвращает гиперболический тангенс

SEC

SEC(numeric)

Возвращает секанс

SECH

SECH(numeric)

Возвращает гиперболический секанс

CSC

CSC(numeric)

Возвращает косеканс

CSCH

CSCH(numeric)

Возвращает гиперболический косеканс

TRUNCATE

TRUNCATE(numeric[, scale])

Отсекает дробную часть числа до указанной точности (scale)

PI

PI

Возвращает приближенное значение числа пи

Функции даты и времени:

Имя

Синтаксис

Описание

EXTRACT

EXTRACT(timeUnit FROM datetime)

Возвращает указанное поле из значения даты, времени или временной метки (timestamp)

FLOOR

FLOOR(datetime TO timeUnit)

Округляет значение даты, времени или временной метки в меньшую сторону до указанной единицы

CEIL

CEIL(datetime TO timeUnit)

Округляет значение даты, времени или временной метки в большую сторону до указанной единицы

TIMESTAMPADD

TIMESTAMPADD(timeUnit, interval, datetime)

Добавляет интервал к временной метке

TIMESTAMPDIFF

TIMESTAMPDIFF(timeUnit, datetime1, datetime2)

Возвращает количество границ указанных единиц времени между двумя значениями datetime

LAST_DAY

LAST_DAY(date)

Возвращает дату последнего дня месяца

DAYNAME

DAYNAME(datetime)

Возвращает название дня недели

MONTHNAME

MONTHNAME(datetime)

Возвращает название месяца

DAYOFMONTH

DAYOFMONTH(datetime)

Возвращает день месяца

DAYOFWEEK

DAYOFWEEK(datetime)

Возвращает день недели

DAYOFYEAR

DAYOFYEAR(datetime)

Возвращает день года

YEAR

YEAR(datetime)

Возвращает год

QUARTER

QUARTER(datetime)

Возвращает квартал года

MONTH

MONTH(datetime)

Возвращает номер месяца

WEEK

WEEK(datetime)

Возвращает номер недели

HOUR

HOUR(datetime)

Возвращает час

MINUTE

MINUTE(datetime)

Возвращает минуту

SECOND

SECOND(datetime)

Возвращает секунду

TIMESTAMP_SECONDS

TIMESTAMP_SECONDS(integer)

Преобразует количество секунд с 00:00:00 01.01.1970 во временную метку

TIMESTAMP_MILLIS

TIMESTAMP_MILLIS(integer)

Преобразует количество миллисекунд с 00:00:00 01.01.1970 во временную метку

TIMESTAMP_MICROS

TIMESTAMP_MICROS(integer)

Преобразует количество микросекунд с 00:00:00 01.01.1970 во временную метку

UNIX_SECONDS

UNIX_SECONDS(timestamp)

Возвращает количество секунд с 00:00:00 01.01.1970

UNIX_MILLIS

UNIX_MILLIS(timestamp)

Возвращает количество миллисекунд с 00:00:00 01.01.1970

UNIX_MICROS

UNIX_MICROS(timestamp)

Возвращает количество микросекунд с 00:00:00 01.01.1970

UNIX_DATE

UNIX_DATE(date)

Возвращает количество дней с 01.01.1970

DATE_FROM_UNIX_DATE

DATE_FROM_UNIX_DATE(integer)

Преобразует количество дней с 01.01.1970 в дату

DATE

DATE(value) или DATE(year, month, day)

Преобразует значение в дату или создает дату из отдельных полей

TIME

TIME(value) или TIME(hour, minute, second)

Преобразует значение в время или создает время из отдельных полей

DATETIME

DATETIME(value) или DATETIME(year, month, day, hour, minute, second)

Преобразует значение во временную метку или создает ее из полей даты и времени

CURRENT_TIME

CURRENT_TIME

Возвращает текущее время

CURRENT_TIMESTAMP

CURRENT_TIMESTAMP

Возвращает текущую временную метку

CURRENT_DATE

CURRENT_DATE

Возвращает текущую дату

LOCALTIME

LOCALTIME

Возвращает текущее местное время

LOCALTIMESTAMP

LOCALTIMESTAMP

Возвращает текущую местную временную метку

JDBC-функции для экранирования текущего времени

{fn CURDATE()}, {fn CURTIME()}, {fn NOW()}

JDBC-псевдонимы экранирования для текущих даты, времени и временной метки

TO_CHAR

TO_CHAR(datetime, format)

Форматирует значение даты, времени или временной метки

TO_DATE

TO_DATE(string, format)

Преобразует строку в дату

TO_TIMESTAMP

TO_TIMESTAMP(string, format)

Преобразует строку во временную метку

XML-функции:

Имя

Синтаксис

Описание

EXTRACTVALUE

EXTRACTVALUE(xml, xpath)

Возвращает текст, который был выбран с помощью выражения XPath

XMLTRANSFORM

XMLTRANSFORM(xml, xslt)

Преобразует XML с помощью таблицы стилей XSLT

EXTRACT

"EXTRACT"(xml, xpath)

Возвращает фрагмент XML, который был выбран с помощью выражения XPath. Используйте кавычки в имени, если нужно отличить эту функцию от EXTRACT для даты/времени

EXISTSNODE

EXISTSNODE(xml, xpath)

Возвращает значение 1, если выражение XPath выбирает хотя бы один XML-узел. В противном случае возвращает значение 0

JSON-функции и предикаты:

Имя

Синтаксис

Описание

FORMAT JSON

value FORMAT JSON

Помечает значение как входные данные в JSON-формате

JSON_VALUE

JSON_VALUE(jsonValue, path)

Извлекает скалярное значение SQL из JSON с помощью выражения JSON-пути

JSON_QUERY

JSON_QUERY(jsonValue, path)

Извлекает JSON-объект или массив из JSON с помощью выражения JSON-пути

JSON_TYPE

JSON_TYPE(jsonValue)

Возвращает тип значения JSON

JSON_EXISTS

JSON_EXISTS(jsonValue, path)

Возвращает значение TRUE, если JSON соответствует указанному выражению пути

JSON_DEPTH

JSON_DEPTH(jsonValue)

Возвращает глубину вложенности значения JSON

JSON_KEYS

JSON_KEYS(jsonValue[, path])

Возвращает ключи JSON-объекта

JSON_PRETTY

JSON_PRETTY(jsonValue)

Возвращает JSON с форматированием (в удобочитаемом виде)

JSON_LENGTH

JSON_LENGTH(jsonValue[, path])

Возвращает длину значения JSON

JSON_REMOVE

JSON_REMOVE(jsonValue, path[, path]…)

Удаляет данные, которые были выбраны выражениями путей, и возвращает обновленный JSON

JSON_STORAGE_SIZE

JSON_STORAGE_SIZE(jsonValue)

Возвращает количество байт, которые используются бинарным представлением JSON

JSON_OBJECT

JSON_OBJECT(jsonKey : jsonValue[, jsonKey : jsonValue]…)

Создает JSON-объект из пар «ключ-значение»

JSON_ARRAY

JSON_ARRAY([jsonValue[, jsonValue]…])

Создает JSON-массив из значений

IS JSON

value IS [NOT] JSON [VALUE]

Проверяет, является ли значение JSON или нет

IS JSON OBJECT

value IS [NOT] JSON OBJECT

Проверяет, является ли значение JSON-объектом или нет

IS JSON ARRAY

value IS [NOT] JSON ARRAY

Проверяет, является ли значение JSON-массивом или нет

IS JSON SCALAR

value IS [NOT] JSON SCALAR

Проверяет, является ли значение скалярным значением JSON или нет

Функции и операторы коллекций:

Имя

Синтаксис

Описание

ARRAY

ARRAY[value[, value]…] или ARRAY(query)

Создает массив из значений или из результата запроса

MAP

MAP[key, value[, key, value]…] или MAP(query)

Создает карту (map) из пар «ключ-значение» или из результата запроса

ITEM

array[index] или map[key]

Возвращает элемент из массива или карты

CARDINALITY

CARDINALITY(collection)

Возвращает количество элементов в коллекции

IS EMPTY

collection IS EMPTY

Проверяет, является ли коллекция пустой (не содержит элементов)

IS NOT EMPTY

collection IS NOT EMPTY

Проверяет, содержит ли коллекция хотя бы один элемент

Другие функции и операторы:

Имя

Синтаксис

Описание

ROW

ROW(value[, value]…)

Создает строковое значение

CAST

CAST(value AS type)

Преобразует значение в указанный тип

Infix cast

value::type

Преобразует значение в указанный тип с помощью синтаксиса приведения PostgreSQL

TYPEOF

TYPEOF(value)

Возвращает тип значения в DataGrid SQL

COALESCE

COALESCE(value, value[, value]…)

Возвращает первое значение из списка, которое не является NULL

NVL

NVL(value1, value2)

Возвращает значение value1, если оно не является NULL. В противном случае возвращает значение value2

NULLIF

NULLIF(value1, value2)

Возвращает NULL, если значения равны. В противном случае возвращает значение value1

CASE

CASE WHEN condition THEN result [ELSE result] END

Возвращает результат, который был выбран на основе условий ветвления

DECODE

DECODE(value, search, result[, search, result]…[, default])

Сравнивает значение с искомыми значениями и возвращает соответствующий результат

LEAST

LEAST(value[, value]…)

Возвращает наименьшее значение из переданных аргументов

GREATEST

GREATEST(value[, value]…)

Возвращает наибольшее значение из переданных аргументов

COMPRESS

COMPRESS(string)

Сжимает строку и возвращает данные в бинарном виде

OCTET_LENGTH

OCTET_LENGTH(binary)

Возвращает количество байт в бинарных данных

QUERY_ENGINE

QUERY_ENGINE()

Возвращает имя движка запросов, который используется для выполнения данного запроса

SYSTEM_RANGE

TABLE(SYSTEM_RANGE(start, end[, increment]))

Возвращает таблицу с одним столбцом BIGINT с именем X и одной строкой для каждого значения в заданном диапазоне

Поддерживаемые типы данных#

Ниже приведены типы данных, поддерживаемые SQL-движком на основе Calcite:

Тип данных

Java класс

BOOLEAN

java.lang.Boolean

DECIMAL

java.math.BigDecimal

DOUBLE

java.lang.Double

REAL/FLOAT

java.lang.Float

INT

java.lang.Integer

BIGINT

java.lang.Long

SMALLINT

java.lang.Short

TINYINT

java.lang.Byte

CHAR/VARCHAR

java.lang.String

DATE

java.sql.Date

TIME

java.sql.Time

TIMESTAMP

java.sql.Timestamp

INTERVAL YEAR TO MONTH

java.time.Period

INTERVAL DAY TO SECOND

java.time.Duration

BINARY/VARBINARY

byte[]

UUID

java.util.UUID

OTHER

java.lang.Object

Оптимизация запросов с помощью подсказок (hints)#

Оптимизатор запросов делает все возможное, чтобы построить самый быстрый план выполнения. Однако существующий оптимизатор запросов не может быть одинаково эфффективным для всех возможных случаев. Пользователь больше знает о структуре данных, архитектуре приложения или распределении данных в кластере. Подсказки (hints) SQL могут помочь оптимизатору сделать оптимизацию более рациональной или построить план выполнения быстрее.

Примечание

Подсказки SQL необязательны для использования и могут быть пропущены в некоторых случаях.

Формат подсказок#

Подсказки SQL задаются специальным комментарием /*+ HINT */, называемым блоком подсказки. Пробелы перед и после имени подсказки обязательны. Блок подсказки размещается сразу после реляционного оператора, чаще всего после SELECT. Несколько блоков подсказок для одного реляционного оператора не допускаются.

Пример

SQL#
SELECT /*+ NO_INDEX */ T1.* FROM TBL1 where T1.V1=? and T1.V2=?

Допускается задавать несколько подсказок для одного и того же реляционного оператора. Чтобы использовать несколько подсказок, разделите их запятыми (пробелы необязательны).

Пример

SQL#
SELECT /*+ NO_INDEX, EXPAND_DISTINCT_AGG */ SUM(DISTINCT V1), AVG(DISTINCT V2) FROM TBL1 GROUP BY V3 WHERE V3=?

Параметры подсказок#

Параметры подсказки, если они требуются, помещаются в скобки после имени подсказки и разделяются запятыми.

Параметр подсказки может быть заключен в кавычки. Параметр в кавычках чувствителен к регистру. Параметры с кавычками и без кавычек не могут быть определены для одной и той же подсказки.

Пример

SQL#
SELECT /*+ FORCE_INDEX(TBL1_IDX2,TBL2_IDX1) */ T1.V1, T2.V1 FROM TBL1 T1, TBL2 T2 WHERE T1.V1 = T2.V1 AND T1.V2 > ? AND T2.V2 > ?;

SELECT /*+ FORCE_INDEX('TBL2_idx1') */ T1.V1, T2.V1 FROM TBL1 T1, TBL2 T2 WHERE T1.V1 = T2.V1 AND T1.V2 > ? AND T2.V2 > ?;

Области видимости подсказок#

Подсказки определяются для реляционного оператора, обычно для SELECT.

Большинство подсказок видны для своих реляционных операторов, для низлежащих операторов, запросов и подзапросов. Подсказки, определенные в подзапросе, видны только для этого подзапроса и его подзапросов. Подсказка не видна для вышележащего реляционного оператора, если она определена после него.

Пример

SQL#
SELECT /*+ NO_INDEX(TBL1_IDX2), FORCE_INDEX(TBL2_IDX2) */ T1.V1 FROM TBL1 T1 WHERE T1.V2 IN (SELECT T2.V2 FROM TBL2 T2 WHERE T2.V1=? AND T2.V2=?);

SELECT T1.V1 FROM TBL1 T1 WHERE T1.V2 IN (SELECT /*+ FORCE_INDEX(TBL2_IDX2) */ T2.V2 FROM TBL2 T2 WHERE T2.V1=? AND T2.V2=?);

Обратите внимание, что только первый запрос имеет подсказку в следующем случае:

SQL#
SELECT /*+ FORCE_INDEX */ V1 FROM TBL1 WHERE V1=? AND V2=?
UNION ALL
SELECT V1 FROM TBL1 WHERE V3>?

Исключение: подсказки уровня движка или оптимизатора, такие как DISABLE_RULE или QUERY_ENGINE, должны быть определены в начале запроса и относятся ко всему запросу.

Ошибки в подсказках#

Оптимизатор пытается применить каждую подсказку и ее параметры, если это возможно. Но он пропускает подсказку или параметр подсказки, если:

  • не существует такой поддерживаемой подсказки;

  • необходимые параметры подсказки не переданы;

  • параметры подсказки были переданы, но подсказка не поддерживает какой-либо параметр;

  • параметр подсказки неверен или ссылается на несуществующий объект, например, несуществующий индекс или таблицу;

  • текущая подсказка или текущие параметры несовместимы с предыдущими, например, принудительное использование и отключение одного и того же индекса.

Поддерживаемые подсказки#

FORCE_INDEX#

Принудительно заставляет использовать индексы для сканирования таблиц.

Параметры:

  • пусто — принудительное использование индексов для сканирования таблиц. Оптимизатор выберет любой доступный индекс;

  • одно имя индекса — оптимизатор использует указанный индекс;

  • несколько имен индексов (могут относиться к разным таблицам) — оптимизатор выберет указанные индексы при сканировании.

Пример

SQL#
SELECT /*+ FORCE_INDEX */ T1.* FROM TBL1 T1 WHERE T1.V1 = T2.V1 AND T1.V2 > ?;

SELECT /*+ FORCE_INDEX(TBL1_IDX2, TBL2_IDX1) */ T1.V1, T2.V1 FROM TBL1 T1, TBL2 T2 WHERE T1.V1 = T2.V1 AND T1.V2 > ? AND T2.V2 > ?;

NO_INDEX#

Отключает сканирование индексов.

Параметры:

  • пусто — не использовать индексы при сканировании таблиц. Оптимизатор отключит все индексы;

  • одно имя индекса — оптимизатор пропустит указанный индекс;

  • несколько имен индексов (могут относиться к разным таблицам) — оптимизатор пропустит указанные индексы при сканировании.

Пример

SQL#
SELECT /*+ NO_INDEX */ T1.* FROM TBL1 T1 WHERE T1.V1 = T2.V1 AND T1.V2 > ?;

SELECT /*+ NO_INDEX(TBL1_IDX2, TBL2_IDX1) */ T1.V1, T2.V1 FROM TBL1 T1, TBL2 T2 WHERE T1.V1 = T2.V1 AND T1.V2 > ? AND T2.V2 > ?;

ENFORCE_JOIN_ORDER#

Устанавливает порядок соединений (JOIN) в запросе. Ускоряет построение плана запроса, включающего много соединений.

Пример

SQL#
SELECT /*+ ENFORCE_JOIN_ORDER */ T1.V1, T2.V1, T2.V2, T3.V1, T3.V2, T3.V3 FROM TBL1 T1 JOIN TBL2 T2 ON T1.V3=T2.V1 JOIN TBL3 T3 ON T2.V3=T3.V1 AND T2.V2=T3.V2

SELECT t1.v1, t3.v2 FROM TBL1 t1 JOIN TBL3 t3 on t1.v3=t3.v3 WHERE t1.v2 in (SELECT /*+ ENFORCE_JOIN_ORDER */ t2.v2 FROM TBL2 t2 JOIN TBL3 t3 ON t2.v1=t3.v1)

EXPAND_DISTINCT_AGG#

Принудительно разворачивает несколько операций агрегации с ключевым словом DISTINCT через соединения (JOIN).

Пример

SQL#
SELECT /*+ EXPAND_DISTINCT_AGG */ SUM(DISTINCT V1), AVG(DISTINCT V2) FROM TBL1 GROUP BY V3

QUERY_ENGINE#

Выбор конкретного движка для выполнения отдельных запросов. Подсказка на уровне движка.

Параметры:

Требуется один параметр — имя движка.

Пример

SQL#
SELECT /*+ QUERY_ENGINE('calcite') */ V1 FROM TBL1

DISABLE_RULE#

Отключает определенные правила оптимизатора. Подсказка на уровне оптимизатора.

Параметры:

Одно или несколько правил оптимизатора для пропуска.

Пример

SQL#
SELECT /*+ DISABLE_RULE('MergeJoinConverter') */ T1.* FROM TBL1 T1 JOIN TBL2 T2 ON T1.V1=T2.V1 WHERE T2.V2=?

SQL-статистика#

DataGrid может вычислять статистику и использовать ее для построения оптимального плана выполнения SQL-запроса. На основании статистики оптимизатор решает, как он будет выполнять SQL-запрос, будет ли использоваться индекс, и если да — какой. Это позволяет значительно ускорить выполнение запроса.

Без статистики планировщик выполнения SQL-запросов пытается угадать избирательность условий запроса с помощью только общих эвристических методов. Чтобы получить более точные планы:

  1. Убедитесь, что статистика включена.

  2. Настройте сбор статистики для таблиц, которые участвуют в запросе. Подробный пример описан ниже в разделе «Получение наиболее эффективного плана выполнения с помощью статистики».

Статистика проверяется и обновляется каждый раз после выполнения одного из следующих действий:

  • запуск узла;

  • изменение топологии;

  • изменение конфигурации.

Узел проверяет разделы и собирает по каждому из них статистику, которую можно использовать для оптимизации SQL-запросов.

Включение статистики#

SQL-статистика включена по умолчанию. Статистика хранится локально, а параметры ее конфигурации — по всему кластеру. Чтобы просмотреть состояние использования статистики, выполните команду:

$ ./control.sh --property get --name 'statistics.usage.state'

Чтобы включить или отключить статистику при использовании кластера, выполните следующую команду и укажите значение ON, OFF или NO_UPDATE:

control.sh --property set --name 'statistics.usage.state' --val 'ON'

Можно задать значения по умолчанию для распределенных свойств (properties) на уровне конфигурации DataGrid. Эта функциональность будет полезной при использовании in-memory-кластеров.

Пример задания значения с помощью конфигурации DataGrid

XML#
    <bean class="org.apache.ignite.configuration.IgniteConfiguration">
        <property name="DistributedPropertiesDefaultValues">
            <map>
                <entry key="statistics.usage.state" value="ON"/>
            </map>
        </property>
    </bean>

Чтобы проверить значение после запуска, используйте statistics.usage.state — подробнее указано выше.

Пример вывода

Command [PROPERTY] started
Arguments: --property get --name statistics.usage.state
--------------------------------------------------------------------------------
statistics.usage.state = ON
Command [PROPERTY] finished with code: 0

Устаревание статистики#

У каждой партиции есть специальный счетчик для отслеживания общего количества измененных строк (добавленных, удаленных или обновленных). Если общее количество измененных строк превышает значение MAX_CHANGED_PARTITION_ROWS_PERCENT, партиция анализируется повторно. После этого узел заново собирает статистику.

Чтобы настроить параметр MAX_CHANGED_PARTITION_ROWS_PERCENT, повторно запустите команду ANALYZE с требуемым значением параметра. По умолчанию используется параметр DEFAULT_OBSOLESCENCE_MAX_PERCENT = 15. Эти параметры применяются ко всем указанным объектам.

Примечание

Поскольку статистические данные собираются с помощью полного сканирования каждой партиции, рекомендуется отключить функциональность устаревания статистики при работе с небольшим количеством изменяющихся строк. Это особенно актуально в случае работы с большими объемами данных, когда полное сканирование может привести к снижению производительности.

Когда данные меняются, статистику нужно пересобирать. Если сбор статистики включен, она будет пересобираться автоматически при достижении установленного MAX_CHANGED_PARTITION_ROWS_PERCENT (по умолчанию 15%).

Чтобы сэкономить ресурсы процессора (CPU) при отслеживании устаревания, используйте состояние NO_UPDATE:

control.sh --property set --name 'statistics.usage.state' --val 'NO_UPDATE'

Получение наиболее эффективного плана выполнения с помощью статистики#

Пример получения оптимизированного плана выполнения для базового запроса:

  1. Создайте таблицу и добавьте в нее данные:

    SQL#
    CREATE TABLE statistics_test(col1 int PRIMARY KEY, col2 varchar, col3 date);
    
    INSERT INTO statistics_test(col1, col2, col3) VALUES(1, 'val1', 'YYYY-MM-DD');
    INSERT INTO statistics_test(col1, col2, col3) VALUES(2, 'val2', 'YYYY-MM-DD');
    INSERT INTO statistics_test(col1, col2, col3) VALUES(3, 'val3', 'YYYY-MM-DD');
    INSERT INTO statistics_test(col1, col2, col3) VALUES(4, 'val4', 'YYYY-MM-DD');
    INSERT INTO statistics_test(col1, col2, col3) VALUES(5, 'val5', 'YYYY-MM-DD');
    INSERT INTO statistics_test(col1, col2, col3) VALUES(6, 'val6', 'YYYY-MM-DD');
    INSERT INTO statistics_test(col1, col2, col3) VALUES(7, 'val7', 'YYYY-MM-DD');
    INSERT INTO statistics_test(col1, col2, col3) VALUES(8, 'val8', 'YYYY-MM-DD');
    INSERT INTO statistics_test(col1, col2, col3) VALUES(9, 'val9', 'YYYY-MM-DD');
    
  2. Создайте индексы для каждого столбца:

    SQL#
    CREATE INDEX st_col1 ON statistics_test(col1);
    CREATE INDEX st_col2 ON statistics_test(col2);
    CREATE INDEX st_col3 ON statistics_test(col3);
    
  3. Получите план выполнения для базового запроса:

    Примечание

    Значение col2 меньше максимального значения в таблице, а значение col3 выше максимального. Весьма вероятно, что второе условие не вернет результат, что повышает его избирательность. Поэтому в базе данных следует использовать индекс st_col3.

    SQL#
    EXPLAIN SELECT * FROM statistics_test WHERE col2 > 'val2' AND col3 > 'YYYY-MM-DD'
    
    SELECT
    "__Z0"."COL1" AS "__C0_0",
    "__Z0"."COL2" AS "__C0_1",
    "__Z0"."COL3" AS "__C0_2"
    FROM "PUBLIC"."STATISTICS_TEST" "__Z0"
    /* PUBLIC.ST_COL2: COL2 > 'val2' */
    WHERE ("__Z0"."COL2" > 'val2')
    AND ("__Z0"."COL3" > DATE YYYY-MM-DD)
    

    Без собранной статистики в базе данных недостаточно информации для выбора правильного индекса (поскольку у обоих индексов одинаковая избирательность с точки зрения планировщика). Эта проблема устраняется в шагах ниже.

  4. Соберите статистику для таблицы statistics_test:

    SQL#
    ANALYZE statistics_test;
    
  5. Снова получите план выполнения и убедитесь, что выбран индекс st_col3:

    SQL#
    EXPLAIN SELECT * FROM statistics_test WHERE col2 > 'val2' AND col3 > 'YYYY-MM-DD'
    
    SELECT
    "__Z0"."COL1" AS "__C0_0",
    "__Z0"."COL2" AS "__C0_1",
    "__Z0"."COL3" AS "__C0_2"
    FROM "PUBLIC"."STATISTICS_TEST" "__Z0"
    /* PUBLIC.ST_COL3: COL3 > DATE ‘YYYY-MM-DD’ */
    WHERE ("__Z0"."COL2" > 'val2')
    AND ("__Z0"."COL3" > DATE YYYY-MM-DD)
    

Обновление статистики#

Собранные значения можно обновить — для этого укажите дополнительные параметры в команде ANALYZE. Указанные значения обновляют данные, которые собраны по одному на каждом узле в системном представлении STATISTICS_LOCAL_DATA (эти данные используются оптимизатором SQL-запросов), но не в STATISTICS_PARTITION_DATA (сохраняет реальную статистическую информацию по разделам). После этого оптимизатор SQL-запросов использует обновленные значения.

Подробнее о системных представлениях написано в разделе «События мониторинга»: STATISTICS_LOCAL_DATA и STATISTICS_PARTITION_DATA.

Каждая команда ANALYZE обновляет все такие значения для своих объектов. Например, если уже есть обновленное значение TOTAL и нужно обновить значение DISTINCT, используйте оба параметра в одной команде ANALYZE. Чтобы задать разные значения для разных столбцов, используйте несколько команд ANALYZE следующим образом:

SQL#
ANALYZE MY_TABLE(COL_A) WITH 'DISTINCT=5,NULLS=6';
ANALYZE MY_TABLE(COL_B) WITH 'DISTINCT=500,NULLS=1000,TOTAL=10000';

Квоты памяти#

SQL-движок на основе Apache Calcite может отслеживать и ограничивать объем heap-памяти, которая используется операторами выполнения запросов Calcite. Эта функциональность полезна для защиты серверного узла от одного большого запроса или от множества одновременно выполняющихся запросов с большим расходом памяти.

С помощью CalciteQueryEngineConfiguration можно настроить два типа квот:

Квота

Описание

Значение по умолчанию

Размерность

globalMemoryQuota

Квота heap-памяти на узел для всех SQL-запросов Calcite, которые выполняются на узле

0 (отключена)

Байт

queryMemoryQuota

Квота heap-памяти на узел для каждого отдельного SQL-запроса Calcite, который выполняется на узле

0 (отключена)

Байт

Если квота будет превышена, выполнение запроса прервется с ошибкой и сгенерируется исключение:

  • Global memory quota for SQL queries exceeded для глобальной квоты;

  • Query quota exceeded для квоты на отдельный запрос.

Квоты применяются к структурам выполнения запроса, которые хранят строки в heap-памяти, например:

  • сортировка;

  • хеш-соединения;

  • хеш-агрегации;

  • операции с множествами;

  • операции с коллекциями;

  • spool-операторы;

  • материализация результатов.

Квоты не ограничивают всю память процесса и не учитывают использование регионов данных DataGrid, прямой (off-heap) памяти, нативной памяти JVM и кеша страниц ОС. Квоты действуют на уровне каждого узла. При выполнении распределенного запроса общее потребление памяти в кластере может быть выше, так как лимит применяется независимо на каждом узле-участнике. Размер квот следует подбирать с учетом параметра -Xmx и ожидаемого количества одновременно выполняющихся SQL-запросов.

<bean class="org.apache.ignite.configuration.IgniteConfiguration">
    <property name="sqlConfiguration">
        <bean class="org.apache.ignite.configuration.SqlConfiguration">
            <property name="queryEnginesConfiguration">
                <list>
                    <bean class="org.apache.ignite.calcite.CalciteQueryEngineConfiguration">
                        <property name="default" value="true"/>
                        <property name="globalMemoryQuota" value="#{4L * 1024 * 1024 * 1024}"/>
                        <property name="queryMemoryQuota" value="#{512L * 1024 * 1024}"/>
                    </bean>
                </list>
            </property>
        </bean>
    </property>
</bean>
IgniteConfiguration cfg = new IgniteConfiguration().setSqlConfiguration(
    new SqlConfiguration().setQueryEnginesConfiguration(
        new CalciteQueryEngineConfiguration()
            .setDefault(true)
            .setGlobalMemoryQuota(4L * 1024 * 1024 * 1024)
            .setQueryMemoryQuota(512L * 1024 * 1024)
    )
);