Курс молодого бійця PostgreSQL

Хочу поділитися корисними прийомами роботи з PostgreSQL (інші СУБД мають схожий функціонал, але можуть мати інший синтаксис).

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

Даний матеріал буде корисний тим, хто повністю освоїв базові навички SQL і бажає вчитися далі. Раджу виконувати і експериментувати з прикладами в pgAdmin‘e, я зробив все SQL-запити здійсненними без розгортання будь-яких дампів.

Поїхали!

1. Використання тимчасових таблиць

При вирішенні складних завдань важко помістити рішення в один запит (хоча, намагаються так багато зробити). У таких випадках зручно поміщати будь-які проміжні дані у временную таблицю, для використання їх в подальшому.

Такі таблиці створюються як звичайні, але з ключовим словом TEMP, і автоматично видаляються після завершення сесії.

Ключ ON COMMIT DROP автоматично видаляє таблицю (і всі пов’язані з нею об’єкти) при завершенні транзакції.

Приклад:

ROLLBACK;
BEGIN;
CREATE TEMP TABLE my_fist_temp_table -- варто використовувати найбільш унікальне ім'я
ON COMMIT DROP -- видаляємо таблицю при завершенні транзакції
AS 
SELECT 1 AS id, CAST ('якісь значення' AS TEXT) AS val;

------------ Додаткові маніпуляції з таблицею: ------------------

 -- змінимо таблицю, додавши стовпець. Буду частенько зачіпати суміжні теми
ALTER TABLE my_fist_temp_table 
ADD COLUMN is_deleted BOOLEAN NOT NULL DEFAULT FALSE;
 -- для тих, хто не в курсі, найчастіше дані в таблиці не видаляються, а позначаються як віддалені подібним прапором

CREATE UNIQUE INDEX ON my_fist_temp_table (lower(val))
WHERE is_deleted = FALSE; -- можна навіть створити індекс/обмеження, якщо це необхідно
-- цей індекс не дозволить вставити дублікат(не залежно від регістра) для стовпця VAL, для не видалених рядків

-- маніпулюємо даними таблиці
UPDATE my_fist_temp_table 
SET id=id+3; 

 -- перевіряємо/використовуємо вміст таблиці
SELECT * FROM my_fist_temp_table;
--COMMIT;

 

2. Часто використовується скорочений синтаксис Postgres

 

  • Перетворення типів даних.

Вираз:

SELECT CAST ('365' AS INT);

можна записати менш громіздко:

SELECT '365'::INT;

 

  • Скорочений запис конструкції (I)LIKE ‘%text%’

LIKE сприймає шаблонні вирази. Подробиці в мануалі
оператор LIKE можна замінити на ~~ (дві тільди)
оператор ILIKE можна замінити на ~~* (дві тільди з зірочкою)

Пошук регулярними виразами (має відмінний від LIKE синтаксис)
оператор ~ (одна тильда) сприймає регулярні вирази
оператор ~* (одна тільда і зірочка) регистронезависимая версія ~

Наведу приклад пошуку різними способами рядків, які містять слово text

Скорочений синтаксисОписАналог (I)LIKE

~ ‘text’
or
~~ ‘%text%’
Перевіряє відповідність виразу з урахуванням регістру LIKE ‘%text%’
~* ‘text’
~~* ‘%text%’
Перевіряє відповідність висловом без урахування регістру ILIKE ‘%text%’
!~ ‘text’
!~~ ‘%text%’
Перевіряє невідповідність висловом з урахуванням регістру NOT LIKE ‘%text%’
!~* ‘text’
!~~* ‘%text%’
Перевіряє невідповідність висловом без урахування регістру NOT ILIKE ‘%text%’

 

3. Загальні табличні вирази (CTE). Конструкція WITH

Дуже зручна конструкція, дозволяє помістити результат запиту в тимчасову таблицю і тут же використовувати її.

Приклади будуть примітивні, щоб вловити суть.

a) Простий SELECT

WITH cte_table_name AS ( -- задаємо зручне нам ім'я таблиці
 SELECT schemaname, tablename -- наш будь-який запит
 FROM pg_catalog.pg_tables -- наприклад, системна таблиця з таблицями бази
 ORDER BY 1,2
)
SELECT * FROM cte_table_name; -- вказуємо нашу таблицю
--за фактом отримаємо результат виконання запиту в дужках

Таким способом можна ‘обертати’ які-небудь запити (навіть UPDATE, DELETE і INSERT, про це буде нижче) і використовувати їх результати у подальшому.

