DB developers channel
803 subscribers
2 photos
46 files
102 links
💡 Канал для разработчиков баз данных: Oracle, PostgreSQL
📌 Интересные задачи, фрагменты кода, лучшие практики, архитектура, оптимизация
🔄 Присоединяйся к сообществу — развивайся вместе с нами!
#SQL #Oracle #PostgreSQL #PL/SQL #PL/pgSQL #DB
Download Telegram
🎵 «Перемен требуют наши сердца!
Перемен требуют наши глаза!
В нашем смехе, и в наших слезах, и в пульсации вен
Перемен! Мы ждём перемен!.» — Кино

📌 Разбор задачи: Расстояние редактирования (Levenshtein distance)

🧩 Условие:
Даны две строки. Нужно вычислить минимальное количество операций (вставка, удаление, замена), необходимых для преобразования одной строки в другую.

⚙️ Алгоритм (кратко):
1️⃣ Создаём матрицу, где строки — символы первой строки, а столбцы — символы второй.
2️⃣ Заполняем первую строку и первый столбец начальными значениями (количество операций для пустых префиксов).
3️⃣ Для каждой позиции выбираем минимум из трёх вариантов:
• замена (если символы разные),
• вставка,
• удаление.
4️⃣ В правом нижнем углу матрицы получаем ответ.

🔢 Пример работы:
  0  1  2  3  4  5  6  7  8
1 1 2 3 4 5 6 7 8
2 2 1 2 3 4 5 6 7
3 3 2 2 3 4 5 5 6
4 4 3 3 3 4 5 5 6
5 5 4 4 4 4 4 5 6
6 6 5 5 5 5 5 5 6

👉 Результат: расстояние редактирования = 6
(чтобы из "carrot" получилось "password").

Код на PL/SQL:
DECLARE
str1 VARCHAR2 (100) := 'carrot';
str2 VARCHAR2 (100) := 'password';

TYPE row_tab IS TABLE OF INTEGER;
TYPE matrix_tab IS TABLE OF row_tab;

matrix matrix_tab := matrix_tab ();

PROCEDURE init_matrix (matrix IN OUT NOCOPY matrix_tab) IS
"row" row_tab := row_tab ();
BEGIN
matrix.EXTEND (LENGTH (str1) + 1);
"row".EXTEND (LENGTH (str2) + 1);

FOR i IN 1 .. matrix.COUNT LOOP
matrix (i) := "row";
END LOOP;

FOR i IN 1 .. matrix.COUNT LOOP
FOR j IN 1 .. "row".COUNT LOOP
IF i = 1 AND j = 1 THEN
matrix (i) (j) := 0;
ELSIF i = 1 THEN
matrix (i) (j) := j - 1;
ELSIF j = 1 THEN
matrix (i) (j) := i - 1;
END IF;
END LOOP;
END LOOP;
END init_matrix;

PROCEDURE print_matrix (matrix IN OUT NOCOPY matrix_tab) IS
BEGIN
FOR i IN 1 .. matrix.COUNT LOOP
DECLARE
str VARCHAR2 (100);
BEGIN
FOR j IN 1 .. matrix (i).COUNT LOOP
str := str || ' ' || COALESCE (TO_CHAR (matrix (i) (j)), ' ');
END LOOP;
DBMS_OUTPUT.put_line (str);
END;
END LOOP;
END print_matrix;

PROCEDURE get_match_matrix (matrix IN OUT NOCOPY matrix_tab) IS
BEGIN
FOR i IN 2 .. matrix.COUNT LOOP
FOR j IN 2 .. matrix (i).COUNT LOOP
IF SUBSTR (str1, i - 1, 1) = SUBSTR (str2, j - 1, 1) THEN
matrix (i) (j) := matrix (i - 1) (j - 1);
ELSE
matrix (i) (j) := 1 + LEAST (LEAST (matrix (i - 1) (j - 1), matrix (i - 1) (j)), matrix (i) (j - 1));
END IF;
END LOOP;
END LOOP;
END get_match_matrix;
BEGIN
init_matrix (matrix => matrix);

get_match_matrix (matrix => matrix);

print_matrix (matrix => matrix);
DBMS_OUTPUT.put_line('max_change_amount = ' || TO_CHAR (matrix (matrix.COUNT) (matrix (matrix.COUNT).COUNT)));
END;

👍 Палец вверх — если алгоритмы продолжают заходить.
👎 Палец вниз — если "капусточка" дело хорошее, но пора переходить на мясные блюда.

⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

💬 Есть мысли? Хотите перемен? - Пишите. 👇
#️⃣ #SQL #Oracle #PLSQL
👍7👎1
🎵 «Мы кое в чём поднаторели:
Мы тарелки бьём весь год —
Мы на них уже собаку съели,
Если повар нам не врёт.» — Владимир Высоцкий

💡 Интересный трюк с SQL-запросами!

Предлагаю "лёгкую" разминку для мозга. 🧠

Есть таблица с перечнями слов:
CREATE TABLE word_lists (
id NUMBER,
word_list VARCHAR2(4000)
);

INSERT INTO word_lists (id, word_list) VALUES (1, ',word1,,word2');
INSERT INTO word_lists (id, word_list) VALUES (2, 'word3,,word2,,word4,');
INSERT INTO word_lists (id, word_list) VALUES (3, 'word1,,,,word4');
INSERT INTO word_lists (id, word_list) VALUES (4, 'word5');

Задача:
Создать таблицу words и заполнить её одним универсальным SQL-запросом так, чтобы каждый элемент списка из word_list оказался в отдельной строке:
CREATE TABLE words (
id NUMBER,
word VARCHAR2(4000)
);

📊 Ожидаемый результат:
 id word
1 NULL
1 word1
1 word2
2 NULL
2 word3
2 word2
2 word4
3 NULL
3 word1
3 word4
4 word5

Фактически, требуется нормализовать таблицу word_lists.

⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

💬 Если есть варианты решения, предлагайте. 👇
#️⃣ #RealInterviewTasks #SQL #Oracle #PostgreSQL #PLSQL #PLpgSQL
2🤔1
DB developers channel
🎵 «Мы кое в чём поднаторели: Мы тарелки бьём весь год — Мы на них уже собаку съели, Если повар нам не врёт.» — Владимир Высоцкий 💡 Интересный трюк с SQL-запросами! Предлагаю "лёгкую" разминку для мозга. 🧠 Есть таблица с перечнями слов: CREATE TABLE word_lists…
«О сколько нам открытий чудных
Готовят просвещенья дух
И Опыт, сын ошибок трудных,
И Гений, парадоксов друг,
И Случай, бог изобретатель.» — Александр Пушкин

