fa
Feedback

فریب کلاهبرداران را نخورید! تل‌متریو این کانال‌ها را شناسایی و برچسب‌گذاری می‌کند 👉 برای مشاهده برچسب، اشتراک تهیه کنید

Oracle Developer👨🏻‍💻

Oracle Developer👨🏻‍💻

رفتن به کانال در Telegram

🔝 канал о разработке в СУБД Oracle: SQL, PL/SQL, оптимизация, архитектура и другое... Backend-pro.ru - обучение по различным программам, связанных с backend-разработкой для ФЛ и ЮЛ. Основатель: @denis_dbd Кивилёв Денис Менеджер: @love_flowerrr Влада

نمایش بیشتر
3 431
مشترکین
+124 ساعت
-37 روز
+230 روز
آرشیو پست ها
Открывал резюме на hh - и сразу куча предложений. А сейчас? 📉 Коллеги, всем привет! 👋 На связи Денис. На рынок я сейчас не
Открывал резюме на hh - и сразу куча предложений. А сейчас? 📉 Коллеги, всем привет! 👋 На связи Денис. На рынок я сейчас не выхожу, занят проектом в банке, до собесов руки не доходят. Но, возможно, скоро выйду. Поэтому слежу, что там вообще происходит, и читаю, что пишут люди, которые ищут работу прямо сейчас. Помните, как было раньше? Открываешь резюме на hh - и не успеваешь отвечать, предложения сыпятся пачкой. А какая ситуация сейчас? Смотрим на цифры. Что показывает статистика hh.индекс - это сколько активных резюме приходится на одну активную вакансию. Для IT: 🔹 4,4 в 2021 (блог hh) 🔹 14,2 в 2025 (там же) 🔹 27,4 в сентябре 2026 (обзор рынка труда hh, стр. 6) Оговорка: первые две цифры - годовая оценка hh, третья - месячный срез. Считали не совсем одинаково, но порядок виден: с 2021 года конкуренция выросла примерно в 6 раз. И ещё из того же обзора: за год активных IT-вакансий стало на 33% меньше, а резюме на 15% больше. По Oracle отдельной цифры у hh нет, только IT целиком. Что делают люди Отвечают автоматизацией. Два свежих кейса с Хабра. Да, выборка не велика, поэтому как статистику рынка не берём, но картина понятная: 🔹 553 отклика за три недели: 79 отказов (14%), 3 собеседования (0,5%), 470 без ответа (85%). Больше 500 раз бот упёрся в вопрос без готового ответа или в капчу 🔹 339 откликов ботом на hh за два месяца: 26 приглашений (7,7%), 133 отказа, 110 открыли и молчат, 70 вообще не открыли. Когда автор расширил поиск, конверсия упала до 2,9% Что отвечают компании По опросу 1500 американских нанимающих менеджеров (июнь 2026), отклики через ИИ прогоняют 58%, годом раньше было 35%. Это США, у нас цифр нет. Зато автор бота на hh насчитал робота-рекрутера или ИИ-помощника компании в 18 из 31 чата с живой перепиской. Получается, роботы пишут роботам 🤷🏻‍♂️ Представляете, больше 27 резюме на одну вакансию. Бешеная конкуренция. Мало того, что нужно умудриться пробиться через все эти ATS-ки, ботов и фильтры. Так ещё, если вас всё-таки пригласили на собеседование, там нужно не облажаться: шанс попасть на следующее крайне мал. Выводы вы можете сделать сами. А теперь вопрос к вам. Кто искал работу в этом году: сколько откликов отправили до первого собеса и что в итоге сработало? 👇 Всем хорошего дня 🤝 #карьера #рынок_труда #hh #собеседование #oracle #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

Один из полезных проектов, который был сделан с Claude Code Чатик оживился после сегодняшней шутки. Решил по ходу пьесы сразу
+2
Один из полезных проектов, который был сделан с Claude Code Чатик оживился после сегодняшней шутки. Решил по ходу пьесы сразу записать видосик по поводу одного из назову это пет-проектов, которые могли бы быть полезны сообществу. Плагин для PLSQL Developer - расширенное форматирование текста, вставка шаблонов с датами, преобразование строк, копирование с резолвингом констант и т.д. Идея и желание - моё. Исполнение - 99% Claude Code ✅ Собрать что-то полезное, улучшить какую-то IDE через плагин, автоматизировать надоевшую рутину - теперь дело не месяцев и лет, а часов и дней. И отрицать это бессмыслено. Либо ты освоил современные средства, либо остался на свалке истории. Я уж не говорю про связку с n8n... ууух 🔥 Если интересно могу тоже про это как-нибудь рассказать. ✅ Видос про плагин можно посмотреть тут -> https://www.loom.com/share/6d0231882f9e4dfa8d1ec7fe1380dc56 ✅ Исходники - https://github.com/kivilev/plsql-developer-text-tools #oracle #sql #базы_данных #оптимизация_sql #разработка #аналитика #обучение #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

Я это сделал 😎 Hello, народ! 👋 С вами Денис. Сегодня пятничный юмор 😄 Картинка выше - навеяла. Иногда ловлю себя на странн
Я это сделал 😎 Hello, народ! 👋 С вами Денис. Сегодня пятничный юмор 😄 Картинка выше - навеяла. Иногда ловлю себя на странном чувстве. Что-то сделал. Написал сервис, скрипт, автоматизацию, собрал бота. Работает, красиво, всё как задумано. Но если честно: код писал не я, а Claude Code. Я объяснял, смотрел, ругался, говорил «нет, не так» и «а теперь давай ещё вот это». А гордость при этом - моя. И радуюсь я так, будто сам набрал каждую строчку руками 🥹 Сказали бы мне: «Ты это сделал?» - и я бы честно ответил: «Ну... я». Потому что идея моя, задача моя, и отвечать за результат тоже мне 🤷🏻‍♂️ А у вас так бывает? Гордитесь тем, что «сделали» вместе с ИИшкой? Или совесть мучает? Пишите в комментах 💬 Хороших выходных и быстрейшего сброса лимитов! 🤖 #юмор #ai #claude #пятница #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