b) Можна створити кілька таблиць, перераховуючи їх наступним способом

WITH
 table_1 (col,b) AS (SELECT 1,1), -- перша таблиця
 table_2 (col,c) AS (SELECT 2,2) -- друга таблиця
 --,table_3 (cool,yah) AS (SELECT 2,2 from table_2) -- зовсім недавно дізнався, що можна звертатися до вищестоящої таблиці
SELECT * FROM table_1 FULL JOIN table_2 USING (col);

c) Можна навіть вкласти вищевказану конструкцію у ще один (і більше) WITH

WITH super_with (col,b,c) AS ( /* можемо задати імена стовпців в дужках після імені таблиці */
WITH
 table_1 (col,b) AS (SELECT 1,1),
 table_2 (col,c) AS (SELECT 2,2) 
 SELECT * FROM table_1 FULL JOIN table_2 USING (col)-- вказуємо нашу таблицю
)
SELECT col, b*20, c*30 FROM super_with;

По продуктивності слід сказати, що не варто поміщати в секцію WITH дані, які будуть в значній мірі фільтруватися наступними зовнішніми умовами (за межами дужок запиту), бо оптимізатор не зможе побудувати ефективний запит. Найзручніше покласти в CTE результати, до яких потрібно кілька разів звертатися.

4. Функція array_agg(MyColumn).

Значення в реляційній базі зберігаються розрізнено (атрибути по одному об’єкту можуть бути представлені у кількох рядках). Для передачі даних будь-якої програми часто виникає необхідність зібрати дані в один рядок (клітинку) або масив.
В PostgreSQL для цього існує функція array_agg(), вона дозволяє зібрати в масив дані всього стовпця (якщо вибірка з одного стовпця).
При використанні GROUP BY в масив потраплять дані якого-небудь стовпця щодо кожної групи.

Читайте також  Модуль керування силовим перетворювачем: розробка і складання

Відразу опишу ще одну функцію і перейдемо до прикладу.
array_to_string(array[], ‘;’) дозволяє перетворити масив в рядок: першим параметром вказується масив, другим — зручний нам роздільник в одинарних лапках (апострофах). В якості роздільника можна використовувати
спецсимволиТабуляція t — приміром, дозволить при вставки комірки в EXCEL без зусиль розбити значення на стовпці (використовувати так: array_to_string(array[], E’t’) )
Переклад рядка n — розкладе значення масиву рядків в одній клітинці (використовувати так: array_to_string(array[], E’n’) — поясню нижче чому)
Приклад:

-- створимо і наповнимо даними таблиці вищеописаним способом
WITH my_table (ID, year, any_val) AS ( 
 VALUES (1, 2017,56)
 ,(2, 2017,67)
 ,(3, 2017,12)
 ,(4, 2017,30)
 ,(5, 2020,8)
 ,(6, 2030,17)
 ,(7, 2030,50)
 ) 
SELECT year
,array_agg(any_val) -- збираю дані (по кожному року) в масив
,array_agg(any_val ORDER BY any_val) AS sort_array_agg -- порядок елементів можна відсортувати (з 9+ версії Postgres) 
,array_to_string(array_agg(any_val),';') -- преобразовываю масив в рядок
,ARRAY['This', 'is', 'my' , 'array'] AS my_simple_array -- спосіб створення масиву
FROM my_table 
GROUP BY year; -- групуємо дані по кожному року

Видасть результат:

Виконаємо зворотну дію. Розкладемо масив у рядки за допомогою функції UNNEST, заодно покажу конструкцію SELECT columns INTO table_name. Вміщу це спойлер, щоб стаття не сильно розбухала.
UNNEST запит

-- 1 Підготовчий етап
-- у процесі запиту буде створена таблиця tst_unnest_for_del, з допомогою конструкції SELECT INTO 
-- щоб запит не приводив до помилки, у випадку якщо ви будете кілька разів проганяти цей скрипт, почну цей скрипт з видалення таблиці.
-- я також сподіваюся, що ви запускаєте це не production на сервері будь-якого проекту, де є така таблиця

DROP TABLE IF EXISTS tst_unnest_for_del; /* IF EXISTS не викличе помилки, якщо таблиці для видалення не існує */

WITH my_table (ID, year, any_val) AS ( 
 VALUES (1, 2017,56)
 ,(2, 2017,67)
 ,(3, 2017,12)
 ,(4, 2017,30)
 ,(5, 2020,8)
 ,(6, 2030,17)
 ,(7, 2030,50)
 ) 