📌 Разбор интересной SQL-задачи: парсинг слов с разделителем запятой

Где-то лет 7 назад я нашёл такой вариант 👇

🔹 Решение для ORACLE
SELECT DISTINCT wl.id,
REGEXP_SUBSTR (word_list || ',',
'[^,]+',
1,
LEVEL) AS word
FROM word_lists wl
CONNECT BY wl.id = PRIOR wl.id
AND LEVEL <= REGEXP_COUNT (wl.word_list, ',') + 1
AND PRIOR DBMS_RANDOM.value IS NOT NULL


👉 Здесь интересный момент в использовании DBMS_RANDOM.value.
Поскольку внутри одной строки будут одинаковые узлы (wl.id, REGEXP_COUNT(wl.word_list, ',')), требуется уникальное значение для корректного CONNECT BY.

Но читатели Эльвира и Евгений предложили свои методы. Браво! 🙌
Я весь день ходил довольный, как слон 🐘
Ничто так не поднимает настроение, как хорошо написанный SELECT.

💡 Решение от Эльвиры — элегантное и простое. Снимаю шляпу 🎩
Я его чуть-чуть переделал:

SELECT DISTINCT id,
REGEXP_SUBSTR (wl.word_list,
'[^,]+',
1,
t.column_value) AS word
FROM word_lists wl
CROSS JOIN
TABLE (
CAST (
MULTISET (
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT (wl.word_list, ',') + 1)
AS SYS.odcinumberlist)) t;


💡 Решение от Евгения интересное — я так не умею 🤯
Я его немного сократил, и вот что получилось:

SELECT DISTINCT id, word
FROM (SELECT wl.id,
CAST (x.column_value AS VARCHAR2 (4000)) AS word
FROM word_lists wl,
XMLTABLE (
'if (contains($input, ","))
then ora:tokenize($input, ",")
else ("", $input)'
PASSING wl.word_list AS "input") x)


Также предлагаю вариант используя MODEL
SELECT DISTINCT w.id, w.word
FROM (SELECT id, comma_counter, word
FROM (SELECT id, word_list || ',' AS word_list /*требуется заглушка*/
FROM word_lists)
word_lists
MODEL
PARTITION BY (id)
DIMENSION BY (0 AS comma_counter)
MEASURES (word_list,
0 AS current_comma_pos,
CAST (NULL AS VARCHAR2 (4000)) AS word)
RULES
ITERATE (4000)
UNTIL (iteration_number >=
LENGTH (word_list[0])
- LENGTH (REPLACE (word_list[0], ','))
- 1)
(
current_comma_pos [iteration_number + 1] =
INSTR (word_list[0],
',',
1,
iteration_number + 1),
word [iteration_number + 1] =
SUBSTR (
word_list[0],
current_comma_pos[iteration_number] + 1,
current_comma_pos[iteration_number + 1]
- NVL (current_comma_pos[iteration_number], 0)
- 1))) w
WHERE w.comma_counter != 0
ORDER BY w.id, w.word

👉 Фишка этого метода в том, что мы используем методы динамического программирования и простые функции INSTR и SUBSTR
и не работаем с регулярками и XMLTYPE.
Известно, что и то и другое довольно жадное на ресурсы CPU.

🔹 Решение для PostgreSQL

В PostgreSQL такой приём делается проще 🚀
Хотя я и недолюбливаю PostgreSQL, но в данной ситуации он ведёт себя гибче:
SELECT DISTINCT id,
NULLIF(trim(word), '') AS word
FROM word_lists,
unnest(string_to_array(word_list, ',')) AS word
ORDER BY id, word;


⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

💬 Если придумаете ещё новый вариант — пишите 👇
#️⃣ #RealInterviewTasks #SQL #Oracle #PostgreSQL #PLSQL #PLpgSQL
👍511
🎵 «Ой, где был я вчера — не найду, хоть убей,
Только помню, что стены с обоями.
Помню, Клавка была и подруга при ней,
Целовался на кухне с обоими.» — Владимир Высоцкий

📌 Задача на тему иерархических запросов.
Задачу выслал читатель Max Litynskyi. Выражаю ему большую благодарность за развитие канала.

У нас есть таблица room_links с двумя полями:
CREATE TABLE (
room_out VARCHAR2 (100),
room_in VARCHAR2 (100)
);

Каждая строка таблицы — это прямой проход между двумя комнатами.
Например:
(Кухня, Коридор) — значит, есть дверь из Кухни в Коридор.
(Коридор, Спальня) — значит, из Коридора можно попасть в Спальню.

🔍 Задача: найти кратчайший путь между двумя заданными комнатами, например: Кухня → Спальня.

🏠 Тестовые данные
INSERT INTO room_links (room_out, room_in)
SELECT DISTINCT r.room_out, r.room_in
FROM ( SELECT 'room_' || TO_CHAR (TRUNC (DBMS_RANDOM.VALUE (1, 50)))
AS room_out,
'room_' || TO_CHAR (TRUNC (DBMS_RANDOM.VALUE (1, 50)))
AS room_in
FROM DUAL
CONNECT BY LEVEL <= 1000) r
WHERE r.room_out != r.room_in;


Таким образом, у нас получится «карта помещений» из 50 комнат, соединённых случайными дверями.

👉 Требуется составить SQL запрос, для нахождения кратчайшего пути между двумя заданными комнатами.

⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

💬 Если есть варианты решения, предлагайте. 👇
#️⃣ #RealInterviewTasks #SQL #Oracle #PostgreSQL #PLSQL #PLpgSQL
👍3🤔1
🎵 «Если сто раз с утра всё не так
Если пришла пора сделать шаг
Если ты одинок
Значит, настал твой срок
И ждёт за углом
Перекрёсток семи дорог.» — Андрей Макаревич

🔍 Решение задачи поиска нужного маршрута среди комнат

Задача сводится к поиску пути в графе.
В Oracle для этого удобно использовать иерархический запрос (CONNECT BY).

 Трюк в том, что нужно:

Искать все возможные пути от стартовой комнаты 🚪
Отсекать зацикливания 🔄
Оставить только минимальную длину 📏

WITH paths AS (
SELECT room_out,
room_in,
LEVEL AS room_counter,
SYS_CONNECT_BY_PATH(room_out, ' => ') || ' => ' || room_in AS full_path
FROM room_links
START WITH room_out = 'room_2'
CONNECT BY NOCYCLE PRIOR room_in = room_out
AND room_out != 'room_9'
)
SELECT *
FROM (
SELECT room_out,
room_in,
full_path,
RANK() OVER (ORDER BY room_counter) AS room_counter_rank
FROM paths
WHERE room_in = 'room_9'
) p
WHERE p.room_counter_rank = 1;

 Что делает запрос

START WITH room_out = 'room_2' — начинаем из исходной комнаты 🏠
CONNECT BY NOCYCLE PRIOR room_in = room_out — двигаемся по дверям, не зацикливаясь 🔄
room_out != 'room_9' — если дошли до комнаты room_9, то дальше не идём 🚫
SYS_CONNECT_BY_PATH — собирает весь путь в строку 🛤️

В конце выбираем маршрут до нужной комнаты с минимальной длиной ✂️

📊 Такой подход работает и на сгенерированных тестовых данных — вы получите кратчайшие маршруты между любыми двумя комнатами.

👉 А ещё задачу можно решить через WITH RECURSIVE или даже с помощью MODEL 💡

⚠️ Хотите проверить скрипты, но нет базы под рукой? — онлайн-песочница 💻
💎 Поддержка канала⁉️

💬 Есть что добавить? Не сдерживай себя — пиши 👇
#️⃣ #RealInterviewTasks #SQL #Oracle #PostgreSQL #PLSQL #PLpgSQL
👍21👎1
🎵 «Нагружать все больше нас
Стали почему-то,
Нынче в школе первый класс -
Вроде института.
Нам учитель задает
С иксами задачи,
Кандидат наук и тот -
Над задачей плачет.» — Алла Пугачева

Этим постом я начинаю (и, возможно, заканчиваю) серию «Оптимизация SQL-запросов».

📌 Сегодня вводная для новичков.

📊 Небольшой опрос среди 46 читателей канала показал:
11% — начинающие,
2% — испытывают трудности со сложными запросами.

Это вроде бы мало, чтобы разворачивать целый курс для новичков.
Но и много, чтобы игнорировать и не дать им «маршрут» обучения.

💡 Мои советы

0) SQL — навык, а не «родной язык»
Никто не рождается с пониманием SQL. Все проходят одинаковые стадии — от 🤯 недоумения до 🙌 принятия.
Главный путь — практика. В какой-то момент SQL станет для вас логичным и понятным. Дальше останется только накапливать трюки (и их не так уж много).

1) Настройте локальную среду ⚙️
Создайте место, где можно спокойно экспериментировать, создавать таблицы и объекты, не боясь «сломать прод».

Oracle и PostgreSQL отлично подходят для локальной установки.
Даже если что-то пошло не так — всегда можно всё переустановить.

2) Учитесь по книгам 📚
Для тех, кто не пишет SQL ежедневно, главная трудность — синтаксис. Он специфичный, и к нему нужно привыкнуть.
Но уже через 2–3 недели он станет естественным.

👉 Для старта советую:
«Секреты ORACLE SQL
» — но вообще любая понятная вам книга подойдёт.

3) Разбирайте сложные запросы по шагам 🪜
Многоуровневые вложенные SELECT’ы пугают новичков.
Для анализа используйте конструкцию WITH.

Как обычно пишут:
SELECT ...
FROM (
SELECT ...
FROM (
SELECT ...
FROM a
WHERE ...
) aa
WHERE ...
) aaa
WHERE ...


А вот так понятнее:
WITH a_add AS (
SELECT ...
FROM a
WHERE ...
),
aa_add AS (
SELECT ...
FROM a_add
WHERE ...
),
aaa_add AS (
SELECT ...
FROM aa_add
WHERE ...
)
SELECT * FROM aaa_add;

Так запрос становится прозрачным и разбирается по шагам.
(Но, конечно, не стоит в таком стиле писать всё подряд 😉).

4) Думайте множествами 🔢
Главный совет «на миллион». Даже опытные разработчики часто пишут запросы «по синтаксису». Но SQL проще понимать, если мыслить категориями множеств:

Таблицы = множества строк.
Строки = элементы множества.
Поля = атрибуты элементов.

👉 JOIN — это сопоставление элементов двух множеств по условию.
👉 WHERE — фильтр, выбирающий нужные элементы.
👉 GROUP BY — разбиение множества на подмножества.

Пример: есть таблица «Люди». Нужно соединить их в пары.

Берём множество людей,
накладываем фильтр для первой группы,
накладываем фильтр для второй группы,
соединяем по условию (например, одинаковый возраст, рост или период рождения).

Если приучить себя мыслить множествами, SQL станет в разы понятнее.

⚡️ И главное: лучше решить 1 сложную задачу, чем 1000 лёгких.
Всегда поднимайте планку — только так прокачка идёт быстрее.

⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

👍 Палец вверх — продолжаем и перейдем уже на уровень выше.
👎 Палец вниз — "Вода! Вода! Кругом вода!"

👨‍💻 Эти советы я бы дал самому себе из будущего.
А Вы, уважаемые мидлы, синьоры, эксперты — что бы посоветовали самим себе в прошлом? 👇
#️⃣ #SQLOptimization
👍13👎1
🎵 «Хоть у неё ни черта не осталось!
У неё в кошельке три рубля,
Моя бабушка курит трубку,
Трубку курит бабушка моя.» — Гарик Сукачев

Сегодня суббота — задачи оставим на потом.
Продолжаем рубрику «Полезные ресурсы».

🖥️ Тема: IDE — главный инструмент DB-разработчика

IDE для разработчика — как любимая машина 🚗.
Поменять или обменять сложно: привыкаешь к дизайну, к деталям, к особенностям. Да, у других «машин» тоже есть крутые примочки, но своя всё равно ближе.

🔧 Лично я использую:

PL/SQL Developer — основное IDE.
Toad for Oracle («Жаба») — для анализа, форматирования и мониторинга.
Oracle SQL Developer — ради одной особенной фичи.