Bind не спасёт: имя таблицы в динамическом SQL Коллеги, всем привет! 👋 С вами Денис. В посте про execute immediate и bind-пе
Bind не спасёт: имя таблицы в динамическом SQL Коллеги, всем привет! 👋 С вами Денис. В посте про execute immediate и bind-переменные вы справедливо заметили, что раскрыл я не всё. Aleksander: «имя таблицы bind-ом не передать», «про dbms_sql не упомянуто». Kirill: «литералы можно клеить через dbms_assert». Сергей: «одно значение в нескольких плейсхолдерах». Разбираю по порядку. Всё прогнал на 11g XE, 18c XE и 23ai. 1. Имя таблицы bind-ом не передать
execute immediate 'select count(*) from :t' into n using 'EMPLOYEES';
-- ORA-00903: invalid table name
Bind - это значение, а не часть текста запроса. Имя таблицы или столбца придётся склеивать. Тогда его надо проверить. 2. dbms_assert: что каждая функция проверяет 🔹 simple_sql_name('EMPLOYEES; drop table x') - ORA-44003, проверяет только форму имени 🔹 sql_object_name('HR.EMPLOYEES') - вернёт имя, NO_SUCH_TAB даст ORA-44002: объект должен существовать 🔹 enquote_name('employees') - вернёт "EMPLOYEES": в кавычках и в верхнем регистре С sql_object_name склейка безопасна: '... from '||dbms_assert.sql_object_name(p). Строка employees where 1=0 не пройдёт (ORA-44002). 3. enquote_literal: не экранирует, а проверяет Kirill прав, литерал так клеить можно, но есть нюанс. enquote_literal('O''Reilly') с одиноким апострофом падает с ORA-06502. Принимает только уже удвоенные кавычки. Значит, правильно так:
dbms_assert.enquote_literal(replace(p, '''', ''''''))
Зато и x' or '1'='1 в такой литерал не протащить. Но цена прежняя: на каждое значение свой курсор. Я прогнал три значения - в v$sql три разных текста по 1 выполнению, а с bind - один курсор и 3 выполнения. 4. Одно значение, несколько плейсхолдеров В SQL-операторе binds позиционные: :a ... :a требует using 50, 50, а using 50 даст ORA-01008. В анонимном PL/SQL-блоке привязка по имени: begin :r := :a + :a; end; берёт using out n, 5 и считает 10. 5. dbms_sql: когда столбцы неизвестны заранее
c := dbms_sql.open_cursor;
dbms_sql.parse(c, 'select * from '
  ||dbms_assert.simple_sql_name(tab), dbms_sql.native);
dbms_sql.describe_columns(c, ncols, cols);
for i in 1..ncols loop
  dbms_sql.define_column(c, i, v, 4000);
end loop;
rc := dbms_sql.execute(c);
while dbms_sql.fetch_rows(c) > 0 loop
  for i in 1..ncols loop
    dbms_sql.column_value(c, i, v);
    dbms_output.put(cols(i).col_name||'='||v||'  ');
  end loop;
  dbms_output.new_line;
end loop;
dbms_sql.close_cursor(c);
Для departments вернуло DEPARTMENT_ID=10 DEPARTMENT_NAME=Administration .... Столбцов мы не знали, пока не спросили у курсора. Для такого execute immediate не годится. Здесь я для простоты читаю все столбцы как строку; для LOB и прочих типов смотрите cols(i).col_type. — Итого: bind для значений, dbms_assert для имён, dbms_sql для запросов, о которых вы ничего не знаете заранее. А где у вас на проде без склейки не обошлось? Какие задачи это были? Пишите в чатике 💬 Всем хорошего дня 🤝 #oracle #plsql #динамический_sql #dbms_assert #dbms_sql #bind #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

Параметры в WITH: приём, который подсказали читатели 🤝 Коллеги, всем привет! 👋 С вами Денис. Под постом про execute immedia
Параметры в WITH: приём, который подсказали читатели 🤝 Коллеги, всем привет! 👋 С вами Денис. Под постом про execute immediate и bind-переменные двое читателей независимо подсказали один и тот же приём. Daniil и Александр. Спасибо чату, разбираю. Проблема Берёте запрос из кода или из AWR. В нём :dept, :sal и ещё восемь бинд-переменных, причём одно значение торчит в нескольких местах. Чтобы прогнать его руками, надо везде подставить значения. Долго, и легко ошибиться в одном из мест. Приём Вынести параметры в один CTE и везде обращаться к нему:
-- было: значения надо подставлять в каждом месте
select employee_id, last_name, salary
  from employees
 where department_id = :dept
   and salary > :sal
   and hire_date >= (select min(hire_date)
                       from employees
                      where department_id = :dept);

-- стало: значения живут в одном месте
with p as (select 50 as dept, 3000 as sal from dual)
select e.employee_id, e.last_name, e.salary
  from employees e cross join p
 where e.department_id = p.dept
   and e.salary > p.sal
   and e.hire_date >= (select min(hire_date)
                         from employees
                        where department_id = p.dept);
Оба запроса я прогнал на HR-схеме, результат одинаковый (23 строки). Необязательный параметр, как у Daniil, делается через
nvl: where e.department_id = nvl(p.dept, e.department_id). 
Если p.dept равен null, условие пропускается. Подвох Это отладочный приём, и план у такого запроса может отличаться от боевого. Проверил на 11g XE, 18c XE и 23ai, везде одинаково. Таблица employees, в отделе 50 - 45 человек, в отделе 10 - один: 🔹 запрос с :dept: первый запуск со значением 50 - Oracle подсмотрел значение (bind peeking) и выбрал TABLE ACCESS FULL. Второй запуск со значением 10 использует тот же курсор, и снова full scan на одну строку 🔹 тот же запрос через CTE со значением 10: INDEX RANGE SCAN, в предикате уже литерал "DEPARTMENT_ID"=10 ⚠️ Значит, пока вы отлаживаете логику - CTE удобнее. Когда разбираете производительность, прогоняйте запрос с настоящими :dept (в SQL*Plus через variable и exec), иначе смотрите не на тот план. Плюс литералы дают отдельный sql_id на каждое значение, как мы с вами обсуждали в посте про bind. Ещё один хороший приём от Daniil: в первой строке скрипта комментарий «ЗАПРЕЩЕНО ЗАПУСКАТЬ НА ПРОДЕ В ТАКОМ ВИДЕ». Чтоб CTE-версия не уехала в код. Александр считает, что параметрическое CTE удобно и в эксплуатации. С этим можно спорить: литералы в тексте запроса - это ровно то, от чего мы бежали в прошлом посте. Но можно положить в p и сами биндовые :dept - тогда каждая переменная встречается в запросе один раз. Коллеги, вопрос в чат: как вы запускаете руками запрос с десятью бинд-переменными? Подставляете в редакторе, делаете variable/exec, или у вас есть свой приём? 💬 Всем хорошего дня 🤝 #oracle #sql #bind #отладка #cte #базы_данных #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

553 отклика, 3 собеса. И бот, который упирается в капчу 🤖 Коллеги, всем привет! 👋 С вами Денис. Наткнулся на Хабре на стать
553 отклика, 3 собеса. И бот, который упирается в капчу 🤖 Коллеги, всем привет! 👋 С вами Денис. Наткнулся на Хабре на статью: автор, Python-разработчик, написал бота, который сам находит вакансии и отправляет отклики. Прогнал три недели и выложил цифры. Читается как хороший эксперимент, поэтому делюсь. Что там и что я об этом думаю. Сразу оговорка: все цифры ниже - со слов автора, проверить их я не могу. Что получилось у автора 🔹 553 отклика: 79 отказов (14%), 470 тишины (85%), 3 собеседования (0,5%) 🔹 63 отклика в «VIP»-лаборатории - 10 отказов и ни одного собеса 🔹 все три собеса - в компаниях среднего размера и на роли, о которых он сначала не думал (forward deployed engineer, DevRel) Что зацепило меня Самое интересное не воронка, а то, где бот встал. Из 1127 открытых форм до кнопки «отправить» дошли 553, а 575 остановились. Причины, по автору: ▫️ 383 - вопрос в форме, на который нет шаблонного ответа (зарплата числом, ссылка на тестовое, есть ли знакомые в компании) ▫️ 132 - капча ▫️ ещё 60 - ошибки формы, неподтверждённая отправка и прочее Получается, отсев сдвинулся из резюме в саму форму. И это уже не «HR читает резюме», это гонка роботов с двух сторон: бот кандидата против ATS-защиты. Ещё деталь: экран «Спасибо за отклик» ничего не гарантирует. Автор сверял с почтой, и письмо-подтверждение пришло только на 188 откликов из 553. К чему я это всё Понимаю, что мы здесь в основном Oracle-разработчики, и вакансий в нашей нише не столько, чтобы городить под них робота. Но откликов можно собирать не только с сайтов вакансий: есть форумы, чаты, каналы. Поэтому мне просто интересно. Коллеги, расскажите: 1️⃣ Кто-нибудь пробовал автоматизировать поиск работы: сбор вакансий, рассылку откликов, подгонку резюме? 2️⃣ Что получилось, а что оказалось бесполезным? 3️⃣ Как вы считаете, есть ли в этом вообще смысл или лучше пойти через знакомых? Пишите в чатике 💬 Всем хорошего дня 🤝 #карьера_в_IT #собеседование #oracle #автоматизация #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

EXECUTE IMMEDIATE и конкатенация: страшная залепа 😱 Коллеги, всем привет! 👋 На связи Денис. На днях смотрел рабочий код и у
EXECUTE IMMEDIATE и конкатенация: страшная залепа 😱 Коллеги, всем привет! 👋 На связи Денис. На днях смотрел рабочий код и увидел просто страшную залепу. Динамический SQL, а все параметры вклеены в текст запроса конкатенацией. Материал для Junior и Middle, но такое, к сожалению, пишут и сеньоры 🍅 Примерно так (упростил и переписал на HR-схему):
l_sql := 'select count(*) from employees where department_id = ' || p_dept_id ||
         ' and last_name = ''' || p_last_name || '''' ||
         ' and hire_date >= ''' || p_hire_from || '''';
execute immediate l_sql into l_cnt;
Работает? Работает. А теперь по пунктам, почему так нельзя. 1️⃣ Каждый вызов - новый запрос Для Oracle ... = 101 и ... = 102 - это два разных текста, а значит, два разных SQL_ID и два hard parse. Прогнал цикл на 10 000 вызовов:
concat: 15.61 s   -- 10 000 разных SQL_ID
bind:   .15 s     -- 1 SQL_ID, 10 000 executions
В 100 раз медленнее, и это на пустой тестовой базе. На проде вы ещё и забиваете shared pool мусором: у меня из 10 000 курсоров в v$sql дожили 1319, остальные вытеснили друг друга. Заодно вытесняются и нормальные запросы соседей. 2️⃣ SQL-инъекция Передаём фамилию с сюрпризом:
p_last_name := 'OConnell'' or ''1''=''1';
Конкатенация: вернула 107 строк, то есть всю таблицу. С bind-переменной - 0, потому что это просто такая странная фамилия 😄 3️⃣ Кавычки и O'Connell Обычная фамилия с апострофом ломает запрос:
-- 23ai
ORA-03049: SQL keyword 'CONNELL' is not syntactically valid following '...= 80 and last_name = 'O''
-- 18c
ORA-00933: SQL command not properly ended
4️⃣ Даты через NLS p_hire_from - это date, при конкатенации он неявно превращается в строку по NLS_DATE_FORMAT, а потом обратно. Поставил формат DD.MM.YY и передал 01.01.1999:
string: 01.01.99
concat: 0      -- 99 превратился в 2099 год
bind:   107
Поменяли настройки сессии - поменялся результат запроса. Хрен найдёшь потом 🤦🏻‍♂️ Как надо Значения - только через bind-переменные и using:
execute immediate
  'select count(*)
     from employees
    where department_id = :dept_id
      and last_name     = :last_name
      and hire_date    >= :hire_from'
  into l_cnt
  using p_dept_id, p_last_name, p_hire_from;
Один SQL_ID на все вызовы, инъекции нет, кавычки и даты не ваша забота ✅ Дисклеймер А теперь главное: в примере выше динамический SQL вообще не нужен. Где можно написать статический SQL - пишите статический:
select count(*)
  into l_cnt
  from employees
 where department_id = p_dept_id
   and last_name     = p_last_name
   and hire_date    >= p_hire_from;
PL/SQL-переменные в статическом SQL и так становятся bind-переменными. Плюс компилятор проверит, что таблица и колонки существуют, а зависимости будут видны в user_dependencies. Динамика нужна, когда в рантайме меняется сама структура запроса: имя таблицы, набор колонок, условия. И даже тогда значения идут через bind, а имена объектов - через dbms_assert. Прогнал всё на 23ai и 18c, поведение одинаковое, отличается только текст ошибки в п.3. Подробнее - в документации Oracle по динамическому SQL. Итог Конкатенация в execute immediate - это минус производительность, минус безопасность и плюс баги, которые зависят от настроек сессии. Сначала статический SQL, если никак - динамический с bind-переменными. Если интересно, сниму небольшой видос про это - ставьте 👍🏻 А у вас на код-ревью такое ловят? Или проскакивает? Пишите в чатике 💬 С вами был Денис. Всем хорошего дня 🤝 #oracle #plsql #sql #oracledeveloper #performance #базы_данных #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

NO_DATA_FOUND, который не падает 🫥 Коллеги, всем привет! 👋 На связи Денис. Продолжаем тему тихих ошибок после when others t
NO_DATA_FOUND, который не падает 🫥 Коллеги, всем привет! 👋 На связи Денис. Продолжаем тему тихих ошибок после when others then null. Снова простенькое, для Junior и Middle 😄 Итак, функция-справочник. Таких в любом проекте до фига:
create or replace function get_dept_name(p_dept_id number) return varchar2 is
  l_name departments.department_name%type;
begin
  select department_name into l_name
    from departments
   where department_id = p_dept_id;
  return l_name;
end;
Все знают: select into без строк = ORA-01403: no data found. Проверяем в PL/SQL:
begin
  dbms_output.put_line(get_dept_name(999));
end;
ORA-01403: no data found
ORA-06512: at "HR.GET_DEPT_NAME", line 4
Упало, всё честно ✅ А теперь та же функция в обычном запросе:
select get_dept_name(10)  d10,
       get_dept_name(999) d999
  from dual;
D10              D999
---------------  ------
Administration   (null)
Никакой ошибки. Отдела 999 нет, а запрос спокойно вернул NULL 🤷🏻‍♂️ Почему так? Для SQL-движка ORA-01403 - это не ошибка, а сигнал "строк больше нет". Он долетает из функции и SQL воспринимает его как "ну нет так нет", подставляя NULL. В PL/SQL-блоке такой магии нет, там исключение летит наверх как положено. Причём это только про NO_DATA_FOUND. Если в функции select into вернёт несколько строк, запрос упадёт по-честному:
ORA-01422: exact fetch returns more than requested number of rows
Чем это грозит? 🔹 Отчёт с get_dept_name(...) в select-листе. Кривой id - и в отчёте пустая ячейка вместо ошибки. Никто не заметит неделями. 🔹 insert into ... select ..., get_xxx(id) from ... - в таблицу молча едут NULL-ы, если колонка не NOT NULL. 🔹 Одна и та же функция ведёт себя по-разному в PL/SQL и в SQL. Тесты на PL/SQL зелёные, а в отчёте пусто. Как надо ✅ Решить, что для вас значит "нет данных", и написать это явно. Если это ошибка - перевыбросить своим исключением, его SQL уже не проглотит:
exception
  when no_data_found then
    raise_application_error(-20001, 'Нет отдела ' || p_dept_id);
ORA-20001: Нет отдела 999
ORA-06512: at "HR.GET_DEPT_NAME", line 10
✅ Если NULL - нормальный ответ, тоже напишите это явно: when no_data_found then return null;. Тогда следующий разработчик хотя бы увидит, что это задумано, а не случайно. ✅ А вообще для отчётов лучше left join departments, чем функция на каждую строку. Но это уже другая история 😊 Хозяйке на заметку: прогнал на 11g, 18c и 23ai, поведение везде одинаковое. Разве что в 23ai у ORA-01422 текст немного другой и ссылка на доку. Документация: предопределённые исключения и SELECT INTO. Итог select into в функции, которую вызывают из SQL, - это потенциальный тихий NULL. Пусть функция сама говорит, ошибка это или нормальный ответ, а не SQL решает за вас. А у вас в команде есть правило, что делать с NO_DATA_FOUND в функциях-справочниках? Или каждый как хочет? Пишите в чатике 💬 С вами был Денис. Всем хорошего дня 🤝 #oracle #plsql #sql #oracledeveloper #базы_данных #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

🎥 Index Scan vs Table Access Full: 4 чтения против 15 396 Коллеги, всем привет! 👋 На связи Денис. Я тут последнее время ставлю эксперименты над форматами: карусели, квизы, рилсы, видосы. Что-то заходит, что-то не очень 🤷🏻‍♂️ И вот решил опробовать новую штуку - анимированную визуализацию. Первым подопытным стала база-база: чем индексный доступ отличается от TABLE ACCESS FULL. Что в видосе? 🔸 Как Oracle спускается по B-дереву: корень → ветка → лист. 🔸 Как в листе находит ROWID и по нему сразу идёт в нужный блок таблицы. 🔸 Что будет без индекса: читаем все блоки подряд, хотя нужна одна строка. Цифры не нарисованные, прогнал на Oracle 23ai. Таблица на 1 млн строк, первичный ключ, BLEVEL индекса 2:
select * from anim_t where id = 142;
🔹 INDEX UNIQUE SCAN + TABLE ACCESS BY INDEX ROWID - 4 буфера (3 блока индекса + 1 блок таблицы) 🔹 с хинтом full(anim_t) - TABLE ACCESS FULL - 15 396 буферов. Oracle последовательно читает каждый блок ниже HWM. Цифры брал из dbms_xplan.display_cursor с форматом ALLSTATS LAST, колонка Buffers. Короче говоря, одна и та же строка, а разница - в 3 849 раз по чтениям. Теперь главное. Формат для меня новый, поэтому накидайте в панамку 😄 в чатике 💬 🔸 зашло или такое лучше текстом? 🔸 какую тему визуализировать следующей? Всем хорошего дня 🤝 #oracle #sql #индексы #оптимизация #executionplan #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

WHEN OTHERS THEN NULL - тихий убийца 🤫 Коллеги, всем привет! 👋 На связи Денис. Последние посты были прям хардкорные: SQL_ID
WHEN OTHERS THEN NULL - тихий убийца 🤫 Коллеги, всем привет! 👋 На связи Денис. Последние посты были прям хардкорные: SQL_ID, хэши, трансляторы, кэш скалярных подзапросов. Изнанка и тёмная магия Oracle 🧙‍♂️ Давайте выдохнем и разберём что-нибудь попроще, но не менее важное. Материал для Junior и Middle, сеньоры-помидоры 🍅 могут кивать и вспоминать свою молодость. Итак, классика жанра. Видели такое?
create or replace procedure set_salary(p_emp_id number, p_salary number) is
begin
  update employees set salary = p_salary where employee_id = p_emp_id;
exception
  when others then
    null;
end;
Вызываем с кривыми данными:
begin
  set_salary(100, -1);
  dbms_output.put_line('Зарплата обновлена');
end;
Результат:
Зарплата обновлена
PL/SQL procedure successfully completed.
А в таблице как было 24000, так и осталось. На самом деле update упал на check-констрейнте ORA-02290, но мы об этом никогда не узнаем. Ни мы, ни вызывающий код, ни пользователь, который через неделю придёт с вопросом "а где мои деньги?" 🤷🏻‍♂️ Почему так пишут? 🔹 "Чтобы не падало". Не падает, да. Просто молча делает не то. 🔹 Скопировали из старого кода, а там так было. 🔹 Поставили на время отладки и забыли. Как надо ✅ Ловим только те исключения, которые реально умеем обработать: no_data_found, dup_val_on_index и т.д. Обработать - значит сделать что-то осмысленное, а не "забыть". ✅ Если when others всё-таки нужен (логирование, например), он заканчивается на raise:
exception
  when others then
    log_error(sqlerrm, dbms_utility.format_error_backtrace);
    raise;
Ошибка залогирована, вызывающий код про неё знает, транзакция не продолжается как ни в чём не бывало. ✅ dbms_utility.format_error_backtrace - хозяйке на заметку: показывает номер строки, где реально упало. Без него в логе будет только строка обработчика, и ищи потом ветра в поле. Компилятор вам подскажет Включите предупреждения:
alter session set plsql_warnings = 'ENABLE:ALL';
И на первую версию процедуры Oracle сам скажет. В 11g и 18c:
PLW-06009: procedure "SET_SALARY" OTHERS handler does not end in RAISE or RAISE_APPLICATION_ERROR
В 23ai текст короче, просто ... does not end in RAISE, суть та же. Кстати, прогнал всё на 11g, 18c и 23ai: when others then null везде одинаково молча глотает ошибку. Подробнее про обработку ошибок - в документации Oracle по PL/SQL. Итог Упавшая процедура - это неприятно. Процедура, которая молча не сработала, - это пипец, потому что ищется она неделями. Пусть лучше падает громко. А у вас в проекте есть when others then null? Признавайтесь в чатике, сколько штук нашли поиском по коду 😄💬 С вами был Денис. Всем хорошего дня 🤝 #oracle #plsql #sql #oracledeveloper #базы_данных #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

Один запрос - пять SQL_ID 🤷🏻‍♂️ Коллеги, всем привет! 👋 На связи Денис. Под постом про SQL_ID o1ga спросила: почему Oracle
Один запрос - пять SQL_ID 🤷🏻‍♂️ Коллеги, всем привет! 👋 На связи Денис. Под постом про SQL_ID o1ga спросила: почему Oracle не нормализует текст перед хэшированием? Обсуждали полдня, разбираю. Проверяем Берём запрос:
select count(*) from hr.employees where department_id = 50;
И пишем его пятью способами: капсом, с лишним пробелом, с комментарием и с другим литералом (60 вместо 50):
вариант                      sql_id         план
select count(*) ... = 50     0t6zm0g0cb5jf  2271004725
SELECT COUNT(*) ... = 50     9vq3m92g68wf1  2271004725
лишний пробел                cr05nbnshfn1a  2271004725
select /* report */ ...      ag1rnuz6z28yj  2271004725
тот же, но = 60              70cp0jaa5knrt  2271004725
Пять SQL_ID, пять курсоров, пять hard parse. А план один и тот же. Это к версии Александра: план совпал, но курсоры всё равно разные. Почему так По документации всё просто: текст хэшируется, хэш ищется в shared pool, потом текст сравнивается символ в символ, включая пробелы, регистр и комментарии. Почему не нормализуют (моё мнение): 🔹 Хэш от байтов - копейки, а делается он на каждый parse call. Нормализация - это уже разбор текста. 🔹 Регистр в литерале 'Abc' трогать нельзя, комментарий-хинт /*+ ... */ выкидывать тоже нельзя. Простой upper + trim сломал бы запросы. 🔹 Одинаковый текст - ещё не один курсор. В той же доке один SQL_ID в схемах OE и SH, а курсоры-потомки разные: таблицы-то разные. А нормализовать Oracle умеет В V$SQL есть EXACT_MATCHING_SIGNATURE - сигнатура по нормализованному тексту: без лишних пробелов и в верхнем регистре всё, кроме литералов. Есть ещё FORCE_MATCHING_SIGNATURE - та же сигнатура, но литералы заменены на bind. Смотрим на те же пять вариантов (одинаковая буква - одинаковая сигнатура):
вариант                  exact  force
select count(*) ... = 50   A      X
SELECT COUNT(*) ... = 50   A      X
лишний пробел              A      X
select /* report */ ...    B      Y
тот же, но = 60            C      X
Регистр и пробелы exact-сигнатура прощает, комментарий и литерал - нет. Force-сигнатура прощает ещё и литерал. Короче говоря, нормализация есть, просто не для SQL_ID. Статический SQL в PL/SQL Кирилл подсказал: PL/SQL приводит статический SQL к единому виду сам. Проверил:
declare l number; begin select count(*) into l from hr.employees where department_id = 50; end;
DECLARE L NUMBER; BEGIN SELECT   COUNT(*) INTO L FROM HR.EMPLOYEES   WHERE DEPARTMENT_ID = 50; END;
В V$SQL одна строка и 2 выполнения:
9vq3m92g68wf1  SELECT COUNT(*) FROM HR.EMPLOYEES WHERE DEPARTMENT_ID = 50
Верхний регистр, по одному пробелу, INTO выкинут, комментарии вырезаны, переменные стали :B1. Литерал 50 остался литералом. И SQL_ID совпал с моим запросом капсом из SQL*Plus 😄 Где это применить 🔹 Литералы против bind. 10 000 запросов where salary = <i> на 18c: с литералами 10 044 hard parse и 6.89 с, с bind - 1 hard parse и 0.36 с. 🔹 Один запрос в трёх стилях в коде приложения - три курсора в shared pool. Единый стиль SQL в команде - не только про красоту. 🔹 Ищете в V$SQL одинаковые запросы с разными литералами - группируйте по FORCE_MATCHING_SIGNATURE. 🔹 cursor_sharing = force заменяет литералы на :"SYS_B_0", но регистр не трогает: select и SELECT остались двумя SQL_ID. Проверял на 11g, 18c и 26ai: всё совпало. Итог SQL_ID - хэш от текста байт в байт, и это by design. Нормализацию за вас сделает PL/SQL, а в приложении - bind-переменные и единый стиль. А у вас в команде есть единый стиль SQL или каждый пишет как привык? Пишите в чатик 💬 С вами был Денис. Всем хорошего дня 🤝 #oracle #sql #sql_id #plsql #оптимизация #performance #фишки #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

Семья - самое главное ✅ Друзья всем привет! Сегодня будет воскресный щитпостинг 😊 Не нравится - не читай. В понедельник будут новые технические посты. Накопилось уйма дел, мыслей, всяких моментов, которые нужно решать, но мы рванули с семейством в 3х дневный отпуск. Я решил забить на всё и совершить детокс и побыть вместе с детками. Тем более, что еще в июле перед отъездом обещал старшей дочере свозить их в аквапарк. Пока был в поезде уж очень соскучился. Не могу уже без троих мелких засранцев. Тем более, время летит невероятно быстро. Так оглянуться не успеешь и все вырастут и скажут "пока, папа" и встречи будут редки, да что там встречи - просто телефонные звонки. Короче, пока мелкие надо быть вместе. Не хочу потом жалеть, что отдавался только работе, а с детьми проводил мало времени. Иногда представлю себе, что мне сейчас 80 лет и у меня есть возможность на один день оказаться в прошлом. И я оказываюсь именно в то время, когда детки маленькие сладенькие ❤️ Потом открываю глаза, а мне 43 и они пока маханькие и это реально. В общем, стоит бывать больше с семьей. Иногда стоит выныривать из рутины, суеты и повседневных дел и просто пожить здесь и сейчас, потому что потом это уже не повторить. А работа... она никуда не убежит. Всем хорошего дня! Написал спонтанно пост, пусть на 3+ из 5, но лучше такой опубликованный чем вообще никакой. #oracle #sql #plsql #dbms_sql_translator #оптимизация #фишки #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

Как Oracle считает SQL_ID: разбираем по символам 🔍 Коллеги, всем привет! 👋 На связи Денис. В курсе у меня была ссылка на ст
Как Oracle считает SQL_ID: разбираем по символам 🔍 Коллеги, всем привет! 👋 На связи Денис. В курсе у меня была ссылка на статью о том, как формируется SQL_ID. И она протухла - так бывает 🤷🏻‍♂️ Полез разбираться заново и по ходу пьесы наткнулся на DBMS_SQL_TRANSLATOR (прошлый пост). Обещал рассказать про его функцию SQL_ID - рассказываю. SQL_ID без выполнения запроса
select dbms_sql_translator.sql_id('select * from dual')   sql_id,
       dbms_sql_translator.sql_hash('select * from dual') hash_value
  from dual;

SQL_ID        HASH_VALUE
------------- ----------
a5ks9fhw2v9s1  942515969
Выполняем сам запрос - в V$SQL ровно те же a5ks9fhw2v9s1 и 942515969. Запрос никуда не отправляли, а SQL_ID уже знаем. Как он считается Oracle нигде это не документирует, но давно раскопано (Tanel Poder, Carlos Sierra). Tanel даже выложил скрипт, а я повторил руками: 1️⃣ MD5 от текста запроса + chr(0) в конце. 2️⃣ Берём последние 8 байт и переворачиваем каждые 4 байта. 3️⃣ Получилось 64-битное число - пишем его в base32 с алфавитом 0123456789abcdfghjkmnpqrstuvwxyz (без e, i, l, o). 4️⃣ HASH_VALUE - это младшие 32 бита того же числа.
md5 = 02FC540D4440ADB2 7409CBA2 01A72D38
8 байт:      74 09 CB A2 | 01 A7 2D 38
переворот:   A2CB0974    | 382DA701
base32(A2CB0974382DA701) = a5ks9fhw2v9s1
0x382DA701 = 942515969 = HASH_VALUE
Что значит каждый символ 13 символов × 5 бит = 65 бит, а число 64-битное. Поэтому:
a5ks9fhw2v9s1
символ 1     - 4 бита: только 0-9,a,b,c,d,f,g
символы 2-6  - старшая половина
символ 7     - 3 бита старшей + 2 бита HASH_VALUE
символы 8-13 - 30 младших бит HASH_VALUE
Проверил по 700+ курсорам в V$SQL: первый символ ни разу не вышел за 0-g. А HASH_VALUE по SQL_ID восстанавливается однозначно: dbms_utility.sqlid_to_sqlhash('a5ks9fhw2v9s1') = 942515969. Обратно - нет, половина бит теряется. Подводные камни 🔹 Текст байт в байт. SELECT * FROM dual - уже 3vjxpmhhzngu4. 🔹 Точка с запятой: select * from dual; даст 143pd7y3v0tyz. SQL*Plus её отрезает, функция - нет. 🔹 Текст берите из SQL_FULLTEXT, а не из SQL_TEXT: в SQL_TEXT переводы строк заменены пробелами, и SQL_ID не сойдётся. 🔹 Хозяйке на заметку: для пары курсоров вида BEGIN dbms_output.enable(NULL); END; SQL_ID не сходился. Оказалось, клиент прислал текст с лишним chr(0) в конце, которого в V$SQL не видно 😄 🔹 Под профилем трансляции в V$SQL свой SQL_ID у переведённого текста, а в USER_SQL_TRANSLATIONS.SQL_ID - у исходного. Проверял на Oracle 26ai (23.26), 18c и 11g: SQL_ID одного текста везде одинаковый. Только в 11g самой DBMS_SQL_TRANSLATOR нет (она с 12c), там - ORA-00904. Итог SQL_ID - это просто кусок MD5 от текста в base32, ничего магического. Знаешь текст - знаешь SQL_ID, хоть на тесте, хоть на проде. Кстати, как получить SQL_ID сразу после выполнения, я рассказывал в посте про set feedback on SQL_ID. А вы знали, что последние символы SQL_ID - это почти HASH_VALUE? Пишите в чатик 💬 С вами был Денис. Всем хорошей пятницы 🤝 #oracle #sql #sql_id #dbms_sql_translator #оптимизация #performance #фишки #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

DBMS_SQL_TRANSLATOR: подменяем запрос, не трогая код 🔍 Коллеги, всем привет! 👋 На связи Денис. Копался тут, как Oracle счит
DBMS_SQL_TRANSLATOR: подменяем запрос, не трогая код 🔍 Коллеги, всем привет! 👋 На связи Денис. Копался тут, как Oracle считает SQL_ID, и по ходу пьесы наткнулся на интересный пакет - DBMS_SQL_TRANSLATOR (есть с 12c). Кстати, в нём есть функция SQL_ID - про неё в следующем посте. А сам пакет умеет на лету подменять текст запроса: приложение шлёт одно, а база выполняет другое. Честно скажу: в своей практике я ни разу не видел, чтобы его использовали. Если кто-то применял на проде - поделитесь в комментах, очень интересно 🙏 По мне, это тёмная магия Oracle 🧙‍♂️ Знает про неё узкий круг специалистов, а используют её очень редко. Сделаете на ней что-то - коллеги просто не поймут, что происходит, и запутаются: в коде один запрос, а в базе выполняется другой. Как это работает Создаём профиль трансляции, регистрируем пару «исходный SQL -> SQL на замену» и включаем профиль в сессии. begin dbms_sql_translator.create_profile('APP_FIX'); -- переводить и обычный оракловый SQL, а не только "чужой" dbms_sql_translator.set_attribute('APP_FIX', dbms_sql_translator.attr_foreign_sql_syntax, dbms_sql_translator.attr_value_false); dbms_sql_translator.register_sql_translation('APP_FIX', 'select count(*) from orders where status = :b1', 'select /*+ full(o) */ count(*) from orders o where status = :b1'); end; / alter session set sql_translation_profile = APP_FIX; select count(*) from orders where status = :b1; Без профиля план был INDEX RANGE SCAN, с профилем - TABLE ACCESS FULL, DBMS_XPLAN показывает текст уже с хинтом. Проверил на Oracle 26ai (23.26) и 18c, ведёт себя одинаково. А если атрибут не ставить? По умолчанию FOREIGN_SQL_SYNTAX = TRUE, и запросы из SQL*Plus не переводятся. Нужен ещё alter session set events = '10601 trace name context forever, level 32', а для него - привилегия ALTER SESSION. Приложению профиль включают logon-триггером или через атрибут сервиса (DBMS_SERVICE). Подсмотрел в интернете, как его используют 🔹 Чинят тормозящий запрос вендорского софта без релиза: находят SQL_ID в shared pool, регистрируют замену, вешают logon-триггер (пошаговый гайд). А в Panorama скрипт подмены генерится по SQL_ID. 🔹 Миграция с Sybase и SQL Server: транслятор переводит чужой диалект в оракловый, приложение почти не трогают. Под это пакет изначально и делали. 🔹 Обходят неправильные результаты - подменяют запрос на исправленный (Kerry Osborne). 🔹 Фокусы на конференциях 😄 - один и тот же запрос «магически» становится быстрым, а на деле его тихо перенаправили на другую таблицу (Julian Dontcheff). Подводные камни 🔸 Текст должен совпасть один в один. Проверил: другой регистр, лишний пробел, перенос строки или бинд :b2 вместо :b1 - и подмены нет. 🔸 SQL внутри PL/SQL (и статический, и execute immediate) не переводится, только то, что шлёт клиент. 🔸 Права. Владельцу - CREATE SQL TRANSLATION PROFILE. Чтобы профиль работал у другого юзера: grant use on sql translation profile ему (иначе ORA-24252) и grant translate sql on user app владельцу (иначе ORA-01031). 🔸 Отключить подмену - enable_sql_translation(..., false). Процедуры DISABLE_SQL_TRANSLATION нет. 🔸 REGISTER_ERROR_TRANSLATION в SQL*Plus код ошибки не подменил - это для драйверов при миграции. Итог Чтобы просто подсунуть хинт, есть штатные SQL Patch и SQL Plan Baseline. А если надо переписать сам запрос, а код не ваш - DBMS_SQL_TRANSLATOR рабочий вариант. Но раз уж применили тёмную магию - обязательно документируйте и передавайте знание команде. Иначе через полгода никто не вспомнит, почему прод выполняет не тот запрос, что в коде, и кто-то потратит неделю на расследование 🕵️ А вы встречали его в бою? Пишите в чатик 💬 С вами был Денис. Всем предсказуемых запросов 🤝 #oracle #sql #plsql #dbms_sql_translator #оптимизация #фишки #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

پیام ویدیو00:56

with function и pragma udf: обещали быстрее, проверяем 🔍 Коллеги, всем привет! 👋 На связи Денис. В посте про функции в SQL я вскользь написал: «С 12c: pragma udf или with function - дешевле переключение контекста». Юрий в комментах: «вот это интересно, примеров бы». Держите. С замером 😄 with function Функция объявляется прямо в запросе, без create:
with
  function get_vat(p_sum number) return number is
  begin
    return round(p_sum * 0.2, 2);
  end;
select salary, get_vat(salary) vat
  from employees
/
Когда удобно: разовый скрипт, нет прав на create, логика нужна только этому запросу. Александр в комментах поделился кейсом: через with function собирает трейс-файл из v$diag_trace_file_contents в CLOB. Прям в одном запросе, без объектов в схеме 👍🏻 Нюансы: 🔹 Если with function не в top-level select (вложенный запрос, insert/update/merge) - нужен хинт /*+ with_plsql */, иначе ORA-32034: unsupported use of WITH clause. Хинт не оптимизаторский, без него запрос просто не разберется.
insert /*+ with_plsql */ into t_vat
with
  function get_vat(p_sum number) return number is
  begin
    return round(p_sum * 0.2, 2);
  end;
select salary, get_vat(salary) from employees
/
🔹 В SQL*Plus/SQLcl запрос заканчивается /, а не ; - внутри же PL/SQL. 🔹 Имя из with перекрывает одноименную функцию схемы. pragma udf Хранимая функция с пометкой «меня вызывают из SQL»:
create or replace function get_vat(p_sum number) return number is
  pragma udf;
begin
  return round(p_sum * 0.2, 2);
end;
/
В документации скромно: «might improve its performance». Might. Ну ок, проверяем. Замер sum(f(id)) по таблице на 1 млн строк, функция mod(p, 7) * 2, среднее из 5 прогонов, секунды. Docker на ноутбуке, так что смотрим на порядок, а не на сотые.
вариант                  26ai   18c
обычная функция          4.83   2.11
pragma udf               1.10   1.53
with function            1.38   1.00
with function + udf      1.11   1.15
чистый SQL               0.59   0.42
Что видно: 🔹 pragma udf ускорила хранимую функцию: на 26ai в 4 раза, на 18c скромнее, в 1.4. И вывела ее на уровень with function. 🔹 udf внутри with function компилируется и работает, но выигрыша не дает. Тут Александр прав: «pragma udf не пашет с inline-функциями» - в смысле эффекта, не ошибки. 🔹 Цена udf: при вызове из PL/SQL функция чуть медленнее (цикл на 1 млн вызовов: 0.59 против 0.67 с на 26ai). Если функцию дергают в основном из PL/SQL - udf не нужна. 🔹 Чистый SQL быстрее лучшего варианта с функцией еще в 2 раза. Хозяйке на заметку 🤷🏻‍♂️ Итог Функция нужна в SQL и живет в схеме - ставьте pragma udf. Нужна одному запросу - with function. Можно без функции - пишите на SQL. А вы with function в проде используете или только в скриптах? Пишите в чатик 💬 С вами был Денис. Всем хорошей недели 🤝 #oracle #sql #plsql #оптимизация #performance #функции #фишки #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

Функция в SQL-запросе: можно, но есть нюансы 🔍 Коллеги, всем привет! 👋 На связи Денис. Можно ли вызвать свою PL/SQL-функцию
Функция в SQL-запросе: можно, но есть нюансы 🔍 Коллеги, всем привет! 👋 На связи Денис. Можно ли вызвать свою PL/SQL-функцию прямо в select? Можно. Но за этим «можно» прячется пачка ограничений и пара граблей по производительности. Давайте пройдёмся. Как это выглядит
create or replace function get_vat(p_sum number) return number is
begin
  return round(p_sum * 0.2, 2);
end;
/

select id, amount, get_vat(amount) vat
  from orders
 where get_vat(amount) > 100;
Функция может жить на уровне схемы или в пакете (тогда она должна быть в спецификации). Вызывать можно в select-листе, where, order by, group by, в values у insert и set у update. Ограничения 🔹 Из запроса (select) функция не может менять данные. Вставили insert внутрь - получите ORA-14551. 🔹 Никаких commit, rollback и DDL внутри - ORA-14552. 🔹 Если функция вызвана из insert/update/delete/merge, она не может читать или менять таблицу, которую этот оператор модифицирует - ORA-04091 (mutating table). 🔹 Параметры только IN. Типы параметров и результата - SQL-типы: никаких PL/SQL-записей и ассоциативных массивов. BOOLEAN в SQL появился только в 23ai. Да, ORA-14551 обходится через pragma autonomous_transaction. Но это костыль: отдельная транзакция на каждую строку. Хозяйке на заметку - так лучше не делать 🤷🏻‍♂️ Подводные камни 🔸 Переключение контекста SQL ↔️ PL/SQL на каждый вызов. На миллионе строк это уже заметно. 🔸 Количество вызовов не гарантировано. Oracle может вызвать функцию больше раз, чем строк в выборке. Не завязывайте логику на побочные эффекты. 🔸 Согласованность чтения. Если функция сама делает select, каждый такой запрос видит данные на момент своего вызова, а не на момент старта основного запроса. На долгом запросе можно получить «несогласованный» результат. Как ускорить
-- кэширование скалярного подзапроса
select id, (select get_vat(amount) from dual) vat
  from orders;
🔹 Скалярный подзапрос, как выше: Oracle кэширует результат для одинаковых входных значений, и функция вызывается реже. Как устроен этот кэш и почему на него нельзя слепо полагаться - тема для отдельного поста. Интересно? Ставьте 👍🏻 или пишите в комментах - напишу. 🔹 deterministic - подсказка, что для одних и тех же входных данных результат одинаковый. Нужна и для функционального индекса. 🔹 С 12c: pragma udf в функции или with function прямо в запросе - дешевле переключение контекста. 🔹 А лучше всего - если логику можно написать на чистом SQL, пишите на SQL. Итог Функции в SQL - нормальный инструмент. Но это не бесплатно и не безгранично. Прежде чем тащить функцию в запрос на миллионы строк, подумайте, нельзя ли обойтись без неё. А вы используете функции в запросах или бьёте за это по рукам на ревью? Пишите в чатик 💬 С вами был Денис. Всем быстрых запросов и без мутирующих таблиц 🤝 #oracle #sql #plsql #оптимизация #функции #фишки #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

DUAL: что под капотом у самой популярной таблицы Oracle Коллеги, всем привет! 👋 На связи Денис. Пожалуй, самая популярная та
DUAL: что под капотом у самой популярной таблицы Oracle Коллеги, всем привет! 👋 На связи Денис. Пожалуй, самая популярная таблица в мире Oracle. Каждый из нас писал select sysdate from dual сотни/тысячи раз. А вы когда-нибудь задумывались, что у неё под капотом? Давайте разберёмся. Что такое DUAL 🔹 Обычная таблица, владелец - SYS. Всем доступна через публичный синоним DUAL. 🔹 Одна колонка DUMMY типа VARCHAR2(1). 🔹 Одна строка со значением 'X'. Нужна она для одного: в Oracle SELECT без FROM был невозможен. А посчитать выражение, вызвать функцию или взять NEXTVAL из последовательности хочется. Вот и берём «таблицу-пустышку», где гарантированно одна строка. Кстати, почему DUAL, если строка одна? По известной байке, изначально в ней было две строки и её использовали, чтобы «удваивать» строки через декартово соединение. Потом строку оставили одну, а название прилипло 😄 Что происходит при выполнении Сравните два плана:
explain plan for select sysdate from dual;
-- FAST DUAL

explain plan for select * from dual;
-- TABLE ACCESS FULL | DUAL
🔸 Если колонку DUMMY не трогаем, оптимизатор (с 10g) использует операцию FAST DUAL: в таблицу вообще не ходит, логических чтений ноль. 🔸 Если выбираем * или DUMMY, Oracle честно читает таблицу полным сканом. Мелочь, но в цикле на миллион итераций уже не мелочь. Вывод простой: select * from dual без нужды не пишите. Oracle 23ai: DUAL больше не обязателен В 23ai FROM стал необязательным, наконец-то как в PostgreSQL:
select sysdate;
select 2 * 2;
select my_seq.nextval;
Старый вариант с from dual тоже работает, ничего переписывать не нужно. Но в новом коде можно смело писать короче. А вы уже пишете SELECT без FROM на 23ai или всё ещё сидите на 19c? Поделитесь в чатике 💬 С вами был Денис. Всем лёгких запросов и одной строки в DUAL 🤝 #oracle #sql #dual #oracle23ai #оптимизация #фишки #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE

🤝 Преемственность потоков: до этой идеи мы дошли не сразу Коллеги, всем привет! 👋 На связи Денис. Интересная ситуация случилась на 8м потоке курса по оптимизации. Мне написал Саша С., студент 7 потока, довольно активный участник, который и раньше что-то предлагал. И закинул идею:
а что, если кто-то из 7 потока придет и поприветствует 8? Такая преемственность. Даже кандидатуру предложил.
Я подумал: блин, а ведь классная идея. Как мы раньше не сообразили? Кстати, к слову сказать, это лишний раз показывает, что обратная связь нужна везде: в обучении, в управлении и так далее. С ее помощью можно улучшать свои процессы. Но философствовать на эту тему сейчас не буду. Так вот. Я попросил Олега, одного из лучших студентов 7 потока, прийти и сказать пару слов ребятам. Мне кажется, получилось довольно круто. Небольшой фрагмент можете посмотреть в видосе ⬆️ Мне на самом деле очень приятно, что в нашем сообществе есть люди, которые готовы помогать. Олег выделил свое время. Пусть это всего 5-10 минут, но прийти в пятницу вечером и сказать пару слов - это прям очень приятно. Что я для себя отметил 🔹 обратную связь от студентов стоит слушать, из нее рождаются хорошие идеи; 🔹 комьюнити - это люди, которые готовы помогать просто так, от души; 🔹 преемственность потоков, похоже, станет доброй традицией. Вот такой субботний щитпостинг получился, не обессудьте ) С вами был Денис. Всем позитивного настроя и хороших выходных 🤝 #обучение #комьюнити #обратная_связь #oracle #sql #оптимизация #Denis_Kivilev Канал Oracle Developer | Чатик 💬 Мини-курс Оптимизация: Быстрый старт 🚀 📱 YouTube 📱 ВКонтакте 📱 LinkedIn 📱Threads RUTUBE