Основи мови SQLScript
Основи мови SQLScript
Section titled “Основи мови SQLScript”SQLScript — мова програмування на стороні SAP HANA, розширення SQL. Те, що в ABAP називається AMDP, всередині написане саме на SQLScript. На відміну від звичайного SQL, тут є змінні, цикли, IF/ELSE, винятки, процедури і функції. Декларативна частина (SELECT, JOIN) майже така сама, як у SQL — імперативні розширення дають справжнє програмування на базі.
Ця сторінка — про базові елементи мови: синтаксис, типи літералів, змінні, оператори, NULL, модуляризацію в процедури/функції/бібліотеки. AMDP-обгортку з боку ABAP дивись на сторінці AMDP.
SQL vs SQLScript
Section titled “SQL vs SQLScript”| Критерій | SQL | SQLScript |
|---|---|---|
| Виконання | Окремі statement-и | Скрипт як ціле, в логічному контейнері |
| Змінні | Тільки host-параметри | Скалярні, табличні, масиви |
| Контроль потоку | Немає | IF/ELSE, FOR, WHILE, курсори |
| Винятки | Немає | DECLARE EXIT HANDLER, SIGNAL |
| Модуляризація | Немає | Procedures, functions, libraries |
| Призначення | CRUD | CRUD + бізнес-логіка на стороні БД |
SQLScript — суперсет: будь-який SQL-statement є валідним SQLScript-statement.
Statement-и, пропуски, коментарі
Section titled “Statement-и, пропуски, коментарі”Statement закінчується крапкою з комою (;). Пропуски, табуляції і переноси рядків між токенами ігноруються — форматуй як зручно.
-- Однорядковий коментар (HANA-стиль)
/* Багаторядковий коментар */
* В AMDP — також коментар (ABAP-стиль). При збереженні в БД* перетворюється на --. У "чистому" SQLScript не використовуй.Літерали
Section titled “Літерали”| Тип | Приклад | Дані |
|---|---|---|
| String (ASCII) | 'Jörg' | VARCHAR |
| String (Unicode) | N'Jörg' | NVARCHAR |
| Binary | x'FFF' | VARBINARY |
| Integer | -10 | INTEGER |
| Decimal | -1.2345 | DECIMAL |
| Float | -17.126E30 | DOUBLE |
| Hex | 0xFF | INTEGER |
| Date | DATE'2017-11-10' | DATE |
| Time | TIME'15:42:04.123' | TIME |
| Timestamp | TIMESTAMP'2011-12-31 23:59:59' | TIMESTAMP |
SELECT 'Jörg' AS string, N'Jörg' AS unicode, DATE'2017-11-10' AS d, TIMESTAMP'2011-12-31 23:59:59' AS tsFROM dummy;Без префікса DATE/TIME/TIMESTAMP дата у лапках — звичайний рядок, який буде неявно конвертований за потреби. Краще завжди ставити префікс.
Ідентифікатори
Section titled “Ідентифікатори”Дві нотації:
Simple notation — букви, цифри, _, #, $. Регістронезалежна — внутрішньо приводиться до верхнього регістру:
SELECT id, status, title FROM tasks; -- ID, STATUS, TITLE, TASKSSpecial notation — у подвійних лапках, дозволяє Unicode, пробіли, спецсимволи. Зберігає регістр:
CREATE TABLE id_with_space("ID" int, " ID" int, "ID " int);⚠️ Грабля: спеціальна нотація — пастка
У прикладі вище створено таблицю з трьома “однаковими” колонками —
ID,пробіл+ID,ID+пробіл. Синтаксично валідно, у виводі візуально неможливо розрізнити. Завжди використовуй simple notation, бажано в UPPER_CASE.
Змінні і параметри
Section titled “Змінні і параметри”В SQLScript змінні мають префікс : коли читаєш, без префікса коли пишеш:
DO BEGIN DECLARE lv_count INTEGER = 0;
lv_count = lv_count + 1; -- запис без : SELECT :lv_count FROM dummy; -- читання з :END;Параметри процедур/функцій працюють так само — :param_name у SQLScript-частині коду.
Системні змінні
Section titled “Системні змінні”Префікс :: (два двокрапки):
| Змінна | Що містить |
|---|---|
::CURRENT_OBJECT_NAME | Імʼя поточної процедури/функції (NULL у анонімних блоках) |
::CURRENT_OBJECT_SCHEMA | Schema поточного обʼєкта |
::ROWCOUNT | Кількість записів, оновлених останнім DML-statement |
::SQL_ERROR_CODE | Код помилки (доступна в EXIT HANDLER) |
::SQL_ERROR_MESSAGE | Текст помилки (доступна в EXIT HANDLER) |
::CURRENT_LINE_NUMBER | Номер рядка в коді (з SAP HANA 2.0 SPS 02) |
Оператори
Section titled “Оператори”Арифметичні: +, -, *, /. Унарний - — знак.
Рядкові: || — конкатенація.
Реляційні: =, != (або <>), <, >, <=, >=. Результат — TRUE, FALSE або UNKNOWN (якщо хоч один операнд NULL).
Логічні: AND, OR, NOT.
Пріоритет (зверху вниз): дужки → знак → * / → + - → || → реляційні → NOT → AND → OR.
⚠️ Грабля: NULL ламає логіку
Будь-який оператор з
NULL-операндом даєNULL(для логічних —UNKNOWN).WHERE x = NULLніколи не спрацює — потрібноWHERE x IS NULL. Це причина, чомуLEFT JOIN+WHERE right_table.field = 'X'несподівано фільтрує рядки без матчу — додавайOR right_table.field IS NULL.
Предикати
Section titled “Предикати”Використовуються в WHERE, HAVING, CASE — повертають TRUE/FALSE/UNKNOWN:
| Предикат | Призначення |
|---|---|
LIKE 'pattern' | Шаблон з % (будь-які символи) і _ (один символ) |
IN (val1, val2, ...) | Належність до списку |
BETWEEN a AND b | Включний діапазон |
IS NULL / IS NOT NULL | Перевірка NULL |
EXISTS (subquery) | Підзапит повернув хоча б один рядок |
CONTAINS(...) | Повнотекстовий пошук |
MEMBER OF (multiset) | Належність до multiset |
⚠️ Грабля: предикати в IF/WHILE
У імперативних
IFіWHILEне дозволеніCONTAINSіMEMBER OF.EXISTSіINдозволені тільки з SAP HANA 2.0 SPS 04. КванториALL,ANY,SOME— теж лише в SQL-частині.
Типи даних
Section titled “Типи даних”Скалярні — одне значення (INTEGER, NVARCHAR, DATE, DECIMAL…). Деталі — на сторінці Типи даних і функції SQLScript.
Табличні — потрібні для table variables і table parameters. Створюються трьома способами:
-- 1) З наявної таблиці БДDECLARE lt_tasks TABLE LIKE tasks;
-- 2) Через CREATE TYPE — окремий обʼєкт у каталозіCREATE TYPE tt_task AS TABLE (id INT, title NVARCHAR(100));DECLARE lt_tasks TABLE LIKE tt_task;
-- 3) Inline-визначення колонокDECLARE lt_tasks TABLE (id INT, title NVARCHAR(100));NULL — відсутність значення, не нуль і не порожній рядок. Окрема категорія з власною логікою:
SELECT 1 + NULL FROM dummy; -- NULL (не 1)SELECT 'X' || NULL FROM dummy; -- NULL (не 'X')SELECT CASE WHEN NULL = NULL THEN 'eq' ELSE 'ne' ENDFROM dummy; -- 'ne' (UNKNOWN ≠ TRUE)Оброблення NULL у виразах:
COALESCE(col, 0) -- перше не-NULL значенняIFNULL(col, 0) -- те саме для двох аргументівNULLIF(col, 0) -- NULL якщо col = 0DUMMY — системна таблиця з єдиним рядком (X). Використовується, коли потрібно виконати SELECT без реального джерела даних:
SELECT CURRENT_DATE, CURRENT_USER FROM dummy;SELECT 'Hello' || ' ' || 'World' FROM dummy;Альтернатива — взагалі без FROM:
SELECT CURRENT_DATE;Обидва варіанти працюють у HANA. FROM dummy — традиційна форма, успадкована з Oracle і часто зустрічається в legacy-коді.
Блок — це обгортка з BEGIN ... END навколо statement-ів. Може бути standalone, тілом процедури/функції або частиною контрольної конструкції:
-- Анонімний блок (виконується одразу в SQL Console)DO BEGIN DECLARE lv_x INTEGER = 42; SELECT :lv_x FROM dummy;END;
-- Тіло процедуриCREATE PROCEDURE my_proc ASBEGIN ...END;
-- Блок у IFIF :lv_x > 0 THEN BEGIN ... END;END IF;Структура блоку:
- Декларації (
DECLARE) - Обробники винятків (
DECLARE EXIT HANDLER) - Список statement-ів
Видимість змінних — у межах поточного блоку і його вкладених блоків.
Процедури
Section titled “Процедури”Процедура — іменована підпрограма, що зберігається в БД. Може мати будь-яку кількість IN/OUT/INOUT параметрів довільних типів, читати і змінювати дані:
CREATE PROCEDURE get_tasks_by_status ( IN iv_status INTEGER, OUT et_tasks TABLE (id INT, title NVARCHAR(100))) ASBEGIN et_tasks = SELECT id, title FROM tasks WHERE status = :iv_status;END;
-- ВикликCALL get_tasks_by_status ( iv_status => 3, et_tasks => ? -- ? — placeholder для виводу в SQL Console);User-defined functions
Section titled “User-defined functions”Дві форми залежно від типу повернення:
Table function — повертає таблицю:
CREATE FUNCTION udf_tasks_in_status (iv_status INTEGER)RETURNS TABLE (id INT, title NVARCHAR(100))AS BEGIN RETURN SELECT id, title FROM tasks WHERE status = :iv_status;END;
-- Виклик у FROM як таблицяSELECT * FROM udf_tasks_in_status(3);Scalar function — повертає одне значення:
CREATE FUNCTION udf_username (iv_id INTEGER)RETURNS NVARCHAR(100)AS BEGIN DECLARE lv_name NVARCHAR(100); SELECT firstname || ' ' || lastname INTO lv_name FROM users WHERE id = :iv_id; RETURN :lv_name;END;
-- Виклик у виразіSELECT udf_username(assignee) AS name FROM tasks;Procedure vs Function — коли що
Section titled “Procedure vs Function — коли що”| Критерій | Procedure | Table UDF | Scalar UDF |
|---|---|---|---|
| К-сть OUT-параметрів | Будь-яка | 1 (таблиця) | 1+ (скаляр) |
| Тип IN-параметрів | Скаляри і таблиці | Скаляри і таблиці | Тільки скаляри |
| Можна вживати як вираз | Ні | Як таблицю в FROM | Як скаляр у SELECT, WHERE |
| DML-операції | Так | Ні (тільки читання) | Ні (без table-statement-ів) |
| Виклик інших | Будь-яких | Functions + read-only proc | Тільки scalar UDF |
Правило вибору: якщо потрібно тільки читати і повертати значення/таблицю — UDF (зручніше використовувати у виразах). Якщо треба змінювати дані або складна логіка з кількома виходами — procedure.
⚠️ Грабля: scalar UDF — без таблиць
Scalar UDF не може містити table-statement-и. Тобто
SELECT ... INTO scalarдозволено, аSELECT ... INTO TABLE— ні. Якщо в ході роботи треба проміжна таблиця — переписуй у table UDF або procedure.
Бібліотеки
Section titled “Бібліотеки”Бібліотека — контейнер для повторно використовуваних процедур, функцій, змінних і констант. Створюється через CREATE LIBRARY:
CREATE LIBRARY math_utils LANGUAGE SQLSCRIPT ASBEGIN PUBLIC CONSTANT pi DECIMAL(10,8) = 3.14159265;
PUBLIC FUNCTION square (iv_x DECIMAL) RETURNS DECIMAL(20,4) AS BEGIN RETURN :iv_x * :iv_x; END;END;
-- Використання — з префіксом імені бібліотекиDO BEGIN USING math_utils AS m; SELECT m:square(5), m:pi FROM dummy;END;Видимість — PUBLIC (доступно зовні) або PRIVATE (тільки всередині бібліотеки). Бібліотеки доречні для DRY: спільні утиліти, константи, повторювані обчислення.
Анонімні блоки vs логічні контейнери
Section titled “Анонімні блоки vs логічні контейнери”Анонімний блок (DO BEGIN ... END) | Procedure / Function / Library |
|---|---|
| Виконується одразу в SQL Console | Зберігається в БД як обʼєкт |
| Без імені | Має імʼя і schema |
| Не можна викликати ззовні | Викликається з ABAP, інших procedure, SQL Console |
| Зручно для разових тестів | Для production-логіки |
Що далі
Section titled “Що далі”- Декларативне SQLScript: SELECT, JOIN, CTE — table variables, advanced SELECT, MAP_MERGE
- Типи даних і функції SQLScript — рядки, дати, числа, конвертації
- Імперативне SQLScript — змінні, цикли, курсори, винятки, динамічний SQL
- AMDP — обгортка з боку ABAP