DB developers channel
803 subscribers
2 photos
46 files
102 links
💡 Канал для разработчиков баз данных: Oracle, PostgreSQL
📌 Интересные задачи, фрагменты кода, лучшие практики, архитектура, оптимизация
🔄 Присоединяйся к сообществу — развивайся вместе с нами!
#SQL #Oracle #PostgreSQL #PL/SQL #PL/pgSQL #DB
Download Telegram
🧩 Задача для 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
🎵 «Нас четверо, пока ещё мы вместе!
Но дело есть
И это дело чести!
Девиз наш — "Все за одного"
И в этом наш успех!»
— Три мушкетёра ⚔️

💼 Задача из собеса в СКБ-банк в далёком 2017 году.

📝 Задание 1
Сгенерировать произвольным образом содержимое таблицы C, на основе данных таблицы A и B.

📂 Таблица A:
EMPFIO — ФИО сотрудника 👨‍💼

📂 Таблица B:
OWNM — Наименование подразделения 🏢

📂 Таблица C:
OWNM — Наименование подразделения 🏢
EMPFIO — ФИО сотрудника 👨‍💼

📝 Задание 2
Объяснить, в каком порядке будут выбираться данные из таблиц, какие таблицы будут задействованы в первую очередь, а какие потом.
Таблицы и данные использовать из предыдущего задания.

select * from A, B, C
where a.empfio = c.empfio
and b.ownm = c.ownm;

select * from B, A, C
where a.empfio = c.empfio
and b.ownm = c.ownm;

✏️ Написать запросы, выбирающие данные из таблиц в обратном порядке их использования в указанных двух запросах.
Дать комментарий по предоставленному решению.

📌 То есть:
C -> B -> A
C -> A -> B

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

#RealInterviewTasks #sql #Oracle #PostgreSQL #PLSQL #PLpgSQL
👎1
💼 Разбор задачи из СКБ-банка — генерация данных и порядок соединений
Бог знает, что я написал тогда в 2017 году, но сейчас мой ответ был бы таким.
CREATE TABLE a (empfio VARCHAR2 (50));
CREATE TABLE b (ownm VARCHAR2 (50));
CREATE TABLE c (ownm VARCHAR2 (50), empfio VARCHAR2 (50));

DECLARE
PROCEDURE generate_data (emp_count IN INTEGER, dept_count IN INTEGER, links_count IN INTEGER) IS
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE c';
EXECUTE IMMEDIATE 'TRUNCATE TABLE a';
EXECUTE IMMEDIATE 'TRUNCATE TABLE b';

INSERT INTO a (empfio)
SELECT DBMS_RANDOM.string ('U', 8) AS empfio
FROM DUAL
CONNECT BY LEVEL <= generate_data.emp_count;

INSERT INTO b (ownm)
SELECT DBMS_RANDOM.string ('U', 6) AS ownm
FROM DUAL
CONNECT BY LEVEL <= generate_data.dept_count;

INSERT INTO c (ownm, empfio)
SELECT a.ownm, a.empfio
FROM (SELECT b.ownm, a.empfio
FROM a CROSS JOIN B
ORDER BY DBMS_RANDOM.value) a
WHERE ROWNUM <= generate_data.links_count;
END generate_data;
BEGIN
generate_data (emp_count => 5, dept_count => 3, links_count => 10);

COMMIT;
END;

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

В текущих запросах нет фильтров и в таблицах нет индексов, значит он будет учитывать кардинальность таблиц и соединять их по мере роста объёма данных.

Порядок соединения можно явно задать хинтами ORDERED и LEADING.
select /*+ LEADING (C B A) */ *
from A, B, C
where a.empfio = c.empfio
and b.ownm = c.ownm;

select /*+ ORDERED */ *
from C, A, B
where a.empfio = c.empfio
and b.ownm = c.ownm;

⚡️ Хинты позволяют контролировать порядок соединений и влияют на план выполнения.

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

#RealInterviewTasks #sql #Oracle #PLSQL
🔥2👎1
🎵«И вновь продолжается бой,
И сердцу тревожно в груди.»
— Н. Добронравов