SELECT year
,array_agg(id) AS arr_id -- збираю дані(id) по кожному року в масив
,array_agg(any_val) AS arr_any_val -- збираю дані(any_val) по кожному року в масив
INTO tst_unnest_for_del -- !! спосіб створення та заповнення таблиці з отриманого результату
FROM my_table 
GROUP BY year;

--2 Демонстрування функції Unnest
SELECT unnest(arr_id) unnest_id -- розбираємо стовпець id
,year
,unnest(arr_any_val) unnest_any_val -- розбираємо стовпець any_val
FROM tst_unnest_for_del 
ORDER BY 1 -- відновлюємо сортування по id, без примусової сортування дані можуть бути розташовані хаотично

Результат:

5. Ключове слово RETURNIG *

зазначене після запитів INSERT, UPDATE або DELETE дозволяє побачити рядки, яких торкнулася модифікація (зазвичай сервер повідомляє лише кількість модифікованих рядків).
Зручно в зв’язці з BEGIN подивитися на що саме вплине запит, у разі невпевненості в результаті або для передачі яких-небудь id на наступний крок.

Приклад:

--1
DROP TABLE IF EXISTS for_del_tmp; /* IF EXISTS не викличе помилки, якщо таблиці для видалення не існує */
CREATE TABLE for_del_tmp -- Створюємо таблицю
AS --Наповнюємо згенерованими даними запиту нижче
SELECT generate_series(1,1000) AS id, -- Генеруємо 1000 пронумерованих рядків 
random() AS values; -- Наповнюємо випадковими числами

--2
DELETE FROM for_del_tmp 
WHERE id > 500
RETURNING *; 
/*Покаже всі видалені рядки даної командою, 
RETURNING * - поверне всі стовпці таблиці test, 
так само можна перерахувати стовпці як в SELECT (прим. RETURNING id,name)*/

Можна використовувати в зв’язці з CTE, організовую божевільний приклад.

P. S. Я дуже заморочился, боюся, що вийшло складно, але я постарався все прокоментувати.

--1
DROP TABLE IF EXISTS for_del_tmp; /* IF EXISTS не викличе помилки, якщо таблиці для видалення не існує */
CREATE TABLE for_del_tmp -- Створюємо таблицю
AS --Наповнюємо згенерованими даними запиту нижче
SELECT generate_series(1,1000) AS id, -- Генеруємо 1000 пронумерованих рядків 
((random()*1000)::INTEGER)::text as values; /* Наповнюємо випадковими числами. P. S. У мене Postgre 9.2 Random() повертає дробове число менше одиниці, множу на 1000, щоб отримати цілу частину, потім преобразовываю до INTEGER для позбавлення від дробової частини, і преобразовываю до тексту, т. к. хочу, щоб тип даних створеного стовпця був TEXT*/

--2
DELETE FROM for_del_tmp 
WHERE id > 500
RETURNING *; - Даний запит просто видалить запису, повернувши видалені рядки на екран

--3
WITH deleted_id (id) AS
(
 DELETE FROM for_del_tmp
 WHERE id > 25
 RETURNING id -- видаляємо ще частина даних, записуючи id наше CTE "deleted_id"
)
INSERT INTO for_del_tmp -- ініціюємо INSERT
SELECT id, 'Віддалена рядок' || now()::TIME || 'а якщо бути точним, то' || timeofday()::TIMESTAMP /* тут можна простежити за тим, як відрізняється час повертається функціями (залежить від опису функції, заглиблюватися не буду, і так далеко зайшов)*/
FROM deleted_id -- вставляємо видалені дані з "for_del_tmp" у неї ж
RETURNING *; -- відразу бачимо що проинсертилось
--весь блок можна виконувати нескінченно, ми будемо вставляти ці дані в цю ж таблицю.

--4
SELECT * FROM for_del_tmp; -- перевіряємо, що вийшло в результаті

Таким чином, виконається видалення даних, і віддалені значення передадуться на наступний етап. Все залежить від вашої фантазії і цілей. Перед застосуванням складних конструкцій обов’язково вивчіть документацію вашої версії СУБД! (при паралельному комбінуванні INSERT, UPDATE або DELETE існують тонкощі)

Читайте також  Пишемо завантажувач ПЛІС в LabVIEW. Частина 2