🔍 Реaltime SQL Monitor в Oracle SQL Developer

Это инструмент для мониторинга «тяжёлых» SQL-запросов и PL/SQL-блоков в реальном времени.
По сути, это удобная визуализация на базе DBMS_SQLTUNE.REPORT_SQL_MONITOR и данных V$SQL_MONITOR / V$SQL_PLAN_MONITOR.

💡 Чем полезен:

Показывает, на каком шаге плана и в каком объёме Oracle расходует ресурсы.
Позволяет увидеть узкие места в плане выполнения.
Для новичков — отличная визуализация, сразу становится понятней, как именно исполняется запрос.
Для профи — быстрый первый шаг в диагностике «тяжёлых» запросов.
В 90% случаев этого достаточно, чтобы понять, что именно не так с планом.

💎 Поддержка канала⁉️

👨‍💻 А какими IDE пользуетесь Вы и за какие фичи их цените больше всего? Делитесь в комментах 👇
#️⃣ #SQLOptimization #Tools #Oracle
👍4👎1
🎵 «Ах! Крутится, вертится шар голубой,
Крутится, вертится над головой,
Крутится, вертится, хочет упасть.
Кавалер барышню хочет украсть.» — приписывается Ф. Садовскому

📌 Разбор задачи: Поворот матрицы N×N на 90 градусов по часовой стрелке процедурными методами

Представьте, что матрица — это фотография.
Вы берёте её в руки и поворачиваете на 90 градусов.

Компьютер, конечно, руками не умеет, поэтому он делает так:
– сначала кладёт фотографию на бок (транспонирование),
– потом переворачивает каждую строку, как будто листает страницы книги.

Было:        Стало:
1 2 3 7 4 1
4 5 6 → 8 5 2
7 8 9 9 6 3


DECLARE
TYPE row_tab IS TABLE OF NUMBER;

TYPE matrix_tab IS TABLE OF row_tab;

matrix matrix_tab
:= matrix_tab (row_tab (1, 2, 3),
row_tab (4, 5, 6),
row_tab (7, 8, 9));

newMatrix matrix_tab := matrix_tab ();
BEGIN
<<flip_matrix>>
DECLARE
BEGIN
newMatrix := matrix;

FOR i IN 1 .. matrix.COUNT
LOOP
FOR j IN 1 .. matrix (1).COUNT
LOOP
newMatrix (j) (matrix (i).COUNT - i + 1) := matrix (i) (j);
END LOOP;
END LOOP;
END;

<<print_matrix>>
DECLARE
str VARCHAR2 (4000);
BEGIN
FOR i IN 1 .. newMatrix.COUNT
LOOP
str := '';

FOR j IN 1 .. newMatrix (1).COUNT
LOOP
str := str || ' ' || TO_CHAR (newMatrix (i) (j));
END LOOP;

DBMS_OUTPUT.put_line (str);
END LOOP;
END;
END;

👍 Палец вверх — если такие алгоритмы заходят.
👎 Палец вниз — если это всё несерьезно.

⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

💬 Прошу высказываться смело, прямо, грубо и дерзко, но с уважением.
#️⃣ #SQL #Oracle #PLSQL
👍7👎1
Иногда требуется узнать сколько то или иное выражение занимает в байтах в ORACLE/PostgreSql. А знаете ли Вы функцию, которая такое количество вычисляет?
Anonymous Poll
25%
Да
75%
Нет
🎵 «На линии огня
Пустые города
В которых никогда
Ты раньше не бывала.» — БИ2

Сколько байт занимает выражение? Oracle vs PostgreSQL

Часть 1: ORACLE

Иногда разработчику нужно понять: сколько реально памяти занимает то или иное значение в базе?
Например — строка, число или дата.

📍 В Oracle для этого есть функции VSIZE и LENGTHB:
SELECT 
-- strings
vsize(CAST('Y' AS VARCHAR2(1))) AS single_vchar_bytes,
vsize(CAST('Hello' AS VARCHAR2(4000))) AS vchar_bytes,
vsize(CAST('Привет' AS VARCHAR2(4000))) AS russian_vchar_bytes,
vsize(CAST('Hello' AS CHAR(20))) AS char_bytes,

-- numbers
vsize(CAST(1 AS NUMBER)) AS number_bytes,
vsize(CAST(100000000000000000000000000000000000000 AS NUMBER)) AS big_simple_number_bytes,
vsize(CAST(112312312312312312312312312312312312312 AS NUMBER)) AS big_number_bytes,
vsize(CAST(1 AS INTEGER)) AS integer_bytes,
vsize(CAST(100000000000000000000000000000000000000 AS INTEGER)) AS big_simple_integer_bytes,
vsize(CAST(112312312312312312312312312312312312312 AS INTEGER)) AS big_integer_bytes,

-- dates
vsize(SYSDATE) AS date_bytes,
vsize(SYSTIMESTAMP) AS ts_bytes,

-- others
vsize(CAST(HEXTORAW('DEADBEEF') AS RAW(32))) AS raw_bytes
FROM DUAL;

VARCHAR2(1) = 'Y' → 1 байт.
Ровно один символ.
VARCHAR2(20) = 'Hello' → 5 байт.
Количество байт совпадает с числом символов (латиница в AL32UTF8 = 1 байт).
VARCHAR2(20) = 'Привет' → 12 байт.
В AL32UTF8 каждая кириллическая буква кодируется в 2 байта.
CHAR(20) = 'Hello' → 20 байт.
Фиксированная длина, строка добивается пробелами.
NUMBER = 1 → 2 байта.
Маленькое число хранится очень компактно.

NUMBER = 10^38 → 2 байта.
«Простое» большое число, Oracle хранит его в виде степени 10.
NUMBER = 1123123... (38 знаков) → 21 байт.
«Сложное» большое число, почти максимум возможного.
INTEGER = 1 → 2 байта.
Это синоним NUMBER, поэтому хранится так же.
INTEGER = 10^38 → 2 байта.
Поведение то же самое, что у NUMBER.
INTEGER = 1123123... (38 знаков) → 21 байт.
Аналогично NUMBER с максимальной длиной.
DATE = SYSDATE → 7 байт.
Фиксированный внутренний формат: год, месяц, день, час, минута, секунда.
TIMESTAMP = SYSTIMESTAMP → 13 байт.
7 байт как у DATE + до 6 байт для дробных секунд и таймзоны.
RAW(32) = HEXTORAW('DEADBEEF') → 4 байта.
Ровно столько, сколько передано (байты напрямую).

Вот официальная ссылка на документацию: VSIZE
— что делает функция, синтаксис и примеры

💡 Выводы
1️⃣ Для полей-флагов (is_active, is_actual, is_admin) выгоднее использовать VARCHAR2(1) ('Y' / 'N').
2️⃣ Для целых чисел нет разницы между NUMBER и INTEGER. INTEGER — это подтип NUMBER (38,0), просто подчёркивает целочисленный характер поля.
3️⃣ Для полей типа VARCHAR2(4000) хранятся только фактические данные.

По умолчанию, Oracle резервирует 10% свободного пространства блока, чтобы при UPDATE не создавались дополнительные "ссылочные" блоки.
Если таблица статична (строки не изменяются), можно установить модификатор PTCFREE на 0, и без каких-либо последствий можно использовать максимальный размер VARCHAR2 (4000) при объявлении полей.
Если же таблица нестатична, то размер строковых полей требуется делать с запасом, но не более.

⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

💬 Пишите и Ваше мнение будет услышано.
#️⃣ #SQL #Oracle #PLSQL #CodeArchitecture
👍8👎1
🎵 «Кто сказал что безполезно биться головой об стену
Хлоп на лоб, глаза полезли, лоб становиться кpеменным.» — Вячеслав Бутусов

Сколько байт занимает выражение? Oracle vs PostgreSQL

Часть 2: PostgreSQL

📍 В PostgreSQL для этого есть функция pg_column_size:
SELECT
-- strings
pg_column_size(CAST('Y' AS VARCHAR(1)))
AS single_vchar_bytes,
pg_column_size(CAST('Hello' AS VARCHAR(4000)))
AS vchar_bytes,
pg_column_size(CAST('Привет' AS VARCHAR(4000)))
AS russian_vchar_bytes,
pg_column_size(CAST('Hello' AS CHAR(20)))
AS char_bytes,

-- numbers
pg_column_size(CAST(1 AS NUMERIC))
AS number_bytes,
pg_column_size(CAST(9000000000000000000 AS NUMERIC))
AS big_simple_number_bytes,
pg_column_size(CAST(9223372036854775807 AS NUMERIC))
AS big_number_bytes,
pg_column_size(CAST(1 AS BIGINT))
AS bigint_bytes,
pg_column_size(CAST(9000000000000000000 AS BIGINT))
AS big_simple_bigint_bytes,
pg_column_size(CAST(9223372036854775807 AS BIGINT))
AS big_bigint_bytes,

-- date
pg_column_size(CURRENT_DATE)
AS date_bytes,
pg_column_size(CURRENT_TIMESTAMP)
AS ts_bytes,

-- others
pg_column_size(E'\\xDEADBEEF'::bytea)
AS raw_bytes;


'Y'::VARCHAR(1) → 5 байт.
1 символ + служебные 4 байта varlena (хранение длины).
'Hello'::VARCHAR(4000) → 9 байт.
5 ASCII-символов + 4 байта служебной информации.
'Привет'::VARCHAR(4000) → 16 байт.
6 кириллических символов UTF-8 (по 2 байта) + 4 байта служебной информации.
'Hello'::CHAR(20) → 24 байта.
CHAR хранится как фиксированная длина, плюс 4 байта overhead.
1::NUMERIC → 8 байт.
Минимальное число в NUMERIC + служебные байты.
1e10::NUMERIC → 8 байт.
Большое число в NUMERIC, но умещается в минимальном формате.
1123123... (38 знаков)::NUMERIC → 16 байт.
«Сложное» большое число: больше блоков хранения и служебных байт.
1::BIGINT → 8 байт.
BIGINT всегда занимает 8 байт.
9000000000000000000::BIGINT → 8 байт.
Большое число в диапазоне BIGINT → 8 байт.
9223372036854775807::BIGINT → 8 байт.
Максимум для BIGINT, но размер фиксированный.
CURRENT_DATE (DATE) → 4 байта.
Хранится как число дней с 2000-01-01.
CURRENT_TIMESTAMP (TIMESTAMP) → 8 байт.
Хранится как количество микросекунд с 2000-01-01.
E'\\xDEADBEEF'::BYTEA → 8 байт.
4 байта данных + 4 байта служебной информации (varlena).

Вот официальная ссылка на документацию: pg_column_size
— что делает функция, синтаксис.

Почему для каждого значения требуется на 4 байта больше, чем в ORACLE, мне не ясно.
В документации нет понятного объяснения. Но это и не важно, главное, что выводы аналогичные!

💡 Выводы
1) Для полей-флагов (is_active, is_actual, is_admin) выгоднее использовать тип VARCHAR(1) ('Y', 'N')
2) Если использовать целые числа, нет разницы между объявлении NUMERIC и BIGINT в пределах допустимого, просто подчёркивает целочисленный характер поля.
3) Если поля объявлять как VARCHAR(4000), будут храниться, только то что по факту есть.
По умолчанию PostgreSQL ничего не резервирует, т.е. модификатор fillfactor равен 0. Фактически поведение PostgreSQL не зависит от заявленного предела строковых типов.
Если же fillfactor не равен 0, то размер строковых полей требуется делать с запасом, но не более.

⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

💬 Пишите и Ваше мнение будет прочитано.
#SQL #PostgreSQL #PLpgSQL #CodeArchitecture
👍3👎1
Вы используете поле - битовая маска у себя в коде?
Anonymous Poll
24%
Да. Использую
24%
Да. Не использую
51%
Нет. Не знаю
🎵 «Не дорого ценю я громкие слова,
От коих не одна кружится голова.
Я не ропщу о том, что отказали боги
Мне в сладкой участи оспаривать налоги» — Александр Пушкин

📌 Разбор задачи: Проверка на сбалансированные скобки процедурными методами

🧩 Условие:
Нужно проверить, сбалансированы ли скобки в строке.
Скобки считаются сбалансированными, если:
каждой открывающей соответствует закрывающая;
порядок правильный (( { [ ] } )).

⚙️ Алгоритм (кратко):
1️⃣ Создаём стек для хранения открывающихся скобок.
2️⃣ Идём по строке символ за символом.
3️⃣ Если встречаем '(', '{', '[' → кладём в стек.
4️⃣ Если встречаем ')', '}', ']':
• если стек пустой → ошибка (несбалансировано),
• иначе сверяем, что верх стека — соответствующая открывающая скобка.
Если совпадает → убираем её из стека.
5️⃣ После обхода строки стек должен быть пуст.
6️⃣ Если пуст — всё сбалансировано , иначе — нет .

🔢 Пример:
Строка: (124(){}[[] {123()}  ])
Результат: BALANCED

`SQL
DECLARE
str VARCHAR2(4000) := ' (124(){}[[] {123()} ]) ';

TYPE stack_tab IS TABLE OF VARCHAR2 (1);
stack stack_tab := stack_tab();

indexChar VARCHAR2 (1);
isBalanced VARCHAR2 (1) := 'Y';
BEGIN
FOR index# IN 1..LENGTH(str)
LOOP
indexChar := SUBSTR (str, index#, 1);

IF indexChar NOT IN ('(', '{', '[', ')', '}', ']') THEN
CONTINUE;
END IF;

IF indexChar IN ('(', '{', '[') THEN
stack.extend;
stack(stack.COUNT) := indexChar;
CONTINUE;
END IF;

IF indexChar IN (')', '}', ']') AND stack IS EMPTY THEN
isBalanced := 'N';
EXIT;
END IF;

IF indexChar = ')' THEN
IF stack (stack.COUNT) = '(' THEN
stack.delete(stack.COUNT);
CONTINUE;
ELSE
isBalanced := 'N';
EXIT;
END IF;
END IF;

IF indexChar = '}' THEN
IF stack (stack.COUNT) = '{' THEN
stack.delete(stack.COUNT);
CONTINUE;
ELSE
isBalanced := 'N';
EXIT;
END IF;
END IF;

IF indexChar = ']' THEN
IF stack (stack.COUNT) = '[' THEN
stack.delete(stack.COUNT);
CONTINUE;
ELSE
isBalanced := 'N';
EXIT;
END IF;
END IF;
END LOOP;

IF stack IS NOT EMPTY THEN
isBalanced := 'N';
END IF;

stack.delete;

IF isBalanced = 'N' THEN
dbms_output.put_line ('UNBALANCED');
ELSE
dbms_output.put_line ('BALANCED');
END IF;
EXCEPTION
WHEN OTHERS THEN
IF stack IS NOT NULL AND stack IS NOT EMPTY THEN
stack.delete;
RAISE;
END IF;
END;
`


👍 Палец вверх — если такие алгоритмы полезны.
👎 Палец вниз — если тема себя изжила.

⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

💬 Эльвира свой вариант выставила. А остальные? Что скажет "купечество"?
#️⃣ #SQL #Oracle #PLSQL
👍9👎2
image.png
1 MB
🎵 «Мои друзья — хоть не в болонии
Зато не тащут из семьи
А гадость пьют — из экономии
Хоть поутру — да на свои!» — Владимир Высоцкий

Поле как битовая маска

Представьте: у вас есть таблица с кучей полей-флагов.

Например:
is_internal
is_main
is_admin
is_resident
и т.д.

Все эти поля принимают только два значения: 0/1, 'Y'/'N' или аналогичные.

Но проблема в том, что таких флагов может быть десятки, и аналитики каждую неделю просят добавлять новые.
А это значит — переписывать код, менять десятки объектов в БД и заново накатывать на прод. 😩

Чтобы избежать этого ада, используют битовые маски.

🛠 Способы реализации битовых масок
1️⃣ Через INTEGER (NUMBER(38,0))

Каждый бит отвечает за конкретный флаг.

bit_mask =>
POWER(2,0) * CASE WHEN is_internal = 'Y' THEN 1 ELSE 0 END +
POWER(2,1) * CASE WHEN is_main = 'Y' THEN 1 ELSE 0 END +
POWER(2,2) * CASE WHEN is_admin = 'Y' THEN 1 ELSE 0 END +
POWER(2,3) * CASE WHEN is_resident = 'Y' THEN 1 ELSE 0 END

Пример:
is_internal = Y, остальные = N
👉 маска = 0001₂ = 1

Если is_internal=Y и is_resident=Y:
👉 маска = 1001₂ = 9

⚠️ Ограничение: максимум 38 бит.

2️⃣ Через RAW
Можно хранить как двоичные данные.
Пример для is_internal=Y и is_resident=Y:

1001₂ = 09 (HEX)

SQL-пример:
SELECT UTL_RAW.CAST_TO_RAW(CHR(BIN_TO_NUM(1,0,0,1)))
FROM dual;

3️⃣ Через VARCHAR2

Экзотичную битовую маску в виде VARCHAR2:
bit_mask = 'YNNY'
Каждый символ обозначает состояние флага.

⚖️ Баланс
Идея простая: вместо десятков двоичных полей мы храним одно поле с маской.

Но универсальность стоит ресурсов:
придётся нагружать CPU для декодирования маски
возможно нужен справочник, какой бит за что отвечает

🔥 Если тема интересна — далее покажу имплементацию.
Наберём хотя бы 20 🤓.

👍 Палец вверх — тема зашла.
👎 Палец вниз — не зашла.

⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

💬 Справедливую и не очень критику туда. 👇
#SQL #Oracle #PLSQL #CodeArchitecture
👍162
🎵 «Деньги бывают — такое бывает…» — Ленинград

📊 Вычисление дней просрочки по клиентам и счетам

Эта задача встречается на собеседованиях и в банковской аналитике.
Количество дней просрочки — ключевой показатель для расчёта резервов.

🔹 Условие задачи

Таблица движения средств по просроченной задолженности:
amount > 0 → долг возник, день считается первым днём просрочки
amount < 0 → долг уменьшается
Когда накопленный долг = 0 → задолженность погашена, первый и последний день включаются

💡 Важно: погашение происходит по принципу FIFO — сначала гасим старые долги, потом новые,
т.е. если клиент вовремя не погасил долг, то с этого дня начинается рассчет количества дней просрочки.
Если клиент вносил деньги для уплаты долга, то банк обязан сначала эти деньги учесть в самых старых просроченных платежах,
а потом, если остается остаток, то пробует погасить более новые долги.

Возможно, у разработчиков, не знакомых с кредитными задачами, возникнет сложность с логикой.
Во-первых, это нормально! Я только к банковским терминам привыкал полгода (План счетов, Дебет, Кредит, Просрочка, Вынос на просрочку, Погашение, Комиссии, Пенни, и прочий ужас).
Во-вторых, никто не обещал легкой жизни.

🔹 Таблица движения средств
CREATE TABLE account_movements (
client_id NUMBER,
account_id NUMBER,
payment_date DATE,
amount NUMBER
);


🔹 Пример данных
-- Клиент 1 — ежемесячные долги с частичными погашениями, полное погашение
INSERT INTO account_movements VALUES (1, 101, DATE '2024-01-01', 100);
INSERT INTO account_movements VALUES (1, 101, DATE '2024-02-01', 100);
INSERT INTO account_movements VALUES (1, 101, DATE '2024-03-01', 100);
INSERT INTO account_movements VALUES (1, 101, DATE '2024-04-01', 100);
INSERT INTO account_movements VALUES (1, 101, DATE '2024-05-01', 100);
INSERT INTO account_movements VALUES (1, 101, DATE '2024-06-01', 100);

INSERT INTO account_movements VALUES (1, 101, DATE '2024-01-15', -99);
INSERT INTO account_movements VALUES (1, 101, DATE '2024-02-15', -99);
INSERT INTO account_movements VALUES (1, 101, DATE '2024-03-15', -99);
INSERT INTO account_movements VALUES (1, 101, DATE '2024-04-15', -99);
INSERT INTO account_movements VALUES (1, 101, DATE '2024-05-15', -99);
INSERT INTO account_movements VALUES (1, 101, DATE '2024-06-15', -99);

INSERT INTO account_movements VALUES (1, 101, DATE '2024-07-01', -6);

-- Клиент 2 — медленные погашения
INSERT INTO account_movements VALUES (2, 201, DATE '2024-01-01', 50);
INSERT INTO account_movements VALUES (2, 201, DATE '2024-02-01', 50);
INSERT INTO account_movements VALUES (2, 201, DATE '2024-03-01', 50);
INSERT INTO account_movements VALUES (2, 201, DATE '2024-04-01', 50);
INSERT INTO account_movements VALUES (2, 201, DATE '2024-05-01', 50);
INSERT INTO account_movements VALUES (2, 201, DATE '2024-06-01', 50);

INSERT INTO account_movements VALUES (2, 201, DATE '2024-02-15', -20);
INSERT INTO account_movements VALUES (2, 201, DATE '2024-03-20', -30);
INSERT INTO account_movements VALUES (2, 201, DATE '2024-05-10', -50);

INSERT INTO account_movements VALUES (2, 201, DATE '2024-08-01', -150);

-- Клиент 3 — не погашения
INSERT INTO account_movements VALUES (3, 301, DATE '2024-01-01', 200);
INSERT INTO account_movements VALUES (3, 301, DATE '2024-02-01', 200);
INSERT INTO account_movements VALUES (3, 301, DATE '2024-03-01', 200);
INSERT INTO account_movements VALUES (3, 301, DATE '2024-04-01', 200);
INSERT INTO account_movements VALUES (3, 301, DATE '2024-05-01', 200);
INSERT INTO account_movements VALUES (3, 301, DATE '2024-06-01', 200);

💡 Ожидаемый результат на 15.05.2024

Клиент Задолженность Дней просрочки Период
1 5 15 2024-05-01 – 2024-05-15
2 150 76 2024-03-01 – 2024-05-15
3 1000 136 2024-01-01 – 2024-05-15

В реальной жизни долги бывают разных видов, но принцип одинаков.

⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

👍 Банковская тема интересна
👎 Такой ужас видеть больше не хочу
#️⃣ #RealInterviewTasks #SQL #Oracle #PLSQL #PostgreSQL
👍5👎1
🎵 «Если друг оказался вдруг
И не друг, и не враг, а — так,
Если сразу не разберёшь,
Плох он или хорош...» — Владимир Высоцкий

Сегодня суббота — разбор задач оставим на будущее.
Продолжаем рубрику «Полезные ресурсы».

🖥️ Тема: Работа с Git и анализ кода

Раньше для работы с Git я использовал Git Extensions
для управления репозиториями, а для поиска и анализа файлов — Total Commander и Notepad++.

Но коллеги помогли (или слегка заставили 😉) перейти на JetBrains IntelliJ IDEA.
Сложная и мощная программа, к ней нужно привыкнуть, но она того стоит!
Особенно мне нравится поиск по ключевым словам и файлам, а также встроенные инструменты для анализа кода.
Вообще, если вы работаете с проектами в Git и большими кодовыми базами — рекомендую попробовать.

💎 Поддержка канала⁉️

👨‍💻 А вы используете Git? Какими инструментами для анализа файлов пользуетесь?
Делитесь в комментариях 👇
#️⃣ #Tools
1👍1🔥1
🎵 «Две звезды - две светлых повести,
В своей любви, как в невесомости.
Два голоса среди молчания,
В небесном храме звезд венчание.» — Алла Пугачева

📌 Разбор задачи: Два указателя — проверить, содержит ли отсортированный массив два числа, сумма которых равна заданному числу target.

Представьте, что у вас есть ряд чисел, и вам нужно найти пару, которая даёт нужную сумму.
Алгоритм (кратко):
– ставит «левый» указатель на начало массива,
– «правый» указатель — в конец,
– двигает их навстречу друг другу в зависимости от суммы текущей пары.

DECLARE
TYPE set_tab IS TABLE OF INTEGER;

"set" set_tab := set_tab (0,1,2,3,4,5,6,7,8,10);
target INTEGER := 15;
currentTarget INTEGER := 0;
leftIndex INTEGER := 1;
rightIndex INTEGER := "set".LAST;
isTargetExist VARCHAR2(1) := 'N';
BEGIN
WHILE leftIndex != rightIndex LOOP
currentTarget := "set"(leftIndex) + "set"(rightIndex);

IF currentTarget = target THEN
isTargetExist := 'Y';
EXIT;
ELSIF currentTarget > target THEN
rightIndex := rightIndex - 1;
ELSE
leftIndex := leftIndex + 1;
END IF;
END LOOP;

IF isTargetExist = 'Y' THEN
DBMS_OUTPUT.put_line('exist');
ELSE
DBMS_OUTPUT.put_line('not exist');
END IF;
END;

💡 Логика проста:
– Если сумма больше нужной — двигаем правый указатель влево;
– Если сумма меньше — двигаем левый указатель вправо;
– Если нашли — выход!

👍 Палец вверх — если нравится.
👎 Палец вниз — если не нравится.

⚠️ Хотите проверять скрипты без базы — используйте онлайн-песочницу.
💎 Поддержка канала⁉️

💬 Есть что добавить? 👇
#️⃣ #SQL #Oracle #PLSQL
👍41👎1
«Вы, конечно, стоите на краю финансовой пропасти? — спросил он Балаганова.
Это вы насчет денег? — сказал Шура. — Денег у меня нет уже целую неделю.
В таком случае вы плохо кончите, молодой, человек, — наставительно сказал Остап.
Финансовая пропасть-самая глубокая из всех пропастей, в нее можно падать всю жизнь.»
— Илья Ильф и Евгений Петров.

📊 Разбор задачи «Вычисление дней просрочки по клиентам и счетам»

"Пара слов без протокола"...
В банках подготовка данных для получения сводной таблицы долгов и погашений занимает приличное время.
Дело в том, что есть разные виды задолженностей и разный приоритет погашений для каждого вида задолженности.
А также есть корректирующие операции, которые проводятся задним числом.
Вообщем, это демо модель и я не претендую, что она оптимальная, так как у неё есть недостатки.
Недостаток в том, что приходится считать FIFO баланс даже тогда, когда уже в этом нет необходимости.
Из-за этого, я видел и процедурную реализацию данной задачи.
Какое решение подойдет именно Вам, нужно тестировать и разбираться.

Теперь разберем решение на SQL.

🔹 Шаг 1. Движение и накопленный FIFO-баланс
Сначала нам нужно посчитать «накопленный долг» на каждый день с учётом FIFO.
Для этого мы суммируем все возникшие долги (amount > 0) по дате, и отдельно все погашения (amount < 0) по счёту:
SELECT am.*,
SUM(CASE WHEN am.amount > 0 THEN am.amount ELSE 0 END)
OVER (PARTITION BY am.client_id, am.account_id
ORDER BY am.payment_date)
+ SUM(CASE WHEN am.amount < 0 THEN am.amount ELSE 0 END)
OVER (PARTITION BY am.client_id, am.account_id) AS fifo_balance
FROM account_movements am
WHERE am.payment_date <= DATE '2024-05-15'


👉 Здесь ключевая идея:
Долги мы считаем, как накопительную сумму.
Погашения долга считаем, как общую сумму по клиенту и по договору (счету)

🔹 Шаг 2. Определяем дату начала просрочки
Теперь для каждого клиента/счёта нужно найти первую дату, когда долг стал больше 0:
MIN(CASE WHEN am1.fifo_balance > 0 THEN am1.payment_date END) AS first_overdue_date

Если долг никогда не возникал → просрочки нет.

🔹 Шаг 3. Рассчитываем количество дней просрочки

Если у клиента есть дата первой просрочки, то количество дней = разница между контрольной датой и этой датой (оба дня включаются):
CASE
WHEN first_overdue_date IS NULL THEN 0
ELSE DATE '2024-05-15' - first_overdue_date + 1
END AS overdue_day_amount


Итого:
SELECT am2.client_id,
am2.account_id,
CASE
WHEN first_overdue_date IS NULL THEN 0
ELSE DATE '2024-05-15' - first_overdue_date + 1
END AS overdue_day_amount
FROM (
SELECT am1.client_id,
am1.account_id,
MIN(CASE WHEN am1.fifo_balance > 0 THEN am1.payment_date END) AS first_overdue_date
FROM (
SELECT am.*,
SUM(CASE WHEN am.amount > 0 THEN am.amount ELSE 0 END)
OVER (PARTITION BY am.client_id, am.account_id
ORDER BY am.payment_date)
+ SUM(CASE WHEN am.amount < 0 THEN am.amount ELSE 0 END)
OVER (PARTITION BY am.client_id, am.account_id) AS fifo_balance
FROM account_movements am
WHERE am.payment_date <= DATE '2024-05-15'
) am1
GROUP BY am1.client_id, am1.account_id
) am2;

💡 Что получилось
Клиент Количество дней просрочки
1 15
2 76
3 136

Есть еще интересней задачка на эту тему - поиск максимального количества дней просрочки по договору.
Задача не про заданную дату, а ретроспективно на всё время жизни договора.
Клиент мог выходить на просрочку несколько раз и в данный момент её не иметь.

👍 Палец вверх — банковская тема интересна.
👎 Палец вниз — Какая муть! Мне это категорически не интересно.

⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

💬 Хотите что-то добавить. 👇
#️⃣ #RealInterviewTasks #SQL #Oracle #PLSQL #PostgreSQL
👍12👎1
🎵 «Пусть все будет так, как ты захочешь.
Пусть твои глаза, как пpежде, гоpят.
Я с тобой опять сегодня этой ночью.
Hу а впpочем, следующей ночью,
Если захочешь, я опять у тебя.» — Чайф.

📌 Случай из практики

Допустим, у нас есть таблица:
CREATE TABLE something (
id NUMBER,
col1 VARCHAR2(20),
col2 VARCHAR2(10),
col3 VARCHAR2(500),
created DATE,
created_by NUMBER,
updated DATE,
updated_by NUMBER
);

🔹 Таблица небольшая, но часто читаемая. Записи в ней редактируются, но не слишком активно.

Требование: изменить поле col1 → нужно перевести его в тип NUMBER и пересчитать значения.
Например:
CASE 
WHEN col1 = 'value1' THEN 1
WHEN col1 = 'value2' THEN 2
ELSE 3
END

Казалось бы, задача простая:

1. Добавляем новое поле col1_new.
2. Заполняем его как нужно.
3. Переименовываем старое поле в col1_old, а новое — в col1.
4. Если всё прошло успешно — col1_old можно убрать или пометить как неиспользуемое.

Но есть дополнительное условие:
Новое поле не должно сместиться в конец списка, а должно остаться на своём месте — сразу после id и перед col2.

🤔 Как бы вы справились с такой задачей?

⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️

💬 Ваши мысли и Ваши решения туда. 👇
#️⃣ #Cases #SQL #Oracle #PLSQL #PostgreSQL #PLpgSQL
👍2👎1