🌳 Работа с иерархическими данными в Oracle

Есть таблица с данными в виде дерева:
CREATE TABLE t (
id NUMBER, -- идентификатор узла
pid NUMBER, -- идентификатор родительского узла
nam VARCHAR2(255) -- наименование
);

Пример данных
ID  PID  NAM
1 Корень
2 1 Узел2
3 1 Узел3
4 2 Узел4
5 4 Узел5
6 5 Узел6
7 4 Узел7

Требование:
Написать запрос для получения дерева от корневого узла.
Узел 5 и все его потомки должны быть исключены из результата.
Для каждого узла нужно вывести имя его родителя.
Данные отсортировать в порядке возрастания ID с учётом иерархии.

Ожидаемый результат
ID  PID  NAM    PARENT_NAM
1 Корень
2 1 Узел2 Корень
4 2 Узел4 Узел2
7 4 Узел7 Узел4
3 1 Узел3 Корень

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

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

Исходные данные:
CREATE TABLE t (
id NUMBER, -- идентификатор узла
pid NUMBER, -- идентификатор родительского узла
nam VARCHAR2(255) -- наименование
);

📋 Пример содержимого:
ID PID NAM
1 Корень
2 1 Узел2
3 1 Узел3
4 2 Узел4
5 4 Узел5
6 5 Узел6
7 4 Узел7

🚫 Дерево до фильтрации:
scss
Корень (1)
├── Узел2 (2)
│ └── Узел4 (4)
│ ├── Узел5 (5)
│ │ └── Узел6 (6)
│ └── Узел7 (7)
└── Узел3 (3)

Дерево после фильтрации (узел 5 и потомки удалены):
scss
Корень (1)
├── Узел2 (2)
│ └── Узел4 (4)
│ └── Узел7 (7)
└── Узел3 (3)

📌 Решение 1 (CONNECT BY):
SELECT 
t.id,
t.pid,
t.nam,
PRIOR t.nam AS parent_nam
FROM t
START WITH t.pid IS NULL
CONNECT BY PRIOR t.id = t.pid AND t.id != 5
ORDER SIBLINGS BY t.id;

📌 Решение 2 (рекурсивный WITH, быстрее):
WITH t1 (id, pid, nam, parent_name) AS (
SELECT t.id, t.pid, t.nam, NULL AS parent_name
FROM t
WHERE t.pid IS NULL
UNION ALL
SELECT t2.id, t2.pid, t2.nam, t1.nam AS parent_name
FROM t t2
INNER JOIN t1 ON t2.pid = t1.id
WHERE t2.id != 5
)
SEARCH DEPTH FIRST BY id SET order1
SELECT t1.id, t1.pid, t1.nam, t1.parent_name
FROM t1
ORDER BY order1;

📊 Оба запроса дают одинаковый результат, но второй обычно эффективнее при больших объёмах данных.

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

#RealInterviewTasks #sql #oracle #plsql
👎1🤝1
Лекция_для_начинающих_План_запроса_в_PostgreSQL.sql
103.4 KB
📖 Как я оказался одним из авторов курса по SQL от Яндекса

Вначале было очень интересно, и меня буквально распирало от важности. Но… платили мало, работать приходилось много. Я стал сочковать.

В итоге — выгнали. Но уроков 10 я всё же написал 🙂
Курс, кстати, не для DB, а скорее ознакомительный, для прикладных программистов.
И, думаю, свои цели он достигает. В этом плане могу его рекомендовать.
А в целом, без ограничения общности, уверен, что и другие аналогичные курсы не хуже.

💡 Я выкладываю свой ответ на тестовое задание - "Напишите и объясните человеку с улицы, что такое план запроса в PostgreSQL."
Текст — сырой, без редактуры, но я старался и собеседование прошел😁

💎 Поддержка канала⁉️
#sql #PostgreSQL #PLpgSQL
🔥5🤔2👎1
🎵"Нас атомной бомбой испугаешь едва ли"
— группа Ленинград
🎯 Тестовое задание на соискателя роли SQL Developer в 2022 году