6. Збереження результату запиту в файл

У команди COPY багато різних параметрів і призначень, опишу найпростіше застосування для ознайомлення.

COPY (
SELECT * FROM pg_stat_activity /* Наш запит. Для прикладу: системна таблиця виконуваних процесів БД */
--) TO 'C:/TEMP/my_proc_tst.csv' -- Запис результату запиту в файл. Приклад для Windows
) TO '/tmp/my_proc_tst.csv' -- Запис результату запиту в файл. Приклад для LINUX
--) TO STDOUT -- виведе дані в консоль або лог pgAdmin
WITH CSV HEADER -- Необов'язкова рядок. Передає назва стовпців таблиці у файл

 

7. Виконання запиту на іншій базі

Не так давно дізнався, що можна адресувати запит до іншої бази, для цього є функція dblink (всі подробиці в мануалі)

Приклад:

SELECT * FROM dblink(
'host=localhost user=postgres dbname=postgres', /* host і user можна не вказувати, якщо ви хочете використовувати поточні */
'SELECT "Віддалена база:" || current_database()' /* є свої нюанси і обмеження. Як приклад, запит передається в одинарних лапках, тому лапки всередині запиту повинні бути екрановані (в даному прикладі для екранування використовую дві одинарні лапки поспіль). */
) 
RETURNS (col_name TEXT)
UNION ALL
SELECT 'Поточна база:' || current_database();

Якщо виникає помилка: «ERROR: function dblink(unknown, unknown) does not exist»необхідно виконати установку розширення наступною командою:

CREATE EXTENSION dblink;

 

8. Функція similarity

Функція визначення схожості одного значення до іншого.

Використовував для зіставлення текстових даних, які були схожі, але не дорівнюють один одному (були помилки). Заощадив купу часу і нервів, звівши до мінімуму ручну прив’язку.
similarity(a, b) видає дробове число від 0 до 1, чим ближче до 1, тим точніше збіг.
Перейдемо до прикладу. З допомогою WITH організуємо тимчасову таблицю з вигаданими даними (і спеціально исковерканными для демонстрації функції), і будемо порівнювати кожну рядок з нашим текстом. У прикладі нижче будемо шукати те, що більше схоже на ТОВ «РОМАШКА» (підставимо у другий параметр функції).

WITH company (id,c_name) AS (
 VALUES (1, 'ТОВ РОМАШка')
 UNION ALL
 /* P. S. UNION ALL працює швидше, ніж UNION, оскільки відсутня примусова сортування для усунення дублікатів, яка нам не потрібно в даному випадку */
 VALUES (2, 'ТОВ "РОМАШКА"')
 UNION ALL
 VALUES (3, 'ТОВ РаМАШКА')
 UNION ALL
 VALUES (4, 'ВАТ "РОМАКША"')
 UNION ALL
 VALUES (5, 'ЗАТ РОМАШКА')
 UNION ALL
 VALUES (6, 'ТОВ РВ МАШКА')
 UNION ALL
 VALUES (7, 'ТОВ РОГИ І КОПИТА')
 UNION ALL
 VALUES (8, 'ZAO РОМАШКА')
 UNION ALL
 VALUES (9, 'Як це сюди потрапило?')
 UNION ALL
 VALUES (10, 'Ромашка 33')
 UNION ALL
 VALUES (11, 'ІП "РомаШкович"')
 UNION ALL
 VALUES (12, 'ТОВ "Рома Шкович"')
 UNION ALL
 VALUES (13, 'ІП "Рома Шкович"')
)
SELECT *, similarity(c_name, 'ТОВ "РОМАШКА"')
,dense_rank() OVER (ORDER BY similarity(c_name, 'ТОВ "РОМАШКА"') DESC) 
AS "Ранжування результатів" -- віконна функцій, про неї буде сказано нижче
FROM company
WHERE similarity(c_name, 'ТОВ "РОМАШКА"') >0.25 -- значення від 0 до 1, чим ближче до 1, тим точніше збіг
ORDER BY similarity DESC;

Отримаємо наступний результат:

Якщо виникає помилка «ERROR: function similarity(unknown, unknown) does not exist»необхідно виконати установку розширення наступною командою:

CREATE EXTENSION pg_trgm;

Приклад складніше

