Язык Celesta-SQL
1. Язык CelestaSQL определения объектов базы данных
Скрипты на языке CelestaSQL должны иметь кодировку UTF-8. |
На языке Celesta SQL пишутся скрипты определения гранул. Скрипт на языке CelestaSQL состоит из конструкций
в обязательном порядке разделённых точкой с запятой, начиная с CREATE GRAIN
:
2. Комментарии в языке CelestaSQL
CelestaSQL поддерживает стандартные однострочные и многострочные комментарии, а также комментарии CelestaDoc:
-- однострочный комментарий
/*многострочный
комментарий*/
/**комментарий CelestaDoc*/
Обычные комментарии могут использоваться в любом месте текста, комментарии CelestaDoc — только непосредственно перед определением гранулы, таблицы, поля или индекса.
3. Идентификаторы объектов в языке CelestaSQL
Идентификаторы объектов — это, иначе говоря, имена гранул, таблиц, полей, индексов, ограничений и представлений, их синтаксис в Celesta имеет ряд строгих ограничений.
-
В обычных СУБД дозволяется, чтобы идентификаторы объектов содержали знаки пробела и неалфавитные знаки, если эти имена заключены в обрамляющие символы, например «[]» для MS SQL Server. Т. е. в MS SQLServer допустимо, например, такое имя таблицы: [Long Table$Name]. Celesta, однако, не может поддерживать пробелы и неалфавитные знаки в идентификаторах потому, что имя каждой Celesta-таблицы должно являться именем Java-класса, а имя каждого Celesta-поля — именем Java-переменной. Поэтому идентификатор любого именованного объекта CelestaSQL должен как минимум удовлетворять правилам наименования переменной в Java, т. е. может состоять только из больших и малых букв латинского алфавита, цифр и знаков подчёркивания, при этом не может начинаться с цифры.
-
Заключение идентификаторов Celesta в кавычки в скриптах CelestaSQL не допускается на уровне синтаксиса, т. к. практической надобности в этом нет (имена никогда не содержат пробелов). На системном уровне при формировании запросов к СУБД Celesta, однако, всегда заключает имена своих объектов в прямые кавычки ("ANSI quotes"), чтобы гарантировать, что базы данных Oracle, PostgreSQL и H2 не будут нарушать регистр букв, составляющих идентификатор.
-
Идентификаторы в Celesta являются чувствительными к регистру, однако нельзя создавать две таблицы, имена которых отличаются только регистром.
-
Длина любого идентификатора в Celesta не может быть больше 30 символов.
-
Идентификаторы (префиксы) гранул, кроме того, не могут содержать в себе знаков подчёркивания. Это связано с тем, что комбинация имени гранулы со знаком подчёркивания и другим идентификатором зачастую используется во внутрисистемных целях Celesta, и запрет на использование знака подчёркивания в именах гранул необходим для исключения возможных неоднозначностей.
Обычной практикой является написание идентификаторов в CelestaSQL в "snake_case" — в процессе трансформации в Java-классы они конвертируются в 'CamelCase'. |
4. Конструкция CREATE SCHEMA (GRAIN)
С выражения CREATE SCHEMA
должен начинаться любой скрипт определения гранулы.
Используется следующий синтаксис (слова GRAIN
и SCHEMA
являются синонимами):
Опция WITH NO AUTOUPDATE
исключает схему целиком из процесса автообновления базы данных.
Как и соответствующая опция таблицы, может быть использована в ситуации, когда структура схемы находится под внешним управлением.
Указание версии обязательно для исключения непроизвольного автоматического даунгрейда базы данных при запуске старой версии гранулы на более свежей версии базы данных.
В этом смысле, по отношению к тэгу «1.23,TITAN3.34»:
-
«1.23,TITAN3.35» – более свежая версия (обновилась модификация), можно выполнять автоапгрейд
-
«1.24,TITAN3.34» – более свежая версия (обновилась базовая версия), можно выполнять автоапгрейд
-
«1.23,TITAN3.34,PLUTO1.00» – более свежая версия (добавилась ещё одна модификация), можно выполнять автоапгрейд
-
«TITAN3.34,1.23» – та же самая версия (порядок следования тэгов не играет роли), автоапгрейд выполняться будет лишь при несовпадении контрольных сумм, ошибки не произойдёт
-
«1.22,TITAN3.34» – более старая базовая версия, автоапгрейд выполняться не будет, произойдёт ошибка и Celesta остановится.
-
«1.22,TITAN3.36» – несогласующаяся версия, апдейт выполняться не будет, ошибка. Версии «1.23,PLUTO1.00», «1.25» также будут несогласующимся с версией «1.23,TITAN3.34» и не станут накатываться автоматически.
Каждая из версий сравнивается, как число с плавающей запятой.
5. Конструкция CREATE SEQUENCE
Используется следующий синтаксис:
Основными ограничениями и отличиями этой конструкции от аналогичной конструкции в различных СУБД являются:
-
Если не указано значение
MINVALUE
, то оно автоматически выставляется равным значениюSTART WITH
(по умолчанию 1). -
Если не указано значение
MAXVALUE
, то оно автоматически выставляется равным значениюLong.MAX_VALUE
. -
Значение
START WITH
не обновляется для созданных ранее последовательностей, даже если было изменено в файле гранулы (связано с тем, что Oracle разрешает данную операцию только через удаление и пересоздание последовательности, а идеология Celesta не предусматривает удаление stateful объектов из БД). -
Если в грануле присутствует таблица с именем, например,
A
, то является недопустимым создание последовательности с именемA_seq
, так как это имя зарезервировано системой.
6. Конструкция CREATE TABLE
Используется следующий синтаксис:
Иными словами, в конструкции CREATE TABLE
в скобках через запятую в любом порядке могут быть перечислены определения полей, определения первичных ключей или определения внешних ключей, а за скобками, возможно, перечислены опции.
На практике определения всех полей идут подряд в самом начале, далее — определения составных первичных ключей (первичные ключи, состоящие из одного поля, можно определять на самом поле) и определения составных внешний ключей (опять же, внешние ключи, состоящие из одного поля, можно определять на самом поле).
6.1. Определения полей
field_definition (Определение поля) — конструкция, задающая тип поля, название поля, его свойства NULL
/NOT NULL
и DEFAULT
, опционально может заканчиваться конструкцией PRIMARY KEY
и/или FOREIGN KEY
.
Сокращенное определение внешнего ключа (inline_fk_definition) имеет следующий синтаксис:
Здесь table_ref — ссылка на таблицу, которая может быть однокомпонентной (если таблица, на которую указывает внешний ключ, находится в текущей грануле) или двухкомпонентной, с явным указанием имени гранулы:
Определение синтаксиса правил внешних ключей (fk_rules) см. в разделе «Внешние ключи».
6.2. Типы данных
Используется следующая система типов данных.
Celesta | Microsoft SQL Server | Oracle | PostgreSQL | Firebird | H2 | Java type for cursor | |
---|---|---|---|---|---|---|---|
Integer (32-bit) |
INT |
INT |
NUMBER |
INT4 |
INTEGER |
INTEGER |
java.lang.Integer |
Floating point (64-bit) |
REAL |
FLOAT(53) |
REAL |
FLOAT8 [= DOUBLE PRECISION] |
DOUBLE PRECISION |
DOUBLE |
ava.lang.Double |
Fixed point (decimal) |
DECIMAL(p,s) |
DECIMAL(p,s) |
NUMBER(p,s) |
NUMERIC(p,s) |
DECIMAL(p,s) |
DECIMAL(p,s) |
java.math.BigDecimal |
String (Unicode) |
VARCHAR(n) |
NVARCHAR(n) |
NVARCHAR(n) |
VARCHAR(n) |
VARCHAR(n) |
VARCHAR(n) |
java.lang.String |
Long string (Unicode) |
TEXT |
NVARCHAR(MAX) |
NCLOB |
TEXT |
BLOB SUB_TYPE TEXT |
CLOB |
java.lang.String |
Binary |
BLOB |
VARBINARY(MAX) |
BLOB |
BYTEA |
BLOB |
VARBINARY(MAX) |
java.lang.String |
Date/time |
DATETIME |
DATETIME |
TIMESTAMP |
TIMESTAMP |
TIMESTAMP |
TIMESTAMP |
java.util.Date |
Date/time with time zone |
DATETIME WITH TIME ZONE |
DATETIMEOFFSET |
TIMESTAMP WITH TIME ZONE |
TIMESTAMPZ |
TIMESTAMP WITH TIME ZONE |
TIMESTAMP WITH TIME ZONE |
java.time.ZonedDateTime |
Boolean |
BIT |
BIT |
NUMBER [check in (0, 1)] |
BOOL |
SMALLINT [check in (0, 1)] |
BOOLEAN |
java.lang.Boolean |
См. также раздел «Особенности работы Celesta с поддерживаемыми типами СУБД».
Для каждого типа поля имеется свой вариант определения:
Вместо конструкции DEFAULT <целое число>
для поля с типом INT
может использоваться конструкция NEXTVAL(<sequence name>)
.
Таким образом значение колонки будет инкрементироваться при вставке в зависимости от указанной последовательности.
Стоит отметить, что можно использовать только последовательности, объявленные в той же грануле, что и таблица, к которой принадлежит колонка.
Здесь <binary literal> — шестнадцатеричное представление последовательности байтов, начинающееся с 0x и не заключённые в кавычки, например: 0xFFAAFFAAFF.
В качестве значения DEFAULT
для поля с типом DATETIME
может использоваться функция GETDATE()
(текущий момент времени).
6.3. Первичные ключи
primary_key_definition (определение первичного ключа) — конструкция, задающая состав полей, входящих в первичный ключ таблицы. Возможно в двух вариантах:
-
сокращённый вариант — когда ключевые слова “
PRIMARY KEY
” идут сразу после определения поля, это даёт возможность более короткой и наглядной записи в случае, когда первичный ключ состоит всего из одного поля, -
полный вариант — когда конструкция
PRIMARY KEY
находится в определении таблицы среди определения полей, и может содержать как одно поле, так и любое количество полей.
Ограничения:
-
Не допускается создание таблиц без
PRIMARY KEY
(за исключениемREAD ONLY
-таблиц). Это необходимо для работоспособности классов доступа к данным. -
В таблице может быть не более одного упоминания
PRIMARY KEY
, будь то сокращённое выражение в конце определения поля или составное выражение в определении таблицы. -
Не допускается создание PK по полям с типами
BLOB
иTEXT
. -
Не допускается создание PK по nullable-полям.
-
Не допускается более одного вхождения одного и того же поля в определение PRIMARY KEY.
6.4. Внешние ключи
foreign_key_definition (Определение внешнего ключа) — конструкция, задающая связь между таблицами по одному или нескольким полям.
Как и в случае с конструкцией PRIMARY KEY
, возможно использование в двух вариантах — сокращённом (встроенном в определение поля, связь по одному полю) и полном (перечисляется среди определений полей).
Кроме того, внешний ключ можно создать вне определения таблицы при помощи конструкции "alter table add constraint":
Ограничения:
-
Foreign key, простой или составной, может ссылаться только на Primary Key, причём полностью. (В Celesta SQL не допускаются ограничения
UNIQUE
, поэтому из двух возможностей, стандартно предлагаемых в СУБД для Foreign Keys, остаётся только возможность ссылки на Primary Key). -
Типы полей должны в точности совпадать (если поле – строковое, длина ссылающегося поля должна быть точно равна длине поля, на которое ссылаются).
-
Нельзя создать более одного определения Foreign Key на одном и том же наборе столбцов (частный случай – не может быть двух FK, определённых для одного и того же столбца).
fk_rules — ссылочные действия:
Поддерживаемые ссылочные действия:
-
NO ACTION
– запрет удаления/модификации родительской записи, если на неё есть ссылки. -
SET NULL
– выставлениеNULL
в ссылках. Естественно, использовать это действие запрещено дляNOT NULL
-able полей. -
CASCADE
– каскадное удаление/обновление полей.
В момент создания foreign key сослаться можно только
-
на таблицу, определённую в текущей грануле выше по тексту,
-
на таблицу, определённую с другой грануле.
«Зацикленные» ссылки по foreign key (например, вида A→B→C→A) при разработке структуры базы данных в действительности нужны крайне редко и обычно говорят об ошибке проектировщика. Единственный широко применимый практически значимый пример «зацикливания» — ссылка таблицы на саму себя при организации иерархического перечня по принципу parent-child. |
В Celesta не допускается создание «зацикленных» ссылок между таблицами, принадлежащими разным гранулам. Если надо организовать ссылки по кругу между несколькими таблицами внутри гранулы, то это можно сделать, воспользовавшись конструкцией "alter table add constraint foreign key".
В частности, работает следующий пример:
CREATE TABLE aa(idaa INT NOT NULL PRIMARY KEY, idc INT , textvalue nvarchar(10));
CREATE TABLE a (ida INT NOT NULL PRIMARY KEY, descr nvarchar(2), parent INT
FOREIGN KEY REFERENCES a(ida), --ссылка таблицы на саму себя
fff INT FOREIGN KEY REFERENCES aa(idaa) --первая часть круговой ссылки
внешний ключ, создаваемый вне таблицы:
ALTER TABLE aa ADD CONSTRAINT fk1
FOREIGN KEY (idc) REFERENCES a(ida); --вторая часть круговой ссылки
Пример создания составного ключа, состоящего из двух полей:
CREATE TABLE empfiles(
id INT NOT NULL PRIMARY KEY,
user_id varchar(200) NOT NULL,
orgtype_id varchar(255) NOT NULL,
question_id varchar(30) NOT NULL,
FOREIGN KEY (orgtype_id, question_id) REFERENCES schema.table(field, field)
6.5. Опции
Celesta позволяет указывать следующие опции после определения таблицы:
Таким образом, поддерживаются следующие возможности:
-
WITH VERSION CHECK
— режим по умолчанию: включение механизма отслеживания версий записей для исключения потерянных обновлений (см. раздел «Защита от потерянных обновлений»). Указывать данную опцию явно не требуется. -
WITH NO VERSION CHECK
— отключение механизма отслеживания версий записей. Требуется в случаях, когда нет необходимости в защите от потерянных обновлений — например, в таблицах, используемых только для добавления записей. Возможность модифицировать данные в таблице при этой опции сохраняется, но возможны «потерянные обновления». -
WITH READ ONLY
— режим «только чтение». Требуется в случаях, когда данные таблицы поступают из внешних источников, а не заносятся средствами Celesta, либо если необходимо подключиться к таблице, относящейся к иному приложению, и потому нежелательно вносить в её данные какие-либо изменения. В этом режиме механизм отслеживания версий записей отключается, а класс для доступа к таблице генерируется без методов модификации данных. Кроме того, для таких таблиц не требуется указывать первичный ключ. -
Опция
NO AUTOUPDATE
, которая может применяться совместно с данными опциями, отключает таблицу от процесса автообновления базы данных. Используется в случае, когда структура какой-либо таблицы изменяется в базе данных иными средствами и система не должна пытаться синхронизировать структуру этой таблицы с описанием на языке CelestaSQL автоматически.
7. Конструкция CREATE INDEX
Индексы применяются для ускорения фильтрации по полям таблиц и создаются при помощи следующей синтаксической конструкции:
Имена индексов должны быть уникальны в пределах гранулы. Все индексы в Celesta допускают повторяющиеся значения.
8. Конструкция CREATE VIEW
Представления (views) служат для доступа только на чтение к данным, собранным из одной или нескольких таблиц при помощи SQL-запроса SELECT
.
Для каждого своего представления Celesta создаёт объект-представление в базе данных, транслируя при этом SQL-запрос на языке CelestaSQL в соответствующий диалект языка SQL.
Представления создаются при помощи синтаксической конструкции
-
<view name> (имя представления) должно быть уникальным в пределах гранулы и не совпадать с именем таблицы.
-
query (запрос) представляет собой SQL-запрос, имеющий следующий синтаксис:
SELECT
-запросы в CelestaSQL могут быть объединены в цепочку при помощи UNION ALL
.
Как обычно, требуется, чтобы каждый из запросов в цепочке возвращал одинаковое количество столбцов, и столбцы были совпадающих типов.
В качестве названий столбцов, возвращаемых выражением UNION ALL
, принимаются названия столбцов первого SELECT
-запроса.
Nullability возвращаемых столбцов вычисляется по правилу: выражение столбца может возвращать null
, если хотя бы в одном из запросов в цепочке UNION ALL
соответствующий столбец может возвращать null
.
Основными ограничениями и отличиями такого запроса от SQL-запросов в различных СУБД являются:
-
Запросы строятся только на таблицах и материализованных представлениях (текущей гранулы или соседних гранул). Во избежание выстраивания неэффективных конструкций невозможно построение запросов на обычных представлениях.
-
Конструкция
SELECT *
не поддерживается, и всякое поле запроса, если только это не ссылка на поле таблицы с уникальным в рамках запроса именем, должно иметь определённый и уникальный псевдоним. Это нужно для возможности однозначного создания класса-курсора, поля которого соответствуют именам столбцов запроса. -
Не поддерживается конструкция
ORDER BY
, т. к. при необходимости определённым образом отсортировать результирующий набор следует воспользоваться методом orderBy(…) соответствующего курсора. -
Не поддерживается конструкция
GROUP BY
…HAVING
. -
Не поддерживаются
FULL JOIN
(объединенияLEFT
иRIGHT
joins) иCROSS JOIN
(декартовы произведения таблиц). -
Не поддерживается конструкция
WITH
и вложенные запросы.
Большая часть из функциональности, не поддерживаемой на уровне CelestaSQL, эффективно эмулируется в API классов доступа к данным.
Ссылка на таблицу (table_ref) имеет синтаксис
line(optx('/<grain name>', '.'), '/<table name>')
Имя гранулы указывать не обязательно, если таблица находится в той же грануле, что и текущее представление.
Терм, определяющий поле представления, имеет следующий синтаксис:
Над выражениям типа INT
и REAL
допустимы обычные арифметические операции с обычным приоритетом: максимальный приоритет у унарного минуса, далее — умножение и деление («*», «/»), далее — сложение и вычитание («+», «-»).
Над выражениями типа VARCHAR
допустима операция конкатенации «||», а также функции UPPER
и LOWER
, сводящие текст к верхнему и нижнему регистру, соответственно.
Операции над выражениями прочих типов недопустимы.
Ссылки на поля могут быть однокомпонентными (если они однозначно указывают на поле определённой таблицы) либо двухкомпонентными, в этом случае в качестве первой компоненты следует указывать псевдоним таблицы из конструкции FROM
либо — если явный псевдоним отсутствует — имя таблицы.
Специальный вид идентификатора <$param id> — знак $, за которым следует идентификатор — служит для ссылок на параметры функций.
Наконец, синтаксис логического выражения condition, используемого в конструкциях JOIN
… ON
и WHERE
:
Следует отметить, что term внутри конструкции SUM
должен являться числом.
Особенность конструкции GROUP BY
в CelestaSQL заключается в необходимости всегда перечислять в ней все неагрегатные колонки из выборки.
9. Конструкция CREATE MATERIALIZED VIEW
Материализованные представления (materialized views) служат для доступа только на чтение к агрегатным данным, собранным из одной таблицы и объединенным при помощи выражения GROUP BY
.
Для каждого своего материализованного представления Celesta создаёт таблицу в базе данных, модифицируемую триггерами базы данных при изменении родительской таблицы.
Материализованные представления создаются при помощи синтаксической конструкции
Основными ограничениями и отличиями такого запроса от SQL-запросов в различных СУБД являются:
-
Запросы строятся только на таблице текущей гранулы.
-
Конструкция
SELECT *
не поддерживается. -
В выборке обязательно должен участвовать хотя бы один агрегатный и хотя бы один неагрегатный столбец.
-
Неагрегатные колонки должны ссылаться на
NOT NULL
колонки родительской таблицы. -
Если в
GROUP BY
выражении участвует колонка типаDATETIME
, то ее значения будут округляться в точности до дня (часы, минуты и более точные измерения отсекаются). -
Из агрегирующих операций доступны только
SUM
иCOUNT
.
10. Конструкция CREATE FUNCTION
Данная конструкция определяет функции — они же параметризованные представления.
Они служат для доступа только на чтение к данным, собранным из одной или нескольких таблиц при помощи SQL-запроса SELECT
с учетом переданных параметров.
Функции создаются при помощи синтаксической конструкции
Параметры декларируются через запятую, с заданием имени и типа параметра:
Чтобы сослаться на параметр в выражении функции, необходимо перед именем параметра ставить знак $
(например, param1
будет фигурировать в выражении запроса как $param1
).
Основными ограничениями и отличиями такого запроса от SQL-запросов в различных СУБД являются:
-
Конструкция
SELECT *
не поддерживается. -
В объявлении обязательно должен иметься хотя бы один параметр.
-
Все объявленные параметры обязательно должны использоваться.
В остальном синтаксическое выражение аналогично обычному View.
Пример функции:
CREATE FUNCTION pView2(param int, param2 varchar) AS
select f1, f2, f3 from t1
where f2 = $param AND f3 = $param2;