Задача:
Дана таблица курсов валют:
create table rates(
curr_id number, -- идентификатор валюты
date_rate date, -- дата, с которой действует курс
rate number -- значение курса
);

Особенности:
Курс не устанавливается на каждую календарную дату.
Установленный курс действует до следующей его смены.
Уникальный ключ: curr_id + date_rate.

Пример данных:
URR_ID DATE_RATE RATE
1 01.01.2010 30
2 01.01.2010 40
1 02.01.2010 32
1 05.01.2010 33
2 10.01.2010 41
2 15.01.2010 42

Требуется:
Написать SQL-запрос, который вернёт действующий курс указанной валюты на любую заданную дату.

Пример результата:

Для валюты 1 на 03.01.2010 → курс 32
Для валюты 2 на 10.01.2010 → курс 41

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

💬 Напишите как бы Вы решили в комментариях👇
#RealInterviewTasks #sql #Oracle #PostgreSQL #PLSQL #PLpgSQL
👍2👎2
DB developers channel
🎵"Нас атомной бомбой испугаешь едва ли" — группа Ленинград 🎯 Тестовое задание на соискателя роли SQL Developer в 2022 году Задача: Дана таблица курсов валют: create table rates( curr_id number, -- идентификатор валюты date_rate date, -- дата…
🔎 Разбираем задачу по курсам валют

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

1️⃣ Нетранкованные даты
В таблицах даты валюты могут храниться нетранкованные. Иными словами, на один и тот же день может быть несколько записей с разными временами:
например, курс валюты может быть установлен на дату 16.08.2025 00:00:00 и на 16.08.2025 12:00:00.
(Надеюсь, что такое никогда не произойдет — это признак галопирующей инфляции 💸).

2️⃣ Уникальный индекс
Указан уникальный индекс на (curr_id, date_rate). Если же требуется получать данные быстрее, стоит сделать уникальный индекс на все три поля.

📌 Логика запроса
Чтобы получить актуальный курс на заданную дату, нужно выбрать запись с максимальной датой, которая не превышает целевую.
Индекс на (curr_id, date_rate) обеспечивает быстрый поиск по валюте и дате.

1️⃣ Первый вариант (индекс по (curr_id, date_rate))
SELECT r.rate
FROM rates r
WHERE r.curr_id = 9
AND r.date_rate <= SYSDATE - INTERVAL '100' DAY(3)
ORDER BY r.date_rate DESC
FETCH FIRST 1 ROW ONLY;

💡 Как работает:
Oracle использует индекс на (curr_id, date_rate) для поиска всех записей валюты 9 до целевой даты.
Сортировка по date_rate DESC находит последнюю актуальную дату.
После выбора строки Oracle обращается к таблице, чтобы получить значение rate.

⚠️ Эффект:
BUFFER_GETS и DISK_READS выше, так как приходится читать блоки таблицы.
Производительность нормальная, но есть возможность улучшения.

2️⃣ Второй вариант (covering index (curr_id, date_rate, rate))
CREATE UNIQUE INDEX rates_u01
ON rates (curr_id, date_rate, rate);

DECLARE
currId rates.curr_id%TYPE;
dateIate rates.date_rate%TYPE := SYSDATE - INTERVAL '100' DAY;
rate rates.rate%TYPE := 9;

CURSOR getRate (curr_id IN rates.curr_id%TYPE, date_rate IN rates.date_rate%TYPE) IS
SELECT /*+ FIRST_ROWS*/
r.rate
FROM rates r
WHERE r.curr_id = getRate.curr_id AND r.date_rate <= getRate.date_rate
ORDER BY r.date_rate DESC;
BEGIN
OPEN getRate (curr_id => currId, date_rate => dateIate);

FETCH getRate INTO rate;

CLOSE getRate;

DBMS_OUTPUT.put_line ('rate = ' || TO_CHAR (rate));
EXCEPTION
WHEN OTHERS THEN
IF getRate%ISOPEN THEN
CLOSE getRate;
END IF;

RAISE;
END;
/

💡 Как работает:
Индекс теперь содержит все нужные поля, включая rate.
Oracle получает значение курса прямо из индекса, не обращаясь к таблице.
Поиск последней даты по валюте работает так же — сортировка по date_rate DESC.

Эффект:
Снижается число блоков, которые нужно читать.
BUFFER_GETS и DISK_READS сокращаются более чем в 2 раза.
Запрос выполняется быстрее и эффективнее.
#RealInterviewTasks #sql #Oracle #PLSQL
👎1😁1
Продолжим и сравним планы запросов двух вариантов более подробно ⚖️.

Вариант 1️⃣
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 35 | 1 | 00:00:01 |
| * 1 | VIEW | | 1 | 35 | 1 | 00:00:01 |
| * 2 | WINDOW NOSORT STOPKEY | | 9367 | 327845 | 1 | 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID | RATES | 9367 | 327845 | 1 | 00:00:01 |
| * 4 | INDEX RANGE SCAN DESCENDING | RATES_U01 | 2 | | 1 | 00:00:01 |
----------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
------------------------------------------
* 1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=1)
* 2 - filter(ROW_NUMBER() OVER ( ORDER BY INTERNAL_FUNCTION("R"."DATE_RATE") DESC )<=1)
* 4 - access("R"."CURR_ID"=9 AND "R"."DATE_RATE"<=SYSDATE@!-INTERVAL'+100 00:00:00' DAY(3) TO SECOND(0))