WITH company (id,c_name) AS ( -- вхідна таблиця з даними
 VALUES (1, 'ТОВ РОМАШка')
 ,(2, 'ТОВ "РОМАШКА"')
 ,(3, 'ТОВ РаМАШКА')
 ,(4, 'ВАТ "РОМАКША"')
 ,(5, 'ЗАТ РОМАШКА')
 ,(6, 'ТОВ РВ МАШКА')
 ,(7, 'ТОВ РОГИ І КОПИТА')
 ,(8, 'ZAO РОМАШКА')
 ,(9, 'Як це сюди потрапило?')
 ,(10, 'Ромашка 33')
 ,(11, 'ІП "РомаШкович"')
 ,(12, 'ТОВ "Рома Шкович"')
 ,(14, 'ІП "Рома Шкович"')
 ,(13, 'ТОВ РАГА І КАПЫТА')
),
compare (id, need) AS -- наша база для зіставлення
 (VALUES (100500, 'ТОВ "РОМАШКА"')
 ,(9999, 'ТОВ "РОГИ І КОПИТА"')
)

SELECT c1.id c1.c_name, 'порівнюємо з' || c2.need, similarity(c1.c_name, c2.need) 
,dense_rank() OVER (PARTITION BY c2.need ORDER BY similarity(c1.c_name, c2.need) DESC) 
AS "Ранжування результатів" -- віконна функцій, про неї буде сказано нижче
FROM company c1 CROSS JOIN compare c2
WHERE similarity(c_name, c2.need) >0.25 -- значення від 0 до 1, чим ближче до 1, тим точніше збіг
ORDER BY similarity DESC;

Отримаємо такий результат:

Сортуємо по similarity DESC. Першими результатами бачимо найбільш схожі рядки (1— повна схожість).

Необов’язково відображати значення similarity в SELECT, можна просто використовувати його в умові WHERE similarity(c_name, ‘ТОВ «РОМАШКА»’) >0.7
і самим ставити влаштовуючий нас параметр.

P.s. Буду вдячний, якщо підкажете які ще є способи зіставлення текстових даних. Пробував прибирати регулярними виразами все крім букв/цифр, і зіставляти з рівності, але такий варіант не спрацьовує, якщо присутні помилки.

Читайте також  За структурою матеріалу надрукованого предмета можна визначити модель 3D-принтера і конкретний пристрій

9. Віконні функції OVER() (PARTITION BY __ ORDER BY __ )

Майже описавши в своєму чернетці це дуже потужний інструмент, виявив (з сумом і радістю), що подібна якісна стаття на цю тему вже існує. Не бачу сенсу дублювати інформацію, тому рекомендую обов’язково ознайомитися з цією статтею (посилання — habrahabr.ru/post/268983/ автору низький уклін тим, хто ще не вміє користуватися віконними функціями SQL.

10. Множинний шаблон для LIKE

Завдання. Необхідно відфільтрувати список користувачів, імена яких повинні відповідати певним шаблонам.

Як завжди, представлю найпростіший приклад:

-- Створюємо таблицю з даними
CREATE TEMP TABLE users_tst (id, u_name) 
AS (VALUES (1::INT, NULL::VARCHAR(50))
,(2, 'Уляна Х.')
,(3, 'Семен В.')
,(4, 'Вікторія Т.')
,(5, 'Ольга С.')
,(6, 'Єлизавета В.')
,(7, 'Микола Х.')
,(8, 'Ісаак Р.')
,(9, 'Єлисей А.')
);

Маємо запит, який виконує свою функцію, але стає громіздким при великій кількості фільтрів.

SELECT * FROM users_tst
WHERE u_name LIKE '%' 
 OR u_name LIKE '%аа%' 
 OR u_name LIKE 'Уляна Х.'
 OR u_name LIKE 'Єлисей%'
 -- і т. д.

Продемонструю, як зробити його більш компактним:

SELECT * FROM users_tst
WHERE u_name LIKE ANY (ARRAY ['%', '%аа%', 'Уляна Х.', 'Єлисей%'])

Можна зробити цікаві трюки, використовуючи подібний підхід.
Напишіть в коментарях, якщо є думки, як ще можна переписати вихідний запит.

11. Кілька корисних функцій

NULLIF(a,b)
Виникають ситуації, коли певне значення потрібно трактувати як NULL.
Наприклад, рядок нульової довжини ( ” — порожні рядки) або нуль(0).
Можна написати CASE, але лаконічніше використовувати функцію NULLIF, яка має 2 параметра, при рівності яких повертається NULL, інакше виводить початкове значення.

SELECT id
,param
CASE WHEN param = 0 NULL THEN ELSE param END -- рішення через CASE
,NULLIF(param,0) -- рішення через NULLIF
,val 
FROM(
 VALUES( 1, 0, 'В стовпці зліва був 0' )
 ) AS tst (id param,val);

COALESCE вибирає перше не NULL значення

SELECT COALESCE NULL,NULL,-20,1,NULL,-7); --вибере -20

