CRUD у SQLScript: запис у БД
CRUD у SQLScript: запис у БД
Section titled “CRUD у SQLScript: запис у БД”Шість statement-ів змінюють дані в HANA: INSERT, UPDATE, UPSERT (= REPLACE), MERGE INTO, DELETE, TRUNCATE TABLE. Кожен має кілька форм — для одного рядка, для пакету, для запису з підзапиту, з умовою або без. Ця сторінка — швидкий лук-ап усіх форм з пастками, які ловлять у production.
Після кожного DML-statement-у:
::ROWCOUNT— кількість змінених рядківSQL_ERROR_CODE— код помилки (доступна в EXIT HANDLER)
INSERT
Section titled “INSERT”Один рядок
Section titled “Один рядок”-- Усі колонки за порядком оголошенняINSERT INTO tab_1 VALUES (1, 1, 'First row');INSERT INTO tab_1 VALUES (1, 2, 'Date: ' || CURRENT_DATE);
-- Окремий список колонок — інший порядок або частковий набірINSERT INTO tab_1 (key1, value1) VALUES (5, 'No key2 — буде NULL');Багато рядків
Section titled “Багато рядків”-- Список VALUESINSERT INTO tab_1 VALUES (1, 'A'), (2, 'B'), (3, 'C');
-- З SELECT-підзапитуINSERT INTO tab_archive SELECT * FROM tab_active WHERE status = 'CLOSED';
-- З table variableDO BEGIN lt_data = SELECT * FROM source_tab; INSERT INTO target_tab SELECT * FROM :lt_data;END;⚠️ Грабля: INSERT не оновлює
INSERTпадає на duplicate primary key. Якщо хочеш upsert-семантику (вставити або оновити) — використовуйUPSERTабоMERGE INTO. Не лови винятокunique constraint violatedяк спосіб реалізації upsert — це антипатерн, який вбиває продуктивність на великих обʼємах.
UPDATE
Section titled “UPDATE”Простий UPDATE
Section titled “Простий UPDATE”UPDATE tab_2 SET value1 = 'Updated', value2 = 'Line ' || key1 -- вираз, що використовує існуюче значення WHERE key1 < 300;SET приймає будь-який вираз — на основі тих самих або інших колонок рядка. WHERE визначає, які рядки зачіпає.
UPDATE з посиланням на іншу таблицю
Section titled “UPDATE з посиланням на іншу таблицю”Дві форми синтаксису, та сама семантика — оновити одну таблицю значеннями з іншої:
-- Форма 1: UPDATE FROM (Postgres-стиль)UPDATE tab_2 t2 SET t2.value1 = t1.value1 FROM tab_2 AS t2, tab_1 AS t1 WHERE t2.key1 = t1.key1 + t1.key2 * 100;⚠️ Грабля: SAP рекомендує MERGE замість UPDATE FROM
SAP-нота 2241598 явно радить використовувати
MERGE INTOзамістьUPDATE ... FROM— синтаксис чіткіший і поведінка прогнозованіша. На production-коді користуйся MERGE.
UPSERT / REPLACE
Section titled “UPSERT / REPLACE”UPSERT і REPLACE — синоніми (повністю однакові statement-и). Семантика: вставити, якщо нема такого ключа; оновити, якщо є.
Один рядок
Section titled “Один рядок”-- Якщо є рядок з key1=1 → оновлюється; якщо нема → вставляєтьсяUPSERT tab_1 VALUES (1, 1, 'Inserted with UPSERT') WITH PRIMARY KEY;
UPSERT tab_1 VALUES (1, 2, 'Inserted with UPSERT') WITH PRIMARY KEY;
-- Тепер update — той самий ключUPSERT tab_1 VALUES (1, 2, 'Changed with UPSERT') WITH PRIMARY KEY;WITH PRIMARY KEY обовʼязкове — без нього UPSERT не знає, як ідентифікувати рядок.
З column-list (рекомендований варіант)
Section titled “З column-list (рекомендований варіант)”-- Тільки оновлювати/вставляти ці колонки. Решта — NULL/DEFAULT при INSERTUPSERT tab_1 (key1, key2, value1) VALUES (1, 2, 'Updated value1') WITH PRIMARY KEY;Column-list робить код стійким до змін структури таблиці.
З WHERE замість WITH PRIMARY KEY
Section titled “З WHERE замість WITH PRIMARY KEY”UPSERT tab_1 (key1, key2, value1) VALUES (1, 2, 'X') WHERE key1 = 1 AND key2 = 2; -- WHERE мусить покрити повний primary keyМасовий UPSERT з підзапиту
Section titled “Масовий UPSERT з підзапиту”-- Зсуваємо ключі на 10 і вставляємо/оновлюємоUPSERT tab_1 SELECT key1 + 10, key2, 'Inserted by subquery' FROM tab_1 WITH PRIMARY KEY;
-- З column-listUPSERT tab_1 (key1, key2, value1) SELECT key1 + 10, key2, 'Updated by subquery' FROM tab_1 WHERE key2 = 2 WITH PRIMARY KEY;⚠️ Грабля: UPSERT vs ABAP MODIFY
ABAP-statement
MODIFY dbtab FROM TABLE @itabвсередині HANA транслюється саме вUPSERT. Тобто semantics збігається. АлеMODIFY itab(на internal table) — це просто оновлення в памʼяті, без відношення до БД. Не плутати.
MERGE INTO
Section titled “MERGE INTO”Найпотужніша форма upsert — модифікація однієї таблиці на основі іншого джерела з декларативними правилами WHEN MATCHED / WHEN NOT MATCHED:
MERGE INTO tab_1 USING tab_2 AS t2 ON (tab_1.key1 = t2.key1)
WHEN MATCHED THEN UPDATE SET tab_1.key1 = t2.key1, tab_1.key2 = 1, tab_1.value1 = 'UPDATE: ' || tab_1.value1
WHEN NOT MATCHED THEN INSERT VALUES (t2.key1, 1, 'INSERT: ' || t2.value2);Будь-яка з гілок (WHEN MATCHED або WHEN NOT MATCHED) — опціональна. Якщо лишити одну, statement зводиться до UPDATE або INSERT відповідно.
MERGE з table variable як джерелом
Section titled “MERGE з table variable як джерелом”USING приймає назву таблиці — не SELECT-підзапит. Але можна підставити table variable:
DO BEGIN lt_tmp = SELECT * FROM tab_2;
MERGE INTO tab_1 USING :lt_tmp AS t2 ON (tab_1.key1 = t2.key1) WHEN MATCHED THEN UPDATE SET tab_1.value1 = t2.value1 WHEN NOT MATCHED THEN INSERT VALUES (t2.key1, 1, t2.value2);END;MERGE vs UPSERT — коли що
Section titled “MERGE vs UPSERT — коли що”| Критерій | UPSERT | MERGE INTO |
|---|---|---|
| Джерело — VALUES | ✅ | ❌ |
| Джерело — SELECT | ✅ (один рядок або підзапит) | ✅ (тільки named source) |
| Різна логіка для INSERT і UPDATE | ❌ | ✅ |
| Можна тільки оновлювати, не вставляти | ❌ | ✅ (WHEN MATCHED без WHEN NOT MATCHED) |
| Читабельність | Компактний синтаксис | Явні гілки |
Правило: для простого “вставити або оновити з тими самими значеннями” — UPSERT. Для складнішої логіки (різні правила для existing / new рядків) — MERGE.
DELETE
Section titled “DELETE”-- З WHERE — тільки відповідні рядкиDELETE FROM tab_1 WHERE key1 < 100;
-- Без WHERE — усі рядки (але метадані таблиці зберігаються)DELETE FROM tab_1;Можна посилатися на іншу таблицю через підзапит:
DELETE FROM tab_1 WHERE key1 IN ( SELECT key1 FROM tab_archive WHERE archived_at < ADD_YEARS(CURRENT_DATE, -3) );⚠️ Грабля: DELETE без WHERE
Класичний косяк — забути
WHERE. Завжди перевіряй: спочаткуSELECT COUNT(*) FROM ... WHERE ...зі своїм фільтром, тоді той самийWHEREуDELETE. На дев-системі в анонімному блоці — оточуй DML транзакцією зROLLBACKдо тих пір, поки не впевнений.
TRUNCATE TABLE
Section titled “TRUNCATE TABLE”Швидке видалення всіх рядків таблиці:
TRUNCATE TABLE tab_1;Відмінності від DELETE FROM tab_1:
| Критерій | DELETE FROM | TRUNCATE TABLE |
|---|---|---|
WHERE | ✅ дозволено | ❌ заборонено |
| Швидкість | Залежить від обʼєму | Майже миттєво |
| Транзакція | Так, можна ROLLBACK | Implicit commit — не можна відкотити |
Тригери BEFORE DELETE | Спрацьовують | Не спрацьовують |
::ROWCOUNT після | К-сть видалених | Не визначено |
⚠️ Грабля: TRUNCATE — це DDL
Через implicit commit
TRUNCATEне годиться у звичайному application-коді з транзакціями. Використовуй для швидкого очищення staging-таблиць у ETL, тестових сценаріях. У production-bulk-операціях — ні.
Вставка в table variable
Section titled “Вставка в table variable”Decтрукція даних в table variable працює по-іншому — це не SQL-таблиця, а SQLScript-конструкція:
DO BEGIN DECLARE lt_t TABLE (id INT, val NVARCHAR(50));
-- Можна assign-ити повністю lt_t = SELECT 1 AS id, 'A' AS val FROM dummy UNION ALL SELECT 2, 'B' FROM dummy;
-- INSERT/UPDATE/DELETE — теж працюють INSERT INTO :lt_t VALUES (3, 'C'); UPDATE :lt_t SET val = 'X' WHERE id = 1; DELETE FROM :lt_t WHERE id = 2;
SELECT * FROM :lt_t;END;Зверни увагу — у DML на table variable префікс : обовʼязковий і перед імʼям, не як у читанні.
Підсумкова таблиця
Section titled “Підсумкова таблиця”| Statement | Призначення | Mass / single | Implicit commit |
|---|---|---|---|
INSERT | Вставка нових | Обидва | Ні |
UPDATE | Оновлення існуючих | Mass через WHERE | Ні |
UPSERT / REPLACE | INSERT-or-UPDATE по PK | Обидва | Ні |
MERGE INTO | INSERT-and-UPDATE з джерелом-таблицею | Mass | Ні |
DELETE | Видалення з WHERE | Mass через WHERE | Ні |
TRUNCATE TABLE | Видалення всього | Mass | Так |
Що далі
Section titled “Що далі”- Імперативне SQLScript — змінні, цикли, курсори, транзакції, винятки
- Чистий код, тестування, продуктивність — як писати швидкий і підтримуваний код
- Декларативне SQLScript — SELECT, JOIN, CTE
- Типи даних і функції — рядки, дати, числа