⚠️ Оптимизатор вынужден применять аналитическую функцию и делать дополнительную фильтрацию.

Вариант 2️⃣
----------------------------------------------------------
| Id | Operation | Name | E-Rows |
----------------------------------------------------------
| 0 | SELECT STATEMENT | | |
|* 1 | INDEX RANGE SCAN DESCENDING| RATES_U01 | 9367 |
----------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("R"."CURR_ID"=:B2 AND "R"."DATE_RATE"<=:B1")

Комментарий:
Во втором варианте Oracle просто спускается по индексу и берёт последнее значение — всё.

📎 Тестовый скрипт для проверки прикрепил.

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

Смотрите, анализируйте и пишите свои выводы в комментариях 👇
#RealInterviewTasks
👎1🤯1
🎵«И нет нам покоя,
Гори, но живи!
Погоня, погоня,
Погоня, погоня
в горячей крови.»
🎬 к/ф Неуловимые мстители

Недавно проходил собеседование в международную компанию.
От интервью вынес любопытный инсайт: обработка EXCEPTION "тормозит" работу.

Мысль показалась интересной, и я решил проверить её на практике
📊 Эксперимент

1️⃣ Создаём таблицу с числами от 1 до 1 000 000, но только с нечётными id:
CREATE TABLE test (id NUMBER);

INSERT INTO test (id)
SELECT LEVEL
FROM dual
WHERE MOD(LEVEL, 2) = 1
CONNECT BY LEVEL <= 1000000;

COMMIT;

CREATE UNIQUE INDEX test_u01 ON test (id);

2️⃣ В цикле от 1 до 1 000 000 считаем количество чётных и нечётных.

В первом варианте используем EXCEPTION NO_DATA_FOUND.

Во втором варианте вместо исключения применяем MAX, чтобы всегда получать результат.

Результат

Вариант с EXCEPTION:
time: +000000000 00:00:10.140000000
Вариант с MAX:
time: +000000000 00:00:09.921000000

📉 Разница: примерно 0.5 секунды на миллион строк.

🤔 Много это или мало — решать вам.
Но факт остаётся фактом: EXCEPTION действительно тормозит 🚀

Для проверки смотрим скрипт и анализируем.

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

💬 Пишите в комментах, всё что на душе наболело 👇
#Cases
🤮2🔥1🤔1
script_test_exception.sql
9.9 KB
Скрипт для проверки и анализа
👎1
📌 Самый простой вопрос из собесов на SQL. Что думаете, какой вариант даст TRUE?
Anonymous Poll
23%
NULL = NULL
68%
NULL != NULL
9%
NULL > NULL
👎2👍1
👎1😁1
🚀 Хотите прокачать SQL, но базы под рукой нет?

У Славы Рожнева — автора курса SQL в Яндексе — есть два классных ресурса:

🔹 sqltest.online — задачи и тесты для практики
📢 Канал: t.me/sqltestonline

🔹 sqlize.online — онлайн-песочница для SQL-запросов прямо в браузере
📢 Канал: t.me/sqlize

Отличный вариант, чтобы учиться и держать форму в любом месте и в любое время.
#FriendlyResources
🔥2👎1🤔1
DB developers channel
📌 Самый простой вопрос из собесов на SQL. Что думаете, какой вариант даст TRUE?
😅 Всё правильно — без подвоха на собесах редко бывает.
👉 Главное помнить: NULL — это "неизвестное значение", и любые сравнения (=, !=, <, >) с ним дают FALSE (результат сравнения неопределен).

Рабочие варианты проверки:

1) NULL IS NULL → TRUE