GREATEST вибирає найбільше значення з перерахованих

SELECT GREATEST(2,1,NULL,5,7,4,-9); --вибере 7

LEAST вибирає найменше значення з перерахованих

SELECT LEAST(2,1,NULL,5,7,4,-9); -- вибере -9 

PG_TYPEOF показує тип даних стовпця

SELECT pg_typeof(id), pg_typeof(arr), pg_typeof(NULL) 
FROM (VALUES ('1'::SMALLINT, array[1,2,'3',3.5])) AS x(id,arr);
-- покаже smallint, numeric[] і unknown відповідно 

PG_CANCEL_BACKEND зупиняємо небажані процеси в базі

SELECT pid, query, * FROM pg_stat_activity -- таблиця з процесами БД. В старих версіях postgres стовпець PID називався PROCPID
WHERE state <> 'idle' and pid <> pg_backend_pid(); -- виключаємо підключення і свій тільки що викликаний процес

SELECT pg_terminate_backend(PID); /* підставляємо сюди PID процесу який ми хочемо зупинити, на відміну від нижчеподаній команди, посилає більш щадний сигнал про завершення, який не завжди може вбити процес*/
SELECT pg_cancel_backend(PID); /* підставляємо сюди PID процесу який ми хочемо зупинити. Практично гарантовано вбиває запит, щось на зразок KILL -9 в LINUX */

Докладніше в мануалі
P. S.

SELECT pg_cancel_backend(pid) FROM pg_stat_activity -- приклад заради вбиваємо всі процеси
WHERE state <> 'idle' and pid <> pg_backend_pid();

Увага! Ні в якому разі не вбивайте завислий процес через консоль KILL -9 або диспетчер завдань.
Це може привести до краху БД, втрати даних і довгому автоматичного відновлення бази.

12. Екранування символів

Почну з основ.
У SQL рядкові значення обрамляються апострофом (одинарною лапкою).
Числові значення не можна обрамляти апострофами, а для поділу дробової частини потрібно використовувати точку, т. к. кома буде сприйнята як роздільник

SELECT 'Мій текст', 365, 567.6, 567,6 

результат:

Все добре, до тих пір поки не потрібно виводити сам знак апострофа
Для цього існують два способи екранування (відомих мені)

SELECT 1, 'Апостроф" і два апострофа поспіль "" ' -- Екранування подвійним написанням "
UNION ALL
SELECT 2, E'Апостроф ' і два апострофа поспіль '' ' -- екранування зворотним слешем, , англійська літера E перед першою лапкою необхідна, щоб символ  сприймався як символ екранування

результат однаковий:

В PostgreSQL існують більш зручний спосіб використовувати дані, без екранування символів. У обрамлена двома знаками долара $$ рядку можна використовувати практично будь-які символи.

Приклад:

select $$необов'язково писати " щоб просто вивести апостроф ', або морочитися з E'' $$

отримую дані в первозданному вигляді:

Якщо цього мало, і всередині потрібно використовувати два символу долара поспіль $$, то Postgres дозволяє задати свій «обмежувач». Варто лише між двома доларами написати свій текст, наприклад:

select $uniq_tAg$ необов'язково писати " щоб просто вивести апостроф ', або морочитися з E'', обрамляйте в $$ або $any_text$ $uniq_tAg$

Побачимо наш текст:

Для себе цей спосіб відкрив не так давно, коли почав вивчати написання функцій.

Висновок

Сподіваюся, цей матеріал допоможе дізнатися багато нового початківцям і «средничкам». Сам я не є розробником, а можу лише назвати себе любителем SQL, тому те, як використовувати описані прийоми — вирішувати Вам.

Бажаю успіхів у вивченні SQL. Чекаю коментарів і дякую за прочитання!

Степан Лютий

Обожнюю технології в сучасному світі. Хоча частенько і замислююся над тим, як далеко вони нас заведуть. Не те, щоб я прям і знаюся на ядрах, пікселях, коллайдерах і інших парсеках. Просто приходжу в захват від того, що може в творчому пориві вигадати людський розум.

You may also like...

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *