🎵 «Мы себе давали слово - не сходить с пути прямого
Но так уж суждено, хмм
И уж если откровенно - всех пугают перемены
Но - тут уж всё равно, хмм» — Андрей Макаревич
📌 Разбор задачи: Транспонирование матрицы
🧩 Условие:
Дана матрица M x N. Нужно построить новую матрицу N x M, в которой строки становятся столбцами, а столбцы — строками.
🔢 Пример:
⚙️ Алгоритм (кратко):
1️⃣ Создаём пустую матрицу размером, равным транспонированной.
2️⃣ Для каждой ячейки matrix(i, j) кладём её значение в transposedMatrix(j, i).
3️⃣ Выводим результат.
👍 Палец вверх — если тема заходит.
👎 Палец вниз — если пора что-то сменить.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
💬 Как Вам такой поворот? Ответы и прочее там. 👇
#️⃣ #SQL #Oracle #PLSQL
Но так уж суждено, хмм
И уж если откровенно - всех пугают перемены
Но - тут уж всё равно, хмм» — Андрей Макаревич
📌 Разбор задачи: Транспонирование матрицы
🧩 Условие:
Дана матрица M x N. Нужно построить новую матрицу N x M, в которой строки становятся столбцами, а столбцы — строками.
🔢 Пример:
Исходная матрица:
1 2 3
4 5 6
👉 Результат:
1 4
2 5
3 6
⚙️ Алгоритм (кратко):
1️⃣ Создаём пустую матрицу размером, равным транспонированной.
2️⃣ Для каждой ячейки matrix(i, j) кладём её значение в transposedMatrix(j, i).
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));
transposedMatrix matrix_tab := matrix_tab ();
BEGIN
<<create_trasposed_matrix>>
DECLARE
row row_tab := row_tab();
BEGIN
row.EXTEND(matrix.COUNT);
FOR i IN 1 .. matrix(1).COUNT LOOP
transposedMatrix.EXTEND;
transposedMatrix(transposedMatrix.COUNT) := row;
END LOOP;
FOR i IN 1 .. matrix.COUNT LOOP
FOR j IN 1 .. matrix(i).COUNT LOOP
transposedMatrix(j)(i) := matrix(i)(j);
END LOOP;
END LOOP;
END;
<<print_matrix>>
DECLARE
str VARCHAR2(100);
BEGIN
FOR i IN 1 .. transposedMatrix.COUNT LOOP
str := '';
FOR j IN 1 .. transposedMatrix(i).COUNT LOOP
str := str || ' ' || TO_CHAR(transposedMatrix(i)(j));
END LOOP;
DBMS_OUTPUT.put_line(str);
END LOOP;
END;
END;
👍 Палец вверх — если тема заходит.
👎 Палец вниз — если пора что-то сменить.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
💬 Как Вам такой поворот? Ответы и прочее там. 👇
#️⃣ #SQL #Oracle #PLSQL
👍9👎2💊2
script_copy_complex_structure_table.sql
28.5 KB
🎵 «Товаришч, товаришч, скыжи моей ты мамене
Что сын ее погибнул на войне
И с шашкою в рукою, с винтовкою в другою
И с песнею веселой на губе» — приписывается Борису Тимофееву
📢 Копирование сложной структуры таблицы, или как косячит ИИ 🤖
Итак, постановка реальной задачи.
Есть две таблицы TEST.DEMO_PRIMARY_TABLE, TEST.DEMO_SECONDARY_TABLE.
По структуре они должны быть идентичны — отличаться только по имени таблицы PRIMARY ↔ SECONDARY и по названиям объектов: _P_ ↔ _S_.
Требуется скрипт, который бы динамически: при наличии TEST.DEMO_PRIMARY_TABLE создавал TEST.DEMO_SECONDARY_TABLE и наоборот — без данных.
При этом — правильное соответствие в названиях объектов.
🔎 Зачем это нужно
В DWH реализован механизм «двух ног» через синонимы: для каждого среза данных есть 2 таблицы — PRIMARY и SECONDARY.
Они абсолютно идентичны, отличаются только названием таблицы и названиями объектов.
Когда срез пересобирают (новые поля / логика / баги), один срез заполняется, второй остаётся доступным для потребителей.
После завершения — таблицы меняют местами через пересоздание синонима.
Со временем от этого механизма отказались, потому что данных стало слишком много — пересоздание срезов и открытие доступа создавали большую нагрузку.
Пересоздавать срезы начали на резервном сервере, и лишние таблицы стали не нужны.
Удалить их относительно легко — но для каждого наката нужен скрипт отката.
А вот его создать не так-то просто.
Тестовых сред много, и в каждой среде тестировщики могли пересоздавать срезы каждый день — заранее знать, какая таблица актуальна в какой среде, нельзя.
Никто не гарантирует, что везде имена и состав объектов одинаковы.
🤖 А как же косячит Терминатор?
Он не справился с задачей: даже после 4 часов подбора промптов получались разные ошибки и несоответствия. Доверие к ИИ немного подорвано — зато задача стала яснее. Пришлось взять за основу более или менее подходящий скрипт от ИИ и работать самому.
Джон Коннор жив!
🤓 - Ставим, если хотите подробный разбор скрипта.
Если соберем более 20 🤓, то сделаю подробный разбор.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
✍️ Пишите свои мысли в комментариях!
#Cases #SQL #Oracle #PLSQL
Что сын ее погибнул на войне
И с шашкою в рукою, с винтовкою в другою
И с песнею веселой на губе» — приписывается Борису Тимофееву
📢 Копирование сложной структуры таблицы, или как косячит ИИ 🤖
Итак, постановка реальной задачи.
Есть две таблицы TEST.DEMO_PRIMARY_TABLE, TEST.DEMO_SECONDARY_TABLE.
По структуре они должны быть идентичны — отличаться только по имени таблицы PRIMARY ↔ SECONDARY и по названиям объектов: _P_ ↔ _S_.
Требуется скрипт, который бы динамически: при наличии TEST.DEMO_PRIMARY_TABLE создавал TEST.DEMO_SECONDARY_TABLE и наоборот — без данных.
При этом — правильное соответствие в названиях объектов.
-- primary
CREATE TABLE TEST.demo_primary_table
(
id NUMBER NOT NULL,
col1 VARCHAR2 (50 CHAR) NOT NULL,
col2 TIMESTAMP (6) WITH TIME ZONE NOT NULL,
col3 DATE,
col4 NUMBER NOT NULL,
col5 RAW (16),
col6 INTEGER,
col7 TIMESTAMP (6),
col8_id NUMBER,
col8 VARCHAR2 (100),
col3_trunc AS (TRUNC ("COL3"))
)
TABLESPACE USERS;
COMMENT ON TABLE test.demo_primary_table IS 'PRIMARY';
COMMENT ON COLUMN TEST.demo_primary_table.col1 IS 'col1';
COMMENT ON COLUMN TEST.demo_primary_table.col3_trunc IS 'col3_trunc';
CREATE BITMAP INDEX TEST.DEMO_TABLE_P_BMI01
ON TEST.demo_primary_table (col1)
TABLESPACE USERS;
CREATE BITMAP INDEX TEST.DEMO_TABLE_P_BMI02
ON TEST.demo_primary_table (col3_trunc)
TABLESPACE USERS;
CREATE INDEX TEST.DEMO_TABLE_P_I01
ON TEST.demo_primary_table (id)
TABLESPACE USERS;
CREATE INDEX TEST.DEMO_TABLE_P_I02
ON TEST.demo_primary_table (col4, col1, col3)
TABLESPACE USERS;
ALTER TABLE TEST.demo_primary_table
ADD CONSTRAINT DEMO_TABLE_P_C01 CHECK
( (col8 IS NOT NULL AND col8_id IS NOT NULL)
OR (col8 IS NULL AND col8_id IS NULL));
🔎 Зачем это нужно
В DWH реализован механизм «двух ног» через синонимы: для каждого среза данных есть 2 таблицы — PRIMARY и SECONDARY.
Они абсолютно идентичны, отличаются только названием таблицы и названиями объектов.
Когда срез пересобирают (новые поля / логика / баги), один срез заполняется, второй остаётся доступным для потребителей.
После завершения — таблицы меняют местами через пересоздание синонима.
Со временем от этого механизма отказались, потому что данных стало слишком много — пересоздание срезов и открытие доступа создавали большую нагрузку.
Пересоздавать срезы начали на резервном сервере, и лишние таблицы стали не нужны.
Удалить их относительно легко — но для каждого наката нужен скрипт отката.
А вот его создать не так-то просто.
Тестовых сред много, и в каждой среде тестировщики могли пересоздавать срезы каждый день — заранее знать, какая таблица актуальна в какой среде, нельзя.
Никто не гарантирует, что везде имена и состав объектов одинаковы.
🤖 А как же косячит Терминатор?
Он не справился с задачей: даже после 4 часов подбора промптов получались разные ошибки и несоответствия. Доверие к ИИ немного подорвано — зато задача стала яснее. Пришлось взять за основу более или менее подходящий скрипт от ИИ и работать самому.
Джон Коннор жив!
🤓 - Ставим, если хотите подробный разбор скрипта.
Если соберем более 20 🤓, то сделаю подробный разбор.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
✍️ Пишите свои мысли в комментариях!
#Cases #SQL #Oracle #PLSQL
🤓13⚡1❤1👎1💊1
🎵 «Мы колесили по дорогам,
Меняя струны и подруг,
О, нам не хватало рук,
И если все добро от Бога -
Нам не светит теплый рай,
Сколько ни играй,
Это просто замкнутый круг.» — Ария
📌 Разбор задачи: Определение цикла в связном списке
🧩 Условие:
Дан связный список. Необходимо определить, содержит ли он цикл (т.е. есть ли узел, в который можно попасть снова, двигаясь по ссылкам NEXT).
⚙️ Алгоритм (кратко):
1️⃣ Заводим два указателя — медленный (slow) и быстрый (fast).
2️⃣ slow двигается по одному шагу, а fast — по два.
3️⃣ Если в какой-то момент они указывают на один и тот же элемент — цикл существует.
4️⃣ Если fast дошёл до конца (NULL) — цикла нет.
🔢 Пример работы:
Код на PL/SQL
👍 Палец вверх — если тема с алгоритмами продолжает заходить.
👎 Палец вниз — если пора сменить трек.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
💬 А у вас в коде бывали «замкнутые круги»? 👇
#️⃣ #SQL #Oracle #PLSQL
Меняя струны и подруг,
О, нам не хватало рук,
И если все добро от Бога -
Нам не светит теплый рай,
Сколько ни играй,
Это просто замкнутый круг.» — Ария
📌 Разбор задачи: Определение цикла в связном списке
🧩 Условие:
Дан связный список. Необходимо определить, содержит ли он цикл (т.е. есть ли узел, в который можно попасть снова, двигаясь по ссылкам NEXT).
⚙️ Алгоритм (кратко):
1️⃣ Заводим два указателя — медленный (slow) и быстрый (fast).
2️⃣ slow двигается по одному шагу, а fast — по два.
3️⃣ Если в какой-то момент они указывают на один и тот же элемент — цикл существует.
4️⃣ Если fast дошёл до конца (NULL) — цикла нет.
🔢 Пример работы:
Список: 1 → 2 → ... → 9 → 10
↑ ↓
└─────────────┘
👉 Есть цикл (10 → 2)
Код на PL/SQL
CREATE OR REPLACE TYPE node AS OBJECT (
value NUMBER,
next REF node
);
CREATE TABLE nodes OF node;
DECLARE
TYPE list_tab IS TABLE OF REF node;
"list" list_tab := list_tab();
isExistCycle VARCHAR2 (1) := 'N';
listElementCount INTEGER := 10;
BEGIN
<<build_initial_list>>
DECLARE
newElement REF node;
BEGIN
FOR index# IN 1 .. listElementCount
LOOP
INSERT INTO nodes n
VALUES (node (VALUE => index#, NEXT => NULL))
RETURNING REF (n)
INTO newElement;
"list".extend;
"list" ("list".COUNT) := newElement;
IF index# > 1
THEN
UPDATE nodes n
SET n.NEXT = "list" (index#)
WHERE REF (n) = "list" (index# - 1);
END IF;
END LOOP;
END;
<<add_circle_to_list>>
DECLARE
BEGIN
UPDATE nodes n
SET n.NEXT = "list" (2)
WHERE REF (n) = "list" (listElementCount);
END;
<<is_exists_circle>>
DECLARE
slow REF node := "list" (1);
fast REF node := "list" (1);
BEGIN
WHILE fast IS NOT NULL
LOOP
SELECT DEREF (fast).NEXT INTO fast FROM DUAL;
EXIT WHEN fast IS NULL;
SELECT DEREF (fast).NEXT INTO fast FROM DUAL;
SELECT DEREF (slow).NEXT INTO slow FROM DUAL;
IF slow = fast
THEN
isExistCycle := 'Y';
EXIT;
END IF;
END LOOP;
END;
"list".delete;
IF isExistCycle = 'Y'
THEN
DBMS_OUTPUT.put_line ('The cycle is found!');
ELSE
DBMS_OUTPUT.put_line ('The cycle is not fount');
END IF;
EXCEPTION
WHEN OTHERS THEN
IF "list" IS NOT NULL AND "list" IS NOT EMPTY THEN
"list".delete;
END IF;
RAISE;
END;
👍 Палец вверх — если тема с алгоритмами продолжает заходить.
👎 Палец вниз — если пора сменить трек.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
💬 А у вас в коде бывали «замкнутые круги»? 👇
#️⃣ #SQL #Oracle #PLSQL
👍10❤1💊1
🎭 «Гость долго грустил и дергался, но наконец заговорил:
— Видите ли, какая странная история, я здесь сижу из-за того же, что и вы, именно из-за Понтия Пилата, — тут гость пугливо оглянулся и сказал: — Дело в том, что год тому назад я написал о Пилате роман.
— Вы — писатель? — с интересом спросил поэт.
Гость потемнел лицом и погрозил Ивану кулаком, потом сказал:
— Я — мастер»
— М.А. Булгаков, «Мастер и Маргарита»
✨ Сегодня суббота — отдыхаем от задач.
Рубрика «Полезные ресурсы»
Все, кто работает с Oracle, рано или поздно сталкиваются с оптимизацией запросов. И тут появляются хинты — инструмент тонкой настройки работы оптимизатора.
📌 Лучший ресурс по этой теме (и мой личный фаворит) — материалы Игоря Усольцева.
👉 https://iusoltsev.wordpress.com/profile/individual-sql-and-cbo/cbo-hints/
Там всё настолько кратко и по делу, что даже добавлять особо нечего.
Хотел бы спросить у читателей:
👀 Интересен ли вам разбор хинтов прямо на канале и их применение на практике?
👍 Палец вверх — разберём на примерах.
👎 Палец вниз — оставим, как есть, и будем ссылаться на первоисточник.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
💡 Делитесь своими любимыми авторами там. 👇
#tools
— Видите ли, какая странная история, я здесь сижу из-за того же, что и вы, именно из-за Понтия Пилата, — тут гость пугливо оглянулся и сказал: — Дело в том, что год тому назад я написал о Пилате роман.
— Вы — писатель? — с интересом спросил поэт.
Гость потемнел лицом и погрозил Ивану кулаком, потом сказал:
— Я — мастер»
— М.А. Булгаков, «Мастер и Маргарита»
✨ Сегодня суббота — отдыхаем от задач.
Рубрика «Полезные ресурсы»
Все, кто работает с Oracle, рано или поздно сталкиваются с оптимизацией запросов. И тут появляются хинты — инструмент тонкой настройки работы оптимизатора.
📌 Лучший ресурс по этой теме (и мой личный фаворит) — материалы Игоря Усольцева.
👉 https://iusoltsev.wordpress.com/profile/individual-sql-and-cbo/cbo-hints/
Там всё настолько кратко и по делу, что даже добавлять особо нечего.
Хотел бы спросить у читателей:
👀 Интересен ли вам разбор хинтов прямо на канале и их применение на практике?
👍 Палец вверх — разберём на примерах.
👎 Палец вниз — оставим, как есть, и будем ссылаться на первоисточник.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
💡 Делитесь своими любимыми авторами там. 👇
#tools
👍28👎1💊1
✨ Друзья! Вчерашний пост набрал 17 — и это в субботу! 🔥 Я в шоке (приятном). SQL-оптимизация — моё любимое дело ❤️. Но хочу уточнить: с какими трудностями вы сталкиваетесь? Я продумаю курс обучающих постов на эту тему. Итак, возможные трудности:
Anonymous Poll
11%
1️⃣ Я начинающий и у меня сложности буквально со всем.
4%
2️⃣ Мне трудно сочинять, разбирать и переписывать сложные запросы.
30%
3️⃣ У меня проблемы с пониманием планов запросов как таковых.
24%
4️⃣ Простые планы я понимаю, но если план большой и состоит из множества строк — теряюсь.
11%
5️⃣ Я читаю даже самые сложные планы, но мне тяжело находить узкие места и решать проблемы.
11%
6️⃣ Я профи в оптимизации, но хочу читать ваши посты для обмена опытом.
9%
🙃 Боже-Боже! Докатились до того, что начинаем изучать базу.
🎵 «Перемен требуют наши сердца!
Перемен требуют наши глаза!
В нашем смехе, и в наших слезах, и в пульсации вен
Перемен! Мы ждём перемен!.» — Кино
📌 Разбор задачи: Расстояние редактирования (Levenshtein distance)
🧩 Условие:
Даны две строки. Нужно вычислить минимальное количество операций (вставка, удаление, замена), необходимых для преобразования одной строки в другую.
⚙️ Алгоритм (кратко):
1️⃣ Создаём матрицу, где строки — символы первой строки, а столбцы — символы второй.
2️⃣ Заполняем первую строку и первый столбец начальными значениями (количество операций для пустых префиксов).
3️⃣ Для каждой позиции выбираем минимум из трёх вариантов:
• замена (если символы разные),
• вставка,
• удаление.
4️⃣ В правом нижнем углу матрицы получаем ответ.
🔢 Пример работы:
👉 Результат: расстояние редактирования = 6
(чтобы из "carrot" получилось "password").
Код на PL/SQL:
👍 Палец вверх — если алгоритмы продолжают заходить.
👎 Палец вниз — если "капусточка" дело хорошее, но пора переходить на мясные блюда.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
💬 Есть мысли? Хотите перемен? - Пишите. 👇
#️⃣ #SQL #Oracle #PLSQL
Перемен требуют наши глаза!
В нашем смехе, и в наших слезах, и в пульсации вен
Перемен! Мы ждём перемен!.» — Кино
📌 Разбор задачи: Расстояние редактирования (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-запросами!
Предлагаю "лёгкую" разминку для мозга. 🧠
Есть таблица с перечнями слов:
❗ Задача:
Создать таблицу words и заполнить её одним универсальным SQL-запросом так, чтобы каждый элемент списка из word_list оказался в отдельной строке:
📊 Ожидаемый результат:
✅ Фактически, требуется нормализовать таблицу word_lists.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
💬 Если есть варианты решения, предлагайте. 👇
#️⃣ #RealInterviewTasks #SQL #Oracle #PostgreSQL #PLSQL #PLpgSQL
Мы тарелки бьём весь год —
Мы на них уже собаку съели,
Если повар нам не врёт.» — Владимир Высоцкий
💡 Интересный трюк с 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
👉 Здесь интересный момент в использовании DBMS_RANDOM.value.
Поскольку внутри одной строки будут одинаковые узлы (wl.id, REGEXP_COUNT(wl.word_list, ',')), требуется уникальное значение для корректного CONNECT BY.
Но читатели ✨ Эльвира и ✨ Евгений предложили свои методы. Браво! 🙌
Я весь день ходил довольный, как слон 🐘
Ничто так не поднимает настроение, как хорошо написанный SELECT.
💡 Решение от Эльвиры — элегантное и простое. Снимаю шляпу 🎩
Я его чуть-чуть переделал:
💡 Решение от Евгения интересное — я так не умею 🤯
Я его немного сократил, и вот что получилось:
Также предлагаю вариант используя MODEL
👉 Фишка этого метода в том, что мы используем методы динамического программирования и простые функции INSTR и SUBSTR
и не работаем с регулярками и XMLTYPE.
Известно, что и то и другое довольно жадное на ресурсы CPU.
🔹 Решение для PostgreSQL
В PostgreSQL такой приём делается проще 🚀
Хотя я и недолюбливаю PostgreSQL, но в данной ситуации он ведёт себя гибче:
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
💬 Если придумаете ещё новый вариант — пишите 👇
#️⃣ #RealInterviewTasks #SQL #Oracle #PostgreSQL #PLSQL #PLpgSQL
Готовят просвещенья дух
И Опыт, сын ошибок трудных,
И Гений, парадоксов друг,
И Случай, бог изобретатель.» — Александр Пушкин
📌 Разбор интересной 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
👍5❤1 1
🎵 «Ой, где был я вчера — не найду, хоть убей,
Только помню, что стены с обоями.
Помню, Клавка была и подруга при ней,
Целовался на кухне с обоими.» — Владимир Высоцкий
📌 Задача на тему иерархических запросов.
Задачу выслал читатель Max Litynskyi. Выражаю ему большую благодарность за развитие канала.
У нас есть таблица room_links с двумя полями:
Каждая строка таблицы — это прямой проход между двумя комнатами.
Например:
(Кухня, Коридор) — значит, есть дверь из Кухни в Коридор.
(Коридор, Спальня) — значит, из Коридора можно попасть в Спальню.
🔍 Задача: найти кратчайший путь между двумя заданными комнатами, например: Кухня → Спальня.
🏠 Тестовые данные
Таким образом, у нас получится «карта помещений» из 50 комнат, соединённых случайными дверями.
👉 Требуется составить SQL запрос, для нахождения кратчайшего пути между двумя заданными комнатами.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
💬 Если есть варианты решения, предлагайте. 👇
#️⃣ #RealInterviewTasks #SQL #Oracle #PostgreSQL #PLSQL #PLpgSQL
Только помню, что стены с обоями.
Помню, Клавка была и подруга при ней,
Целовался на кухне с обоими.» — Владимир Высоцкий
📌 Задача на тему иерархических запросов.
Задачу выслал читатель 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
Продолжим или закончим 4 часть "Мерлезонского балета" - решение задач методами процедурного языка. Я выбрал очередные 5 задач из 5 тем: работа с массивами, работа со строками, работа с матрицами, структуры данных, динамическое программирование.
Anonymous Poll
8%
Поворот на 90 градусов: Поверните квадратную матрицу на 90 градусов по часовой стрелке.
6%
Проверка на сбалансированные скобки: Проверьте, сбалансированы ли все скобки в строке.
3%
Два указателя: Проверьте, содержит ли отсортированный массив два числа, сумма которых равна заданном
3%
Удаление из списка: Удалите N-ый узел с конца связного списка.
6%
Последовательность Фибоначчи: Вычислите n-е число Фибоначчи, используя динамическое программирование
28%
Я разработчик DB. Всё остальное лишнее.
47%
Мне интересно всё.
🎵 «Если сто раз с утра всё не так
Если пришла пора сделать шаг
Если ты одинок
Значит, настал твой срок
И ждёт за углом
Перекрёсток семи дорог.» — Андрей Макаревич
🔍 Решение задачи поиска нужного маршрута среди комнат
Задача сводится к поиску пути в графе.
В Oracle для этого удобно использовать иерархический запрос (CONNECT BY).
✨ Трюк в том, что нужно:
Искать все возможные пути от стартовой комнаты 🚪
Отсекать зацикливания 🔄
Оставить только минимальную длину 📏
✅ Что делает запрос
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
Если пришла пора сделать шаг
Если ты одинок
Значит, настал твой срок
И ждёт за углом
Перекрёсток семи дорог.» — Андрей Макаревич
🔍 Решение задачи поиска нужного маршрута среди комнат
Задача сводится к поиску пути в графе.
В 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
👍2❤1👎1
🎵 «Нагружать все больше нас
Стали почему-то,
Нынче в школе первый класс -
Вроде института.
Нам учитель задает
С иксами задачи,
Кандидат наук и тот -
Над задачей плачет.» — Алла Пугачева
Этим постом я начинаю (и, возможно, заканчиваю) серию «Оптимизация SQL-запросов».
📌 Сегодня вводная для новичков.
📊 Небольшой опрос среди 46 читателей канала показал:
11% — начинающие,
2% — испытывают трудности со сложными запросами.
Это вроде бы мало, чтобы разворачивать целый курс для новичков.
Но и много, чтобы игнорировать и не дать им «маршрут» обучения.
💡 Мои советы
0) SQL — навык, а не «родной язык»
Никто не рождается с пониманием SQL. Все проходят одинаковые стадии — от 🤯 недоумения до 🙌 принятия.
Главный путь — практика. В какой-то момент SQL станет для вас логичным и понятным. Дальше останется только накапливать трюки (и их не так уж много).
1) Настройте локальную среду ⚙️
Создайте место, где можно спокойно экспериментировать, создавать таблицы и объекты, не боясь «сломать прод».
Oracle и PostgreSQL отлично подходят для локальной установки.
Даже если что-то пошло не так — всегда можно всё переустановить.
2) Учитесь по книгам 📚
Для тех, кто не пишет SQL ежедневно, главная трудность — синтаксис. Он специфичный, и к нему нужно привыкнуть.
Но уже через 2–3 недели он станет естественным.
👉 Для старта советую:
«Секреты ORACLE SQL
» — но вообще любая понятная вам книга подойдёт.
3) Разбирайте сложные запросы по шагам 🪜
Многоуровневые вложенные SELECT’ы пугают новичков.
Для анализа используйте конструкцию WITH.
❌ Как обычно пишут:
✅ А вот так понятнее:
Так запрос становится прозрачным и разбирается по шагам.
(Но, конечно, не стоит в таком стиле писать всё подряд 😉).
4) Думайте множествами 🔢
Главный совет «на миллион». Даже опытные разработчики часто пишут запросы «по синтаксису». Но SQL проще понимать, если мыслить категориями множеств:
Таблицы = множества строк.
Строки = элементы множества.
Поля = атрибуты элементов.
👉 JOIN — это сопоставление элементов двух множеств по условию.
👉 WHERE — фильтр, выбирающий нужные элементы.
👉 GROUP BY — разбиение множества на подмножества.
Пример: есть таблица «Люди». Нужно соединить их в пары.
Берём множество людей,
накладываем фильтр для первой группы,
накладываем фильтр для второй группы,
соединяем по условию (например, одинаковый возраст, рост или период рождения).
Если приучить себя мыслить множествами, SQL станет в разы понятнее.
⚡️ И главное: лучше решить 1 сложную задачу, чем 1000 лёгких.
Всегда поднимайте планку — только так прокачка идёт быстрее.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
👍 Палец вверх — продолжаем и перейдем уже на уровень выше.
👎 Палец вниз — "Вода! Вода! Кругом вода!"
👨💻 Эти советы я бы дал самому себе из будущего.
А Вы, уважаемые мидлы, синьоры, эксперты — что бы посоветовали самим себе в прошлом? 👇
#️⃣ #SQLOptimization
Стали почему-то,
Нынче в школе первый класс -
Вроде института.
Нам учитель задает
С иксами задачи,
Кандидат наук и тот -
Над задачей плачет.» — Алла Пугачева
Этим постом я начинаю (и, возможно, заканчиваю) серию «Оптимизация 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
У неё в кошельке три рубля,
Моя бабушка курит трубку,
Трубку курит бабушка моя.» — Гарик Сукачев
✨ Сегодня суббота — задачи оставим на потом.
Продолжаем рубрику «Полезные ресурсы».
🖥️ Тема: 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 градусов.
Компьютер, конечно, руками не умеет, поэтому он делает так:
– сначала кладёт фотографию на бок (транспонирование),
– потом переворачивает каждую строку, как будто листает страницы книги.
👍 Палец вверх — если такие алгоритмы заходят.
👎 Палец вниз — если это всё несерьезно.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
💬 Прошу высказываться смело, прямо, грубо и дерзко, но с уважением.
#️⃣ #SQL #Oracle #PLSQL
Крутится, вертится над головой,
Крутится, вертится, хочет упасть.
Кавалер барышню хочет украсть.» — приписывается Ф. Садовскому
📌 Разбор задачи: Поворот матрицы 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:
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
Пустые города
В которых никогда
Ты раньше не бывала.» — БИ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:
'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
Хлоп на лоб, глаза полезли, лоб становиться к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️⃣ Если пуст — всё сбалансировано ✅, иначе — нет ❌.
🔢 Пример:
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
От коих не одна кружится голова.
Я не ропщу о том, что отказали боги
Мне в сладкой участи оспаривать налоги» — Александр Пушкин
📌 Разбор задачи: Проверка на сбалансированные скобки процедурными методами
🧩 Условие:
Нужно проверить, сбалансированы ли скобки в строке.
Скобки считаются сбалансированными, если:
каждой открывающей соответствует закрывающая;
порядок правильный (( { [ ] } )).
⚙️ Алгоритм (кратко):
1️⃣ Создаём стек для хранения открывающихся скобок.
2️⃣ Идём по строке символ за символом.
3️⃣ Если встречаем '(', '{', '[' → кладём в стек.
4️⃣ Если встречаем ')', '}', ']':
• если стек пустой → ошибка (несбалансировано),
• иначе сверяем, что верх стека — соответствующая открывающая скобка.
Если совпадает → убираем её из стека.
5️⃣ После обхода строки стек должен быть пуст.
6️⃣ Если пуст — всё сбалансировано ✅, иначе — нет ❌.
🔢 Пример:
Строка: (124(){}[[] {123()} ])
Результат: BALANCED`SQLDECLARE
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-пример:
3️⃣ Через VARCHAR2
Экзотичную битовую маску в виде VARCHAR2:
bit_mask = 'YNNY'
Каждый символ обозначает состояние флага.
⚖️ Баланс
Идея простая: вместо десятков двоичных полей мы храним одно поле с маской.
Но ✋ универсальность стоит ресурсов:
придётся нагружать CPU для декодирования маски
возможно нужен справочник, какой бит за что отвечает
🔥 Если тема интересна — далее покажу имплементацию.
Наберём хотя бы 20 🤓.
👍 Палец вверх — тема зашла.
👎 Палец вниз — не зашла.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
💬 Справедливую и не очень критику туда. 👇
#SQL #Oracle #PLSQL #CodeArchitecture
Зато не тащут из семьи
А гадость пьют — из экономии
Хоть поутру — да на свои!» — Владимир Высоцкий
⚡ Поле как битовая маска
Представьте: у вас есть таблица с кучей полей-флагов.
Например:
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
👍16❤2
🎵 «Деньги бывают — такое бывает…» — Ленинград
📊 Вычисление дней просрочки по клиентам и счетам
Эта задача встречается на собеседованиях и в банковской аналитике.
Количество дней просрочки — ключевой показатель для расчёта резервов.
🔹 Условие задачи
Таблица движения средств по просроченной задолженности:
amount > 0 → долг возник, день считается первым днём просрочки
amount < 0 → долг уменьшается
Когда накопленный долг = 0 → задолженность погашена, первый и последний день включаются
💡 Важно: погашение происходит по принципу FIFO — сначала гасим старые долги, потом новые,
т.е. если клиент вовремя не погасил долг, то с этого дня начинается рассчет количества дней просрочки.
Если клиент вносил деньги для уплаты долга, то банк обязан сначала эти деньги учесть в самых старых просроченных платежах,
а потом, если остается остаток, то пробует погасить более новые долги.
Возможно, у разработчиков, не знакомых с кредитными задачами, возникнет сложность с логикой.
Во-первых, это нормально! Я только к банковским терминам привыкал полгода (План счетов, Дебет, Кредит, Просрочка, Вынос на просрочку, Погашение, Комиссии, Пенни, и прочий ужас).
Во-вторых, никто не обещал легкой жизни.
🔹 Таблица движения средств
🔹 Пример данных
💡 Ожидаемый результат на 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
📊 Вычисление дней просрочки по клиентам и счетам
Эта задача встречается на собеседованиях и в банковской аналитике.
Количество дней просрочки — ключевой показатель для расчёта резервов.
🔹 Условие задачи
Таблица движения средств по просроченной задолженности:
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