2) Использовать функции NVL, COALESCE, CASE, DECODE, SYS_OP_MAP_NONNULL

Так что троллинг удался, но польза от вопроса всё равно осталась 🚀

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

#RealInterviewTasks #sql #Oracle #PLSQL
💯4👎1
🎵«Тем, кто рос в тепле и неге,
Мы, конечно, не коллеги.
Мы крапивные побеги,
Парни во.
Мы всегда и всюду рады
Вызывать у всех досаду,
Для чего нам это надо,
А не для чего.»
— к/ф «Тайна Снежной королевы» 🎬


🔥 А теперь — задачи для тех, кто любит алгоритмы и процедурные языки:

1️⃣ Максимальная разница — найти максимальную разницу в массиве, где больший элемент идёт после меньшего.
2️⃣ Проверка палиндрома — определить, является ли строка палиндромом (игнорируя регистр и лишние символы).
3️⃣ Максимальный квадрат — найти самый большой квадрат из единиц в двоичной матрице.
4️⃣ Максимальная глубина дерева — вычислить максимальную глубину бинарного дерева.
5️⃣ Дом-вор — дома стоят в ряд, нельзя грабить два подряд. Найдите максимальную сумму.

👉 Пробуем решать на PL/SQL или PL/pgSQL😉

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

#sql #Oracle #PostgreSQL #PLSQL #PLpgSQL
👎1👏1
DB developers channel
🎵«Тем, кто рос в тепле и неге, Мы, конечно, не коллеги. Мы крапивные побеги, Парни во. Мы всегда и всюду рады Вызывать у всех досаду, Для чего нам это надо, А не для чего.» — к/ф «Тайна Снежной королевы» 🎬 🔥 А теперь — задачи для тех, кто любит алгоритмы…
🎯 Задача 1. Максимальная разница
Дан массив чисел. Нужно найти максимальную разницу между двумя элементами, где больший элемент идёт после меньшего.

Пример:
[1, 3, 2, 5, 8, 7, 9]  
Максимальная разница = 9 - 1 = 8

😅 Сначала я решил задачу «в лоб» — двойным циклом.

🤖 Но ChatGPT подсказал вариант с одним проходом по массиву, и это действительно оптимально.

💻 Решение на PL/SQL:
DECLARE
TYPE set_tab IS TABLE OF INTEGER;
"set" set_tab := set_tab (1, 3, 2, 5, 8, 7, 9);
minPref INTEGER;
maxResidual INTEGER := NULL;
cand INTEGER;
BEGIN
minPref := "set"(1);

FOR i IN 2 .. "set".COUNT LOOP
cand := "set"(i) - minPref;

IF cand > 0 THEN
IF maxResidual IS NULL THEN
maxResidual := cand;
ELSE
maxResidual := GREATEST(maxResidual, cand);
END IF;
END IF;

minPref := LEAST(minPref, "set"(i));
END LOOP;

IF maxResidual IS NOT NULL THEN
DBMS_OUTPUT.put_line('maxResidual = ' || maxResidual);
ELSE
DBMS_OUTPUT.put_line('No residual pairs');
END IF;
END;

Алгоритм (O(n)):

📌 minPref — минимальный элемент слева (купить дёшево).
📌 maxResidual — лучшая разница (продать дорого).

🔹 Шаги:
Инициализируем: minPref = "set"[1], maxResidual = NULL.
Для каждого следующего элемента i:
Считаем: cand = i - minPref.
Если cand > 0 и больше текущего maxResidual, обновляем результат.
Обновляем minPref = min(minPref, i).
Если так и не нашли пары → выводим « пар нет».

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

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

Дамы и Господа, выбор за вами 😉
#sql #Oracle #PLSQL
👍51👎1
DB developers channel
🎵«Тем, кто рос в тепле и неге, Мы, конечно, не коллеги. Мы крапивные побеги, Парни во. Мы всегда и всюду рады Вызывать у всех досаду, Для чего нам это надо, А не для чего.» — к/ф «Тайна Снежной королевы» 🎬 🔥 А теперь — задачи для тех, кто любит алгоритмы…
🎯 Задача 2. Проверка палиндрома. Разбор по шагам

Цель: Проверка палиндрома: Проверьте, является ли заданная строка палиндромом, игнорируя регистр и небуквенно-цифровые символы..

📌 На входе:
str   := 'K 123 a?* Z(A)  k ';

"KaZAk" <=> "kAZaK"

🧠 Как работает алгоритм

1️⃣ Символ считается небуквой, если SUBSTR(lowerStr,i,1) = SUBSTR(upperStr,i,1)
(для букв lower != upper, для пробелов/знаков/цифр — одинаково; поэтому они пропускаются).
2️⃣ Сравнение идёт по буквам: SUBSTR(lowerStr, leftIndex#, 1) <=> SUBSTR(lowerStr, rightIndex#, 1).
3️⃣Если буквы совпали — сдвигаем оба индекса внутрь; если нет — isPolindrom := 'N' и выходим.
DECLARE
str VARCHAR2 (4000) := 'K 123 a?* Z(A) k ';
lowerStr VARCHAR2 (4000) := LOWER (str);
upperStr VARCHAR2 (4000) := UPPER (str);
isPolindrom VARCHAR2 (1) := 'Y';
leftIndex# INTEGER := 1;
rightIndex# INTEGER := LENGTH (str);
BEGIN
WHILE leftIndex# < rightIndex# LOOP
--DBMS_OUTPUT.put_line ('---');
--DBMS_OUTPUT.put_line ('substr(lowerStr,leftIndex#,1) = ' || substr(lowerStr,leftIndex#,1));
--DBMS_OUTPUT.put_line ('substr(lowerStr,rightIndex#,1) = ' || substr(lowerStr,rightIndex#,1));

IF SUBSTR (lowerStr, leftIndex#, 1) = SUBSTR (upperStr, leftIndex#, 1) THEN -- не буква
leftIndex# := leftIndex# + 1;
CONTINUE;
END IF;

IF SUBSTR (lowerStr, rightIndex#, 1) = SUBSTR (upperStr, rightIndex#, 1) THEN -- не буква
rightIndex# := rightIndex# - 1;
CONTINUE;
END IF;

IF SUBSTR (lowerStr, leftIndex#, 1) = SUBSTR (lowerStr, rightIndex#, 1) THEN
leftIndex# := leftIndex# + 1;
rightIndex# := rightIndex# - 1;
ELSE
isPolindrom := 'N';
EXIT;
END IF;
END LOOP;

DBMS_OUTPUT.put_line ('isPolindrom = ' || isPolindrom);
END;

🧩 Что важно помнить
В текущей логике цифры тоже игнорируются, потому что для них lowerStr = upperStr.
Если нужен строгий режим «игнорируем только небуквенно-цифровые, но цифры учитываем», придётся скорректировать проверки.

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

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

Дамы и Господа, выбор всегда за вами 😉
#sql #Oracle #PLSQL
👍52👎1
А вы со всем согласны?
Если не согласны, то с чем?