DB developers channel
803 subscribers
2 photos
46 files
102 links
💡 Канал для разработчиков баз данных: Oracle, PostgreSQL
📌 Интересные задачи, фрагменты кода, лучшие практики, архитектура, оптимизация
🔄 Присоединяйся к сообществу — развивайся вместе с нами!
#SQL #Oracle #PostgreSQL #PL/SQL #PL/pgSQL #DB
Download Telegram
DB developers channel
🎵 "Вы хотите песен? Их есть у меня." — С. Шнуров Сегодня — не песня, а задачка на размышление (из собеса с одной иностранной компанией). На первый взгляд — идентичные конструкции, но за ними скрыта важная разница. Сравните два варианта: a) SELECT * FROM…
Разбор задачи: В чём разница между SQL-запросом и вызовом PL/SQL-функции?
(К задаче из предыдущего поста)

💡 На первый взгляд оба варианта делают одно и то же — сравнивают значение из table1 с соответствующим значением из table2. Но давайте разберёмся, в чём концептуальная разница между двумя подходами.

📌 Вариант A — скалярный подзапрос
SELECT *
FROM table1 t1
WHERE (SELECT t2.column2
FROM table2 t2
WHERE t2.id = t1.id) = t1.column1;

Что происходит:
Это обычный SQL-запрос с коррелированным подзапросом.
Для каждой строки t1 выполняется подзапрос к table2.
Оптимизатор БД может применить merge join, hash join, nested loops или даже вынести подзапрос в джойн — всё зависит от статистики, индексов и плана выполнения.

Плюсы:
Оптимизируемо.
Может быть трансформировано оптимизатором.
Прозрачно для анализа выполнения.

📌 Вариант B — вызов пользовательской функции
CREATE OR REPLACE FUNCTION f1(id NUMBER) RETURN VARCHAR2 IS
x table2.column2%TYPE;
BEGIN
SELECT t.column2
INTO x
FROM table2 t
WHERE t.id = f1.id;

RETURN x;
END f1;
/
SELECT *
FROM table1 t1
WHERE f1(t1.id) = t1.column1;

Что происходит:

На каждую строку table1 вызывается функция f1.
Внутри функции выполняется SQL-запрос к table2.

Ключевое отличие:

🔐Оптимизатор не может "заглянуть" внутрь PL/SQL-функции.
Запрос к table2 не участвует в планировании — он "чёрный ящик".
Это приводит к контекстным переключениям (SQL ⇄ PL/SQL), что негативно влияет на производительность.
Невозможно применить стандартные оптимизации (например, замену на JOIN).

🔐 Ещё одно ключевое отличие — влияние уровня изоляции
На практике результат запроса A и запроса B может отличаться, даже если они запущены одновременно в разных сессиях.

Почему?

📌 По умолчанию в Oracle используется уровень изоляции READ COMMITTED.
Это означает, что:

В варианте A (чистый SQL) вся выборка выполняется как единое целое — данные читаются в рамках одного "снимка" данных на момент начала запроса.

А вот во втором варианте с функцией — каждое обращение к table2 происходит внутри отдельного запроса, в момент вызова функции.

👉 Итог:
Функция будет видеть уже изменённые данные, если они модифицированы между строками основного SQL.
Даже если изменения происходят прямо в момент выполнения запроса — это легально на READ COMMITTED.

🔄 Пример:
Вы запускаете SELECT ... WHERE f1(t1.id) = t1.column1.

В это время другая сессия меняет table2.

Результат работы f1(...) будет отличаться для разных строк, в зависимости от того, когда именно выполнялся каждый вызов функции.

🛡 Как избежать этого?
Чтобы зафиксировать момент чтения — можно использовать уровень изоляции SERIALIZABLE.

Однако:

Это не гарантирует успех — Oracle выдаст ошибку (ORA-08177: can't serialize access for this transaction), если во время выполнения будут обнаружены конфликтующие изменения.

Даже если изменён блок, в котором хранятся нужные строки (даже если сами строки не изменились), Oracle вернет ошибку.

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

#RealInterviewTasks #sql #Oracle #PostgreSQL #PL/SQL #PL/pgSQL
👎1🔥1
DB developers channel
"Пусть я переору любого соловья..." — Шнур Недавний случай из практики. Java-разработчики обращаются к БД, как умеют — через IN (...) : <select id="select" resultType="Long"> SELECT order_id FROM table_1 WHERE order_id IN <foreach item="orderId"…
🛠 Разбор кейса: IN (...), 999 параметров и производительность

Продолжение истории про Java-разработчиков и их любимый IN (...).

На первый взгляд — всё логично: список orderIds, XML-мэппер MyBatis, всё красиво раскладывается в IN (:1, ..., :N).
Но в реальности — десятки SQL-запросов с разным количеством параметров. И каждый — новый hard parse
(hard parse - это режим, когда оптимизатор определяет план запроса. soft parse, когда оптимизатор берет готовый план из Кэша).

📉 Что это означает:

Нет повторного использования плана выполнения (cursor sharing не спасает).
Под нагрузкой — рост CPU и latch contention.
Объём shared pool растёт, plan cache фрагментируется.

Почему это не ок:
Oracle не объединяет запросы с разным числом параметров.
При высоком QPS — постоянная генерация новых кусков кода (library cache misses).
Особенно тяжело в RAC — загружаются interconnect и global cache.

Что с этим делать?
Передавай список ID через TABLE(...) или временную таблицу (если элементов коллекций более 10000):

SELECT order_id
FROM table_1
WHERE order_id IN (SELECT column_value FROM TABLE(:order_id_array))


👨‍💻 Ну и это ещё не всё.
Такие решения, как выше, часто используются вынужденно — особенно у full-stack и прикладных разработчиков, где БД рассматривается как «глупое хранилище».

Но если хотите, чтобы проект не развалился под нагрузкой — не обращайтесь к БД напрямую через таблицы и вьюхи.
Даже самые простые INSERT, UPDATE, DELETE, SELECT стоит заворачивать в API на стороне БД — процедуры, функции, пакеты.

Не хотите переносить бизнес-логику в БД — не надо.
Но обращайтесь через API, а не напрямую.

📉 Пока строк в таблице — тысячи, «как-то работает».
📉 На миллионах — начинает тормозить.
🔥 А на сотнях миллионов — всё гарантированно подвиснет, и не спасут никакие индексы, если запросов с клиентской стороны будет хаос.

📦 Зачем нужен API к базе:
Код становится читаемым, стабильным и управляемым

Пакеты и процедуры компилируются заранее → экономия CPU

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

Можно свободно переносить таблицы между схемами — интерфейс не меняется

Упрощается миграция на новые модели хранения

Можно централизованно обрабатывать ошибки, транзакции, валидации

И ещё с десяток других плюсов

🧩 Итог:
Решения «в лоб» — работают, пока не стало больно.

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

#Cases
👎1
😌 Суббота, шабат, всё спокойно — разбираем лёгкую задачку.
Просто один странный запрос.

💡 Найдите, что не так с этим SQL-запросом

📦 У нас есть две таблицы:

CREATE TABLE table1 (id NUMBER NOT NULL, name VARCHAR2(100));
CREATE TABLE table2 (id NUMBER NOT NULL, address VARCHAR2(100));

🧩 И такой PL/SQL-блок:

DECLARE
a NUMBER;
b VARCHAR2(10);
BEGIN
SELECT id, address
INTO a, b
FROM table1 t1 LEFT OUTER JOIN table2 t2 ON t1.id = t2.id
WHERE t2.address LIKE '%India%'
GROUP BY t1.name, t2.address, t1.id
HAVING MIN(t1.id) = MAX(t2.id);
END;

🔍 Задача:
В этом коде есть несколько ошибок и странных решений — как логических, так и синтаксических.

Я насчитал 6 неточностей и ошибок. Может быть есть больше?

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

Что здесь не так?
Напишите в комментариях, что бы вы исправили или переписали.

👇 Разбор выложу позже.
#RealInterviewTasks #sql #Oracle #PostgreSQL #PL/SQL #PL/pgSQL
👍2👎1
🤔 Как вы сравниваете два значения в Oracle, если оба могут быть NULL?

Ситуация простая:
col1 = col2

Но оба поля могут быть NULL, и тогда сравнение возвращает FALSE.

Да, можно:
NVL(col1, 0) = NVL(col2, 0)
COALESCE(col1, -1) = COALESCE(col2, -1)
DECODE(col1, col2, 1, 0) = 1
col1 = col2 OR (col1 IS NULL AND col2 IS NULL)

Но все эти методы требуют "заглушек" или слишком громоздки.

🔍 А что используете вы в таких случаях? Есть ли у вас удобная и универсальная конструкция для сравнения "NULL-aware"?

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

#RealInterviewTasks #sql #Oracle #PL/SQL
👎1
DB developers channel
🤔 Как вы сравниваете два значения в Oracle, если оба могут быть NULL? Ситуация простая: col1 = col2 Но оба поля могут быть NULL, и тогда сравнение возвращает FALSE. Да, можно: NVL(col1, 0) = NVL(col2, 0) COALESCE(col1, -1) = COALESCE(col2, -1) DECODE(col1…
Все варианты валидны.
⚠️ (В случае если вы уверены, что значения заглушек в таблице нет
и никогда не будет).
Но предлагаю рассмотреть ещё одну системную функцию,
которую Oracle использует сам внутри.

Она заменяет NULL на специальное внутреннее значение, чтобы можно было безопасно сравнивать:

SYS_OP_MAP_NONNULL(col1) = SYS_OP_MAP_NONNULL(col2)

Таким образом NULL = NULL будет возвращать TRUE, но без искажения логики (в отличие от NVL(col, 0)).

⚠️ Функция не документирована, но активно используется в системных операциях. Работает для разных типов и не требует подстановки "заглушек".

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

#RealInterviewTasks #sql #Oracle #PL/SQL
🤩2👎1
💡 Случай из практики.

Представим, что в проекте есть таблица table_1 с большим числом полей — 50, а то и 100.
Прикладные системы присылают данные в виде XML или JSON. На первом этапе мы их парсим, а затем вызываем метод, который должен обновить или вставить запись в таблицу.

Но есть нюанс:
🔹 один приклад может менять только 3–4 поля,
🔹 другой — 20–30,
🔹 третий вообще только одно.

Как быть?
Передавать 100 параметров в метод?
Писать 10 версий под каждый приклад?
Или, может, есть способ обойтись одним универсальным механизмом, который будет обновлять только то, что реально пришло?

🧩 Как бы вы это реализовали?

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

Пишите свои подходы — обсудим в комментариях или в следующем посте 😉
#CodeArchitecture
👎1
Универсальный метод обновления: магия значений-заглушек
Привет, друзья! 👋

В прошлом посте мы говорили о сложной задаче:
в таблице с 50–100 полями каждый вызов обновляет разное количество данных. Сегодня расскажу, как с этим можно справиться с помощью одного хитрого приёма — значений-заглушек (sentinel values).

Это — простой и рабочий способ, но есть и свои подводные камни. 🚧

🔍 Как это работает?
1️⃣ Для каждого типа данных выбираем уникальное «магическое» значение, которого точно не будет в реальных данных:

Числа — 2147483647 (максимум для 32-битного int)

Даты — 01.01.0101 (ну кто такое хранит?)

Строки — символ CHR(0) (невидимый и неиспользуемый)

2️⃣ В процедуре обновления ставим эти значения по умолчанию.

3️⃣ Когда приходит запрос, обновляем только те поля, где значение отлично от заглушки. Остальные — оставляем как есть.

⚖️ Плюсы и минусы
✔️ Гибко: обновляем ровно то, что нужно
✔️ Универсально: один метод на все случаи
✔️ Без дублирования кода

Длинная сигнатура: 50–100 параметров — тяжело читать и писать
Риск коллизий: кто-то вдруг захочет использовать заглушку в данных
Неочевидно: новичку сложно понять логику по умолчанию

🚀 Есть идеи получше?
Обо всём этом — в следующем посте! 😉

🧩 А как вы бы сделали?
Пишите свои мысли и опыт в комментариях!
Будем разбираться вместе 🔥
#CodeArchitecture
1👎1
table_update_method.sql
4.9 KB
Скрипт для проверки
👎1
💡 Простой вопрос из собесов по SQL и индексам
(про план запросов, но с подвохом)

У нас есть таблица:

CREATE TABLE employees (
fio VARCHAR2(50) NOT NULL,
department_id NUMBER NOT NULL,
start_date DATE NOT NULL
);

CREATE INDEX employees_i01 ON employees (fio, department_id);

И вот такой запрос:

SELECT fio
FROM employees
WHERE fio = :f AND department_id = :dep_id;

На него отлично работает индекс employees_i01, ведь все условия входят в ключ.

Теперь добавим новый фильтр:

SELECT fio
FROM employees
WHERE fio = :f AND department_id = :dep_id AND start_date >= :d;

Как изменится производительность запроса?
1️⃣ Станет быстрее
2️⃣ Станет медленнее
3️⃣ Ничего не изменится

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

🤔 А как думаете вы?
📥 Пишите свои варианты и размышления в комментарии — обсудим!
#RealInterviewTasks
👎1
DB developers channel
💡 Простой вопрос из собесов по SQL и индексам (про план запросов, но с подвохом) У нас есть таблица: CREATE TABLE employees ( fio VARCHAR2(50) NOT NULL, department_id NUMBER NOT NULL, start_date DATE NOT NULL ); CREATE…
✍️ Ответ на вопрос по индексам:

Запрос:
SELECT fio
FROM employees
WHERE fio = :f AND department_id = :dep_id;
🔍 Отлично использует индекс employees_i01 (fio, department_id) — оба условия входят в его ключ, возможен index range scan с минимальным I/O.

Теперь добавляем:

AND start_date >= :d

💥 И тут может случиться подвох.

🧠 Что изменится? Индекс остался тем же, но появился дополнительный фильтр, не входящий в индекс. Oracle больше не сможет полностью удовлетворить запрос только по индексу — ему нужно будет идти в таблицу (table access by rowid), чтобы проверить start_date.

📉 Это добавляет дополнительный шаг (filter) и может замедлить выполнение. Особенно на больших таблицах с большим количество полей.

📌 Ответ: 2️⃣ Станет медленнее.

💎 Поддержка канала⁉️
#RealInterviewTasks
🔥3👎1
DB developers channel
💡 Случай из практики. Представим, что в проекте есть таблица table_1 с большим числом полей — 50, а то и 100. Прикладные системы присылают данные в виде XML или JSON. На первом этапе мы их парсим, а затем вызываем метод, который должен обновить или вставить…
Анализируйте, пользуйтесь, ёшкин кот!!!👋

В прошлом посте мы разбирали, как обновлять разные наборы полей в одной процедуре с помощью значений-заглушек.
Это рабочий, но не идеальный метод.
Но прежний метод колхоз, по сравнению с текущим методом.
Универсального механизм на базе ключ-значение!

⚠️ Метод сложный, но он Вас обогатит во всех смыслах.
Много чему научитесь!

Как это работает?

Вместо того чтобы передавать 50-100 параметров, мы используем специальную структуру данных, которая может хранить разные типы значений.

Тип-вариант (t_variant): Мы создаем объект, который умеет хранить числа, строки, даты и другие типы данных. Внутри него — разные поля, но за раз используется только одно.

Ассоциативный массив: Мы определяем константы (например, number_field= 1, varchar2_field = 5) для каждого поля в таблице. Затем создаем коллекцию или ассоциативный массив, где ключ — это номер поля, а значение — наш t_variant.

Универсальный метод (set_attribute): В процедуре мы просто добавляем в этот массив только те поля, которые нужно обновить.

Применение изменений (apply_changes): Отдельная процедура получает этот массив и обновляет только те столбцы, которые в нём есть.

🚀 Плюсы этого подхода:

Чистый код: Сигнатура процедуры обновления становится очень короткой — она принимает всего одну структуру!
Гибкость: Можно легко добавлять новые поля, не меняя сигнатуру процедуры.
Безопасно: Мы не используем "магические" значения, которые могут совпасть с реальными данными.

🚧 Минусы, конечно, тоже есть:

Сложность: Создание и поддержка кастомных типов (t_variant, коллекций) требует больше усилий.
Производительность: Работа с объектами и коллекциями может быть медленнее, чем простой UPDATE с CASE операторами.
Неочевидно: Для новичка это решение может показаться избыточным.

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

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

Все слова благодарности, лайки, гневные комменты и всё что на душе накипело... 👇
#CodeArchitecture
2👍1👎1
🎵«Помню, помню, помню я,
Как меня мать любила.
И не раз, и не два
Она мне говорила:
Не ходи на тот конец,
Не водись с ворами,
Тебя на каторгу сошлют,
Бряцать кандалами...»
— Народная

⚠️ Никогда не попадайте на каторгу, как бы много Вам не платили - всё равно будет ощущение, что Вас обокрали.

Задача из собеса как раз на такую каторгу.

💡 Что произойдёт после выполнения этого скрипта?
WHENEVER SQLERROR CONTINUE
CREATE TABLE test_tbl (x INT);
INSERT INTO test_tbl (x) VALUES (1);

BEGIN
INSERT INTO test_tbl (x) VALUES (2);
RAISE VALUE_ERROR;
END;
/
SELECT * FROM test_tbl;

Внутри BEGIN ... END — вставка и ошибка. Но стоит WHENEVER SQLERROR CONTINUE.

🤔 Вопрос:
Что отработает, а что нет? Что покажет SELECT?
Будут ли строки (1) и (2), или только (1)?

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

✍️ Пишите в комментариях, как думаете.
(Не забудьте обосновать — там всё не так просто 😉)
#RealInterviewTasks #sql #Oracle #PLSQL
👎1
DB developers channel
🎵«Помню, помню, помню я, Как меня мать любила. И не раз, и не два Она мне говорила: Не ходи на тот конец, Не водись с ворами, Тебя на каторгу сошлют, Бряцать кандалами...» — Народная ⚠️ Никогда не попадайте на каторгу, как бы много Вам не платили - всё равно…
💡 Разбор задачи с собеса

WHENEVER SQLERROR CONTINUE
CREATE TABLE test_tbl (x INT);
INSERT INTO test_tbl (x) VALUES (1);

BEGIN
INSERT INTO test_tbl (x) VALUES (2);
RAISE VALUE_ERROR;
END;
/
SELECT * FROM test_tbl;

Что покажет SELECT?

📌 Ответ: будет только (1).

🛠 Почему:
INSERT (1) — отдельный SQL-оператор, он останется в транзакции.

BEGIN...END; — это одна операция. Необработанное исключение (VALUE_ERROR) откатывает всё, что внутри блока, до implicit savepoint. Поэтому INSERT (2) отменится.

WHENEVER SQLERROR CONTINUE — это настройка SQL*Plus, она лишь говорит не останавливать скрипт, но не отменяет откаты внутри Oracle.

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

#RealInterviewTasks #sql #Oracle #PL/SQL
👎1
🎸 «И всё повторяется, как сон» — Король и Шут

🧠 Классическая задача с собеседований - почти в каждом собесе такая задача встречается.

Дана таблица:
sql
CREATE TABLE transactions (
id NUMBER PRIMARY KEY NOT NULL,
created DATE NOT NULL,
amount NUMBER NOT NULL,
currency VARCHAR2(3) NOT NULL,
description VARCHAR2(100 CHAR)
);

📌 Нужно:
Написать SQL-запрос, который вернёт все лишние дубликаты, где дубликатом считается комбинация
created + currency + amount.

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

🤔 Как бы вы решили?
Пишите свои варианты в комментариях — в следующем посте разберём.
#RealInterviewTasks #sql #Oracle #PostgreSQL #PL/SQL #PL/pgSQL
DB developers channel
🎸 «И всё повторяется, как сон» — Король и Шут 🧠 Классическая задача с собеседований - почти в каждом собесе такая задача встречается. Дана таблица: sql CREATE TABLE transactions ( id NUMBER PRIMARY KEY NOT NULL, created DATE NOT NULL, amount NUMBER…
💡 Разбор задачи о дублях
Чтобы вытащить только лишние дубликаты (не оставляя первый уникальный), удобно использовать аналитическую функцию ROW_NUMBER():
SELECT id,
created,
amount,
currency,
description
FROM (
SELECT t.*,
ROW_NUMBER() OVER (
PARTITION BY created, currency, amount
ORDER BY id
) AS rn
FROM transactions t
) sub
WHERE rn > 1;

📌 Как это работает:
PARTITION BY created, currency, amount
— разбиваем данные на группы с одинаковыми датой, валютой и суммой.

ORDER BY id
— определяем, какой из дублей считать "первым".

ROW_NUMBER()
— нумерует строки в каждой группе с 1.

WHERE rn > 1
— берём только строки с номером больше 1 (т.е. "лишние").

Плюс этого подхода — он легко адаптируется под любые критерии поиска дубликатов и работает и в Oracle, и в PostgreSQL.

А вы бы решали через GROUP BY ... HAVING COUNT(*) > 1 или тоже через аналитику?

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

#RealInterviewTasks #sql #Oracle #PostgreSQL #PLSQL #PLpgSQL
👎1
🔥 «Как увидят нас — так сразу ахнут, и кое-где жаренным запахнет, кое-что за пазухой мы держим...» — из «Бременских музыкантов»

🧠 Интересная задача с собеседования 2022 году

Дано:
CREATE TABLE table1 (
numeric_value NUMBER NOT NULL,
created DATE NOT NULL
);

CREATE OR REPLACE TYPE numeric_value_rec AS OBJECT (
numeric_value NUMBER
)
/

CREATE OR REPLACE TYPE numeric_value_tbl IS TABLE OF numeric_value_rec
/

Код:
DECLARE
portionSize CONSTANT INT := 100;

CURSOR readData IS
SELECT numeric_value_rec(numeric_value) AS x
FROM table1
ORDER BY created
FOR UPDATE;

portionArray numeric_value_tbl;
BEGIN
OPEN readData;
FETCH readData BULK COLLECT INTO portionArray LIMIT portionSize;
CLOSE readData;
END;
/

📌 Вопрос:
Почему этот код не выполняет свою задачу — читать данные по порциям, сохраняя потребление ресурсов постоянным, независимо от размера таблицы?

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

#RealInterviewTasks #Oracle #PLSQL #SQL
👎1
DB developers channel
🔥 «Как увидят нас — так сразу ахнут, и кое-где жаренным запахнет, кое-что за пазухой мы держим...» — из «Бременских музыкантов» 🧠 Интересная задача с собеседования 2022 году Дано: CREATE TABLE table1 ( numeric_value NUMBER NOT NULL, created DATE NOT…
💡 Разбор задачи с собеседования (2022)

📌 Цель — читать данные по порциям, сохраняя постоянное потребление ресурсов.

В чём ошибки исходного решения:

Блокируется вся таблица
Из-за FOR UPDATE без ограничений на выборку блокировка накладывается на все строки, а не только на текущую порцию.

Нет порционного перебора
Если требуется пройти таблицу целиком, надо читать её в цикле по порциям и блокировать только нужную порцию перед обработкой, а затем делать COMMIT, чтобы снять блокировки.

Нет индекса по created
Это приведёт к сортировке всей таблицы при каждом запуске, что при больших объёмах сильно замедлит выполнение.

Нет блока EXCEPTION
При ошибке не будет гарантии, что курсор не будет закрыт, а коллекция — очищена, что создаёт утечки ресурсов.

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

#RealInterviewTasks #Oracle #PLSQL #SQL
👎1
Готовлюсь к тесту по алгоритмам. Для меня это серьезный вызов. В DB такие штуки редко применяются. Интересно ли Вам, такие задачи и их разбор средствами процедурного языка?
Anonymous Poll
71%
Да
12%
Нет
18%
Не морочьте мозги, мне это никогда не пригодится
👎1
🧩 Задача для PL/SQL-разработчиков от Экспо-Банка из 2020 года
DECLARE
TYPE rRec IS RECORD (
vStr VARCHAR2(100),
vDate DATE
);

TYPE tRec IS TABLE OF rRec INDEX BY INTEGER;
tableRec tRec;

Задание:
Написать процедуру сортировки tableRec любым методом, при котором:

vStr сортируется по возрастанию
vDate сортируется по убыванию

Пример входных данных:
'11'  31/10/17
'11' 15/10/17
'22' 01/01/20
'33' 10/07/19
'33' 09/07/19

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

💬 Напишите как бы Вы решили в комментариях — завтра выложу разбор.
#RealInterviewTasks #sql #Oracle #PLSQL
👎1
💡 Разбор задачи от Экспо-Банка

Уловка задачи в том, что ассоциативный массив нельзя использовать в SELECT.
Поэтому сортировку приходится делать вручную методами процедурного языка.

📌 Для решения можно выбрать любой алгоритм сортировки.
Я взял быструю сортировку, но по условию подойдёт и сортировка "пузырьком".

DECLARE
TYPE rRec IS RECORD
(
vStr VARCHAR2 (100),
vDate DATE
);

TYPE tRec IS TABLE OF rRec
INDEX BY PLS_INTEGER;

tableRec tRec;

PROCEDURE quick_sort (left IN INTEGER, right IN INTEGER, sorted_table IN OUT NOCOPY tRec) IS
i INTEGER := left;
j INTEGER := right;
mediana rRec;
tmp rRec;

FUNCTION greater (a IN rRec, b IN rRec)
RETURN BOOLEAN IS
BEGIN
--vStr сортируется по возрастанию, а vDate по убыванию.
RETURN (a.vStr > b.vStr) OR (a.vStr = b.vStr AND a.vDate < b.vDate);
END greater;
BEGIN
mediana := sorted_table ((left + right) / 2);

WHILE i <= j LOOP
WHILE greater (a => mediana, b => sorted_table (i)) LOOP
i := i + 1;
END LOOP;

WHILE greater (a => sorted_table (j), b => mediana) LOOP
j := j - 1;
END LOOP;

IF i <= j THEN
tmp := sorted_table (i);
sorted_table (i) := sorted_table (j);
sorted_table (j) := tmp;
i := i + 1;
j := j - 1;
END IF;
END LOOP;

IF left < j THEN
quick_sort (left => left, right => j, sorted_table => sorted_table);
END IF;

IF i < right THEN
quick_sort (left => i, right => right, sorted_table => sorted_table);
END IF;
END quick_sort;
BEGIN
tableRec (1).vStr := 'Petrov';
tableRec (1).vDate := TO_DATE ('10.08.2025', 'dd.mm.yyyy');
tableRec (2).vStr := 'Petrov';
tableRec (2).vDate := TO_DATE ('11.08.2025', 'dd.mm.yyyy');
tableRec (3).vStr := 'Ivanov';
tableRec (3).vDate := TO_DATE ('01.08.2025', 'dd.mm.yyyy');
tableRec (4).vStr := 'Ivanov';
tableRec (4).vDate := TO_DATE ('02.08.2025', 'dd.mm.yyyy');

quick_sort (left => 1, right => tableRec.COUNT, sorted_table => tableRec);

DECLARE
index# INTEGER;
BEGIN
index# := tableRec.FIRST;

WHILE index# IS NOT NULL LOOP
DBMS_OUTPUT.put_line (tableRec (index#).vStr || ' ' || TO_CHAR (tableRec (index#).vDate, 'dd.mm.yyyy'));
index# := tableRec.NEXT (index#);
END LOOP;
END;

tableRec.delete;
EXCEPTION
WHEN OTHERS THEN
IF tableRec IS NOT NULL THEN
tableRec.delete;
END IF;
END;

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

А было бы вам интересно увидеть реализацию сортировки через бинарное дерево в PL/SQL?
Напишите в комментариях, обсудим.
#RealInterviewTasks #sql #Oracle #PLSQL
👎1