✨ «Через десять минут на советский берег вышел странный человек без шапки и в одном сапоге.
Ни к кому не обращаясь, он громко сказал:
— Не надо оваций! Графа Монте-Кристо из меня не вышло.
Придётся переквалифицироваться в управдомы».
— Илья Ильф и Евгений Петров.
📢 Ну вот я и блогер.
А как любой порядочный блогер, я обязан написать правила и начать "клянить" деньги.
📜 Правила:
1️⃣ Все подписчики достойны уважения вне зависимости от пола, возраста, национальности, опыта и прочего.
Проявление неуважения к себе, к автору канала или другим подписчикам — недопустимо.
✅ Всё! Правила закончились.
Поймите меня правильно, я люблю, когда люди шутят, но считаю не допустимым, когда смеются над кем-то. Любое токсичное поведение порицается.
Споры приведствуются в корректной форме.
📣 Реклама:
Реклама всего, что касается темы канала и тем более на прочие темы — запредельно дорогая 💸 (надо же соблюдать правила!).
Реклама или ссылки на другие TG-каналы и ресурсы — допустимы и даже желательны, но только на принципе взаимности.
👉 Пишите в личку — обсудим.
🤝 Поддержка канала
Самая главная поддержка — делиться своими знаниями (задачами, фичами, техниками).
У меня много идей и материала, но рано или поздно они закончатся.
Вторая просьба — делитесь ссылкой на канал.
Если он нравится Вам, то понравится и Вашим друзьям.
Донаты.
Я жадный и сам донатю редко, поэтому просить или клянчить не буду.
Донатить стоит тогда и только тогда, когда уже не можете не донатить, и если и только, если канал Вас реально обогатил (во всех смыслах).
💡 Я хорошо зарабатываю, но даже один рубль — огромная мотивация.
Для меня важна не сумма, а то, что вы потратили своё личное время и отметили мою работу.
💳 Из России:
СБП: +79022628036 — мой телефон (Сбер, Т-банк)
Карта Сбера моей жены «Екатерина П.» — 4817 7603 0901 1850
🌍 Из-за бугра: Boosty
Крипта USDT:
TLJNKq7iiAq4DXmNXNbb3cpL3CX2SPjURk
Поиск по каналу:
#CodeArchitecture — посты об архитектуре кода.
#SQLOptimization — посты про оптимизацию SQL.
#RealInterviewTasks — реальные задачи с собеседований в разное время.
#Cases — случаи из практики.
#Tools — полезные ссылки.
#FriendlyResources — дружественные ресурсы.
#Reclama — то, что вряд ли будет.
Ни к кому не обращаясь, он громко сказал:
— Не надо оваций! Графа Монте-Кристо из меня не вышло.
Придётся переквалифицироваться в управдомы».
— Илья Ильф и Евгений Петров.
📢 Ну вот я и блогер.
А как любой порядочный блогер, я обязан написать правила и начать "клянить" деньги.
📜 Правила:
1️⃣ Все подписчики достойны уважения вне зависимости от пола, возраста, национальности, опыта и прочего.
Проявление неуважения к себе, к автору канала или другим подписчикам — недопустимо.
✅ Всё! Правила закончились.
Поймите меня правильно, я люблю, когда люди шутят, но считаю не допустимым, когда смеются над кем-то. Любое токсичное поведение порицается.
Споры приведствуются в корректной форме.
📣 Реклама:
Реклама всего, что касается темы канала и тем более на прочие темы — запредельно дорогая 💸 (надо же соблюдать правила!).
Реклама или ссылки на другие TG-каналы и ресурсы — допустимы и даже желательны, но только на принципе взаимности.
👉 Пишите в личку — обсудим.
🤝 Поддержка канала
Самая главная поддержка — делиться своими знаниями (задачами, фичами, техниками).
У меня много идей и материала, но рано или поздно они закончатся.
Вторая просьба — делитесь ссылкой на канал.
Если он нравится Вам, то понравится и Вашим друзьям.
Донаты.
Я жадный и сам донатю редко, поэтому просить или клянчить не буду.
Донатить стоит тогда и только тогда, когда уже не можете не донатить, и если и только, если канал Вас реально обогатил (во всех смыслах).
💡 Я хорошо зарабатываю, но даже один рубль — огромная мотивация.
Для меня важна не сумма, а то, что вы потратили своё личное время и отметили мою работу.
💳 Из России:
СБП: +79022628036 — мой телефон (Сбер, Т-банк)
Карта Сбера моей жены «Екатерина П.» — 4817 7603 0901 1850
🌍 Из-за бугра: Boosty
Крипта USDT:
TLJNKq7iiAq4DXmNXNbb3cpL3CX2SPjURk
Поиск по каналу:
#CodeArchitecture — посты об архитектуре кода.
#SQLOptimization — посты про оптимизацию SQL.
#RealInterviewTasks — реальные задачи с собеседований в разное время.
#Cases — случаи из практики.
#Tools — полезные ссылки.
#FriendlyResources — дружественные ресурсы.
#Reclama — то, что вряд ли будет.
🤡10👍2👎1🔥1🕊1
🎵 «Нагружать все больше нас
Стали почему-то,
Нынче в школе первый класс -
Вроде института.
Нам учитель задает
С иксами задачи,
Кандидат наук и тот -
Над задачей плачет.» — Алла Пугачева
Этим постом я начинаю (и, возможно, заканчиваю) серию «Оптимизация SQL-запросов».
📌 Сегодня вводная для новичков.
📊 Небольшой опрос среди 46 читателей канала показал:
11% — начинающие,
2% — испытывают трудности со сложными запросами.
Это вроде бы мало, чтобы разворачивать целый курс для новичков.
Но и много, чтобы игнорировать и не дать им «маршрут» обучения.
💡 Мои советы
0) SQL — навык, а не «родной язык»
Никто не рождается с пониманием SQL. Все проходят одинаковые стадии — от 🤯 недоумения до 🙌 принятия.
Главный путь — практика. В какой-то момент SQL станет для вас логичным и понятным. Дальше останется только накапливать трюки (и их не так уж много).
1) Настройте локальную среду ⚙️
Создайте место, где можно спокойно экспериментировать, создавать таблицы и объекты, не боясь «сломать прод».
Oracle и PostgreSQL отлично подходят для локальной установки.
Даже если что-то пошло не так — всегда можно всё переустановить.
2) Учитесь по книгам 📚
Для тех, кто не пишет SQL ежедневно, главная трудность — синтаксис. Он специфичный, и к нему нужно привыкнуть.
Но уже через 2–3 недели он станет естественным.
👉 Для старта советую:
«Секреты ORACLE SQL
» — но вообще любая понятная вам книга подойдёт.
3) Разбирайте сложные запросы по шагам 🪜
Многоуровневые вложенные SELECT’ы пугают новичков.
Для анализа используйте конструкцию WITH.
❌ Как обычно пишут:
✅ А вот так понятнее:
Так запрос становится прозрачным и разбирается по шагам.
(Но, конечно, не стоит в таком стиле писать всё подряд 😉).
4) Думайте множествами 🔢
Главный совет «на миллион». Даже опытные разработчики часто пишут запросы «по синтаксису». Но SQL проще понимать, если мыслить категориями множеств:
Таблицы = множества строк.
Строки = элементы множества.
Поля = атрибуты элементов.
👉 JOIN — это сопоставление элементов двух множеств по условию.
👉 WHERE — фильтр, выбирающий нужные элементы.
👉 GROUP BY — разбиение множества на подмножества.
Пример: есть таблица «Люди». Нужно соединить их в пары.
Берём множество людей,
накладываем фильтр для первой группы,
накладываем фильтр для второй группы,
соединяем по условию (например, одинаковый возраст, рост или период рождения).
Если приучить себя мыслить множествами, SQL станет в разы понятнее.
⚡️ И главное: лучше решить 1 сложную задачу, чем 1000 лёгких.
Всегда поднимайте планку — только так прокачка идёт быстрее.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
👍 Палец вверх — продолжаем и перейдем уже на уровень выше.
👎 Палец вниз — "Вода! Вода! Кругом вода!"
👨💻 Эти советы я бы дал самому себе из будущего.
А Вы, уважаемые мидлы, синьоры, эксперты — что бы посоветовали самим себе в прошлом? 👇
#️⃣ #SQLOptimization
Стали почему-то,
Нынче в школе первый класс -
Вроде института.
Нам учитель задает
С иксами задачи,
Кандидат наук и тот -
Над задачей плачет.» — Алла Пугачева
Этим постом я начинаю (и, возможно, заканчиваю) серию «Оптимизация SQL-запросов».
📌 Сегодня вводная для новичков.
📊 Небольшой опрос среди 46 читателей канала показал:
11% — начинающие,
2% — испытывают трудности со сложными запросами.
Это вроде бы мало, чтобы разворачивать целый курс для новичков.
Но и много, чтобы игнорировать и не дать им «маршрут» обучения.
💡 Мои советы
0) SQL — навык, а не «родной язык»
Никто не рождается с пониманием SQL. Все проходят одинаковые стадии — от 🤯 недоумения до 🙌 принятия.
Главный путь — практика. В какой-то момент SQL станет для вас логичным и понятным. Дальше останется только накапливать трюки (и их не так уж много).
1) Настройте локальную среду ⚙️
Создайте место, где можно спокойно экспериментировать, создавать таблицы и объекты, не боясь «сломать прод».
Oracle и PostgreSQL отлично подходят для локальной установки.
Даже если что-то пошло не так — всегда можно всё переустановить.
2) Учитесь по книгам 📚
Для тех, кто не пишет SQL ежедневно, главная трудность — синтаксис. Он специфичный, и к нему нужно привыкнуть.
Но уже через 2–3 недели он станет естественным.
👉 Для старта советую:
«Секреты ORACLE SQL
» — но вообще любая понятная вам книга подойдёт.
3) Разбирайте сложные запросы по шагам 🪜
Многоуровневые вложенные SELECT’ы пугают новичков.
Для анализа используйте конструкцию WITH.
❌ Как обычно пишут:
SELECT ...
FROM (
SELECT ...
FROM (
SELECT ...
FROM a
WHERE ...
) aa
WHERE ...
) aaa
WHERE ...
✅ А вот так понятнее:
WITH a_add AS (
SELECT ...
FROM a
WHERE ...
),
aa_add AS (
SELECT ...
FROM a_add
WHERE ...
),
aaa_add AS (
SELECT ...
FROM aa_add
WHERE ...
)
SELECT * FROM aaa_add;
Так запрос становится прозрачным и разбирается по шагам.
(Но, конечно, не стоит в таком стиле писать всё подряд 😉).
4) Думайте множествами 🔢
Главный совет «на миллион». Даже опытные разработчики часто пишут запросы «по синтаксису». Но SQL проще понимать, если мыслить категориями множеств:
Таблицы = множества строк.
Строки = элементы множества.
Поля = атрибуты элементов.
👉 JOIN — это сопоставление элементов двух множеств по условию.
👉 WHERE — фильтр, выбирающий нужные элементы.
👉 GROUP BY — разбиение множества на подмножества.
Пример: есть таблица «Люди». Нужно соединить их в пары.
Берём множество людей,
накладываем фильтр для первой группы,
накладываем фильтр для второй группы,
соединяем по условию (например, одинаковый возраст, рост или период рождения).
Если приучить себя мыслить множествами, SQL станет в разы понятнее.
⚡️ И главное: лучше решить 1 сложную задачу, чем 1000 лёгких.
Всегда поднимайте планку — только так прокачка идёт быстрее.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
👍 Палец вверх — продолжаем и перейдем уже на уровень выше.
👎 Палец вниз — "Вода! Вода! Кругом вода!"
👨💻 Эти советы я бы дал самому себе из будущего.
А Вы, уважаемые мидлы, синьоры, эксперты — что бы посоветовали самим себе в прошлом? 👇
#️⃣ #SQLOptimization
👍13👎1
🎵 «Хоть у неё ни черта не осталось!
У неё в кошельке три рубля,
Моя бабушка курит трубку,
Трубку курит бабушка моя.» — Гарик Сукачев
✨ Сегодня суббота — задачи оставим на потом.
Продолжаем рубрику «Полезные ресурсы».
🖥️ Тема: IDE — главный инструмент DB-разработчика
IDE для разработчика — как любимая машина 🚗.
Поменять или обменять сложно: привыкаешь к дизайну, к деталям, к особенностям. Да, у других «машин» тоже есть крутые примочки, но своя всё равно ближе.
🔧 Лично я использую:
PL/SQL Developer — основное IDE.
Toad for Oracle («Жаба») — для анализа, форматирования и мониторинга.
Oracle SQL Developer — ради одной особенной фичи.
🔍 Реaltime SQL Monitor в Oracle SQL Developer
Это инструмент для мониторинга «тяжёлых» SQL-запросов и PL/SQL-блоков в реальном времени.
По сути, это удобная визуализация на базе DBMS_SQLTUNE.REPORT_SQL_MONITOR и данных V$SQL_MONITOR / V$SQL_PLAN_MONITOR.
💡 Чем полезен:
Показывает, на каком шаге плана и в каком объёме Oracle расходует ресурсы.
Позволяет увидеть узкие места в плане выполнения.
Для новичков — отличная визуализация, сразу становится понятней, как именно исполняется запрос.
Для профи — быстрый первый шаг в диагностике «тяжёлых» запросов.
В 90% случаев этого достаточно, чтобы понять, что именно не так с планом.
💎 Поддержка канала⁉️
👨💻 А какими IDE пользуетесь Вы и за какие фичи их цените больше всего? Делитесь в комментах 👇
#️⃣ #SQLOptimization #Tools #Oracle
У неё в кошельке три рубля,
Моя бабушка курит трубку,
Трубку курит бабушка моя.» — Гарик Сукачев
✨ Сегодня суббота — задачи оставим на потом.
Продолжаем рубрику «Полезные ресурсы».
🖥️ Тема: IDE — главный инструмент DB-разработчика
IDE для разработчика — как любимая машина 🚗.
Поменять или обменять сложно: привыкаешь к дизайну, к деталям, к особенностям. Да, у других «машин» тоже есть крутые примочки, но своя всё равно ближе.
🔧 Лично я использую:
PL/SQL Developer — основное IDE.
Toad for Oracle («Жаба») — для анализа, форматирования и мониторинга.
Oracle SQL Developer — ради одной особенной фичи.
🔍 Реaltime SQL Monitor в Oracle SQL Developer
Это инструмент для мониторинга «тяжёлых» SQL-запросов и PL/SQL-блоков в реальном времени.
По сути, это удобная визуализация на базе DBMS_SQLTUNE.REPORT_SQL_MONITOR и данных V$SQL_MONITOR / V$SQL_PLAN_MONITOR.
💡 Чем полезен:
Показывает, на каком шаге плана и в каком объёме Oracle расходует ресурсы.
Позволяет увидеть узкие места в плане выполнения.
Для новичков — отличная визуализация, сразу становится понятней, как именно исполняется запрос.
Для профи — быстрый первый шаг в диагностике «тяжёлых» запросов.
В 90% случаев этого достаточно, чтобы понять, что именно не так с планом.
💎 Поддержка канала⁉️
👨💻 А какими IDE пользуетесь Вы и за какие фичи их цените больше всего? Делитесь в комментах 👇
#️⃣ #SQLOptimization #Tools #Oracle
👍4👎1
🔆«Тишина должна быть в библиотеке...» — Уральские пельмени
📌 Серия «Оптимизация SQL-запросов»
Этот пост — для тех, у кого возникают проблемы с пониманием плана запроса как такового.
Прежде чем разбирать конкретные планы запросов и их оптимизацию, давайте уясним, что такое план запроса.
Почему СУБД не всегда может гарантировать оптимальный план? И зачем нужны хинты (подсказки)?
📚 До изобретения баз данных человечество уже умело хранить и обрабатывать большие объёмы информации.
Где же? — спросите вы. Конечно же, в библиотеках!
Те, кто разрабатывал модели баз данных, имели опыт работы с библиотеками, и многие термины были заимствованы именно оттуда: индекс, ключевые слова и пр.
Аналогия с библиотекарем
Представим, что мы в самой большой библиотеке мира — Библиотеке Конгресса США в Вашингтоне.
Вы библиотекарь, и у вас нет никаких электронных устройств для помощи. Только:
📦 хранилище книг,
📑 индекс по названию,
✍️ индекс по автору,
🔑 индекс по ключевым словам.
Теперь рассмотрим примеры:
1️⃣ Самый простой запрос — «выдать все книги».
Подвозим вагоны и сгружаем в любом порядке.
Аналог: TABLE ACCESS FULL
2️⃣ Самый лёгкий запрос — найти книгу с известным местом хранения.
Просто идём и берём её, даже индекс не нужен.
Аналог: TABLE ACCESS BY ROWID
3️⃣ Запрос на книги автора — например, «Александр Сергеевич Пушкин».
Идём в индекс по авторам и получаем ссылки на его книги.
Аналог: INDEX RANGE SCAN (по диапазону).
Но что, если запрос будет таким:
автор пишет на индоевропейском языке,
фамилия начинается на «П»,
имя заканчивается на «р»,
жанр книги — сказка,
книга про рыбу.
Как быть?
Тут уже возникает несколько вариантов (планов) поиска:
искать авторов по первой букве,
искать книги по ключевым словам «сказка» и «рыба»,
пробовать разные комбинации,
или вовсе перебрать все книги.
👉 У вас уже 4 возможных плана запроса! И оптимизатору тоже приходится выбирать, какой путь будет наименее затратным.
⚙️ Оптимизатор:
каждому плану присваивает «стоимость»,
опирается на статистику (собранную заранее или из прошлых запросов),
иногда ошибается, если неправильно оценил путь поиска.
📊 Именно поэтому один и тот же запрос на разных базах может выполняться разными способами.
Если запрос сложный, нужно убедиться, что план не отличается от того, что был в тестовой среде.
Если отличается — применяем хинт или переписываем запрос, чтобы сделать его «понятнее» для оптимизатора.
💡 Главная мысль:
Чтобы понять, что такое план запроса, поставьте себя на место библиотекаря.
Продумайте, как бы вы искали данные вручную.
Именно так «думает» оптимизатор.
Дальше уже техника:
как подсказать оптимизатору, что он ошибается,
или как переписать запрос, чтобы сделать его проще для анализа.
✅ Итого:
План запроса — это последовательность шагов.
Набор приёмов ограничен и часто повторяется.
Главное — понимать логику поиска.
Надеюсь, с понятием «план запроса» мы разобрались.
Дальше будем разбирать конкретные шаги и реальные примеры.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
👍 Палец вверх — продолжаем!
👎 Палец вниз — скучно и неинтересно.
Если есть вопросы, задавайте.
Если Вы опытный и заметили ошибку и/или неточность пишите тоже.
Если же ни то, ни другое, то всё равно пишите. 👇
#️⃣ #SQLOptimization
📌 Серия «Оптимизация SQL-запросов»
Этот пост — для тех, у кого возникают проблемы с пониманием плана запроса как такового.
Прежде чем разбирать конкретные планы запросов и их оптимизацию, давайте уясним, что такое план запроса.
Почему СУБД не всегда может гарантировать оптимальный план? И зачем нужны хинты (подсказки)?
📚 До изобретения баз данных человечество уже умело хранить и обрабатывать большие объёмы информации.
Где же? — спросите вы. Конечно же, в библиотеках!
Те, кто разрабатывал модели баз данных, имели опыт работы с библиотеками, и многие термины были заимствованы именно оттуда: индекс, ключевые слова и пр.
Аналогия с библиотекарем
Представим, что мы в самой большой библиотеке мира — Библиотеке Конгресса США в Вашингтоне.
Вы библиотекарь, и у вас нет никаких электронных устройств для помощи. Только:
📦 хранилище книг,
📑 индекс по названию,
✍️ индекс по автору,
🔑 индекс по ключевым словам.
Теперь рассмотрим примеры:
1️⃣ Самый простой запрос — «выдать все книги».
Подвозим вагоны и сгружаем в любом порядке.
Аналог: TABLE ACCESS FULL
2️⃣ Самый лёгкий запрос — найти книгу с известным местом хранения.
Просто идём и берём её, даже индекс не нужен.
Аналог: TABLE ACCESS BY ROWID
3️⃣ Запрос на книги автора — например, «Александр Сергеевич Пушкин».
Идём в индекс по авторам и получаем ссылки на его книги.
Аналог: INDEX RANGE SCAN (по диапазону).
Но что, если запрос будет таким:
автор пишет на индоевропейском языке,
фамилия начинается на «П»,
имя заканчивается на «р»,
жанр книги — сказка,
книга про рыбу.
Как быть?
Тут уже возникает несколько вариантов (планов) поиска:
искать авторов по первой букве,
искать книги по ключевым словам «сказка» и «рыба»,
пробовать разные комбинации,
или вовсе перебрать все книги.
👉 У вас уже 4 возможных плана запроса! И оптимизатору тоже приходится выбирать, какой путь будет наименее затратным.
⚙️ Оптимизатор:
каждому плану присваивает «стоимость»,
опирается на статистику (собранную заранее или из прошлых запросов),
иногда ошибается, если неправильно оценил путь поиска.
📊 Именно поэтому один и тот же запрос на разных базах может выполняться разными способами.
Если запрос сложный, нужно убедиться, что план не отличается от того, что был в тестовой среде.
Если отличается — применяем хинт или переписываем запрос, чтобы сделать его «понятнее» для оптимизатора.
💡 Главная мысль:
Чтобы понять, что такое план запроса, поставьте себя на место библиотекаря.
Продумайте, как бы вы искали данные вручную.
Именно так «думает» оптимизатор.
Дальше уже техника:
как подсказать оптимизатору, что он ошибается,
или как переписать запрос, чтобы сделать его проще для анализа.
✅ Итого:
План запроса — это последовательность шагов.
Набор приёмов ограничен и часто повторяется.
Главное — понимать логику поиска.
Надеюсь, с понятием «план запроса» мы разобрались.
Дальше будем разбирать конкретные шаги и реальные примеры.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
👍 Палец вверх — продолжаем!
👎 Палец вниз — скучно и неинтересно.
Если есть вопросы, задавайте.
Если Вы опытный и заметили ошибку и/или неточность пишите тоже.
Если же ни то, ни другое, то всё равно пишите. 👇
#️⃣ #SQLOptimization
👍27
🎵 «А не спеть ли мне песню о любви
А не выдумать ли новый жанр
Попопсовей мотив и стихи
И всю жизнь получать гонорар» — Чиж
📚 Серия «Оптимизация SQL-запросов». Планы запросов (одна таблица): часть 1
Продолжим аналогию с библиотекой — ведь она интуитивно понятна.
Я создал таблицу BOOKS, спроектированную весьма плохо — и это намеренно.
Зачем? Чтобы показать, как работает оптимизатор и какие бывают планы выполнения запросов при обращении к одной таблице.
📖 Таблица денормализована, индексов слишком много, констрейнов нет — всё специально.
Цель — рассмотреть, как Oracle строит планы на одной таблице.
🔍 Что у нас есть
Мы — библиотекарь (оптимизатор).
Перед нами хранилище из миллиарда книг 📦
Созданы индексы:
Смотрите, в ORACLE много различных индексов и все они так или иначе применяются, но база это B-tree индексы (B - это не значит бинарный, а значит сбалансированный).
Абсолютное большинство индексов, которые Вы будете встречать, это нормальные B-tree индексы.
Экзотика полезна, но её надо изучать в контексте.
Заполним таблицу тестовыми данными:
#️⃣ #SQLOptimization #SQL #Oracle
А не выдумать ли новый жанр
Попопсовей мотив и стихи
И всю жизнь получать гонорар» — Чиж
📚 Серия «Оптимизация SQL-запросов». Планы запросов (одна таблица): часть 1
Продолжим аналогию с библиотекой — ведь она интуитивно понятна.
Я создал таблицу BOOKS, спроектированную весьма плохо — и это намеренно.
Зачем? Чтобы показать, как работает оптимизатор и какие бывают планы выполнения запросов при обращении к одной таблице.
CREATE TABLE books (
inventory_number NUMBER NOT NULL,
book_name VARCHAR2(500) NOT NULL,
language VARCHAR2(100) NOT NULL,
book_genre VARCHAR2(100) NOT NULL,
author_fullname VARCHAR2(100),
author_firstname VARCHAR2(100),
author_lastname VARCHAR2(100),
author_birthday DATE,
status_book VARCHAR2(50),
book_text CLOB NOT NULL
);
📖 Таблица денормализована, индексов слишком много, констрейнов нет — всё специально.
Цель — рассмотреть, как Oracle строит планы на одной таблице.
🔍 Что у нас есть
Мы — библиотекарь (оптимизатор).
Перед нами хранилище из миллиарда книг 📦
Созданы индексы:
CREATE UNIQUE INDEX books_u01 ON books (inventory_number); -- уникальный номер книги
CREATE INDEX books_i01 ON books (book_name); -- по названию
CREATE INDEX books_i02 ON books (language); -- по языку
CREATE INDEX books_i03 ON books (book_genre); -- по жанру
CREATE INDEX books_i04 ON books (book_name, book_genre, language); -- составной
CREATE INDEX books_i05 ON books (author_fullname); -- по ФИО автора
CREATE INDEX books_i06 ON books (author_lastname, author_firstname); -- по фамилии и имени
CREATE INDEX books_i07 ON books (author_birthday); -- по дате рождения
CREATE INDEX books_i08 ON books (status_book); -- по статусу (Available, Checked Out, Reserved, Repaired)
Смотрите, в ORACLE много различных индексов и все они так или иначе применяются, но база это B-tree индексы (B - это не значит бинарный, а значит сбалансированный).
Абсолютное большинство индексов, которые Вы будете встречать, это нормальные B-tree индексы.
Экзотика полезна, но её надо изучать в контексте.
Заполним таблицу тестовыми данными:
INSERT ALL
INTO books (inventory_number, book_name, language, book_genre, author_fullname, author_firstname, author_lastname, author_birthday, status_book, book_text)
VALUES (1, 'War and Peace', 'English', 'Historical Novel', 'Leo Tolstoy', 'Leo', 'Tolstoy',
TO_DATE('09-09-1828', 'DD-MM-YYYY'), 'Available',
'Long philosophical novel about war and human destiny.')
INTO books VALUES (2, 'Crime and Punishment', 'English', 'Psychological Fiction',
'Fyodor Dostoevsky', 'Fyodor', 'Dostoevsky',
TO_DATE('11-11-1821', 'DD-MM-YYYY'), 'Checked Out',
'Story of guilt, morality, and redemption.')
INTO books VALUES (3, 'Pride and Prejudice', 'English', 'Romance',
'Jane Austen', 'Jane', 'Austen',
TO_DATE('16-12-1775', 'DD-MM-YYYY'), 'Available',
'A classic story about manners, marriage, and social class.')
INTO books VALUES (4, 'Les Misérables', 'French', 'Historical Novel',
'Victor Hugo', 'Victor', 'Hugo',
TO_DATE('26-02-1802', 'DD-MM-YYYY'), 'Reserved',
'Epic tale of injustice, revolution, and redemption.')
INTO books VALUES (5, 'Faust', 'German', 'Tragedy',
'Johann Wolfgang von Goethe', 'Johann', 'Goethe',
TO_DATE('28-08-1749', 'DD-MM-YYYY'), 'Available',
'A scholar makes a pact with the devil in search of knowledge.')
INTO books VALUES (6, 'Don Quixote', 'Spanish', 'Adventure',
'Miguel de Cervantes', 'Miguel', 'Cervantes',
TO_DATE('29-09-1547', 'DD-MM-YYYY'), 'Checked Out',
'A nobleman loses his sanity and becomes a wandering knight.')
SELECT * FROM dual;
#️⃣ #SQLOptimization #SQL #Oracle
📚 Планы запросов (одна таблица): часть 2
1️⃣ Полное чтение таблицы
📄 План: TABLE ACCESS FULL — читаем всю таблицу от начала до конца.
Oracle оценивает объём, строки и стоимость выполнения (Cost = 3).
💡 Если нет статистики — оптимизатор делает dynamic sampling.
Чтобы помочь ему, собираем статистику:
После этого оценки становятся точнее - 6 записей против 1.
2️⃣ Покрытые запросы (INDEX FULL SCAN)
Нужно получить все инвентарные номера:
SELECT inventory_number FROM books;
📈 Oracle использует индекс BOOKS_U01, не трогая таблицу.
Такой запрос называют покрытым — все нужные данные уже есть в индексе.
⚠️ Поэтому избегайте SELECT * — всегда указывайте только нужные колонки!
3️⃣ Самый быстрый запрос 🚀
🪶 ROWID хранит физическое местоположение строки.
Oracle идёт прямо к нужному блоку, минуя индексы.
#️⃣ #SQLOptimization #SQL #Oracle
1️⃣ Полное чтение таблицы
SELECT * FROM books;
Plan Hash Value : 2688610195
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2563 | 3 | 00:00:01 |
| 1 | TABLE ACCESS FULL | BOOKS | 1 | 2563 | 3 | 00:00:01 |
---------------------------------------------------------------------
Notes
-----
- Dynamic sampling used for this statement ( level = 2 )
📄 План: TABLE ACCESS FULL — читаем всю таблицу от начала до конца.
Oracle оценивает объём, строки и стоимость выполнения (Cost = 3).
💡 Если нет статистики — оптимизатор делает dynamic sampling.
Чтобы помочь ему, собираем статистику:
BEGIN
DBMS_STATS.GATHER_TABLE_STATS (USER, 'BOOKS');
END;
После этого оценки становятся точнее - 6 записей против 1.
Plan Hash Value : 2688610195
---------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6 | 2928 | 3 | 00:00:01 |
| 1 | TABLE ACCESS FULL | BOOKS | 6 | 2928 | 3 | 00:00:01 |
---------------------------------------------------------------------
2️⃣ Покрытые запросы (INDEX FULL SCAN)
Нужно получить все инвентарные номера:
SELECT inventory_number FROM books;
📈 Oracle использует индекс BOOKS_U01, не трогая таблицу.
Такой запрос называют покрытым — все нужные данные уже есть в индексе.
⚠️ Поэтому избегайте SELECT * — всегда указывайте только нужные колонки!
3️⃣ Самый быстрый запрос 🚀
SELECT * FROM books WHERE rowid = ...;
Plan Hash Value : 2667597667
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 244 | 1 | 00:00:01 |
| 1 | TABLE ACCESS BY USER ROWID | BOOKS | 1 | 244 | 1 | 00:00:01 |
------------------------------------------------------------------------------
🪶 ROWID хранит физическое местоположение строки.
Oracle идёт прямо к нужному блоку, минуя индексы.
#️⃣ #SQLOptimization #SQL #Oracle
❤2
📚 Планы запросов (одна таблица): часть 3
4️⃣ Доступ по уникальному индексу
📘 План показывает:
INDEX UNIQUE SCAN по BOOKS_U01
затем TABLE ACCESS BY INDEX ROWID
То есть сначала Oracle ищет ROWID в индексе, потом берёт саму книгу.
5️⃣ Доступ по неуникальному индексу
🧭 План: INDEX RANGE SCAN
Даже если значение одно, Oracle предполагает, что книг с таким названием может быть несколько.
💬 Разница между INDEX UNIQUE SCAN и INDEX RANGE SCAN колоссальная по скорости.
Если можете писать точные фильтры по уникальному индексу — делайте это!
Пример:
Результат этих запросов идентичен, но план запросов по скорости отличается "как небо и земля".
В одном случае будет INDEX UNIQUE SCAN, в другом INDEX RANGE SCAN.
Если используется INDEX UNIQUE SCAN — это значительно быстрее, чем диапазонное условие.
6️⃣ Доступ по неполному индексу
SELECT * FROM books
WHERE language = 'Russian' AND book_genre = 'Novel';
📊 План: INDEX SKIP SCAN
Oracle использует составной индекс (book_name, book_genre, language),
пропуская первое поле (book_name).
⚠️ Если видите в плане INDEX SKIP SCAN, это сигнал проверить:
Правильные ли созданы индексы на таблице?
Не в том ли порядке созданы поля составного индекса?
7⃣ Напоследок: есть ещё INDEX FAST FULL SCAN,
но о нём как-нибудь в другой раз. Это уже разбор не для новичков.
#️⃣ #SQLOptimization #SQL #Oracle
4️⃣ Доступ по уникальному индексу
SELECT * FROM books WHERE inventory_number = 1;
Plan Hash Value : 6300684
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 244 | 1 | 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | BOOKS | 1 | 244 | 1 | 00:00:01 |
| * 2 | INDEX UNIQUE SCAN | BOOKS_U01 | 1 | | 0 | 00:00:01 |
------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("INVENTORY_NUMBER"=1)
📘 План показывает:
INDEX UNIQUE SCAN по BOOKS_U01
затем TABLE ACCESS BY INDEX ROWID
То есть сначала Oracle ищет ROWID в индексе, потом берёт саму книгу.
5️⃣ Доступ по неуникальному индексу
SELECT * FROM books WHERE book_name = 'War and Peace';
Plan Hash Value : 480843298
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 244 | 2 | 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED | BOOKS | 1 | 244 | 2 | 00:00:01 |
| * 2 | INDEX RANGE SCAN | BOOKS_I01 | 1 | | 1 | 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("BOOK_NAME"='War and Peace')
🧭 План: INDEX RANGE SCAN
Даже если значение одно, Oracle предполагает, что книг с таким названием может быть несколько.
💬 Разница между INDEX UNIQUE SCAN и INDEX RANGE SCAN колоссальная по скорости.
Если можете писать точные фильтры по уникальному индексу — делайте это!
Пример:
SELECT * FROM books
WHERE inventory_number IN (1, 2, 3);
SELECT * FROM books
WHERE inventory_number >= 1 AND inventory_number <= 3;
Результат этих запросов идентичен, но план запросов по скорости отличается "как небо и земля".
В одном случае будет INDEX UNIQUE SCAN, в другом INDEX RANGE SCAN.
Если используется INDEX UNIQUE SCAN — это значительно быстрее, чем диапазонное условие.
6️⃣ Доступ по неполному индексу
SELECT * FROM books
WHERE language = 'Russian' AND book_genre = 'Novel';
Plan Hash Value : 1417276700
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 244 | 2 | 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED | BOOKS | 1 | 244 | 2 | 00:00:01 |
| * 2 | INDEX SKIP SCAN | BOOKS_I04 | 1 | | 1 | 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 2 - access("BOOK_GENRE"='Novel' AND "LANGUAGE"='Russian')
* 2 - filter("LANGUAGE"='Russian' AND "BOOK_GENRE"='Novel')
📊 План: INDEX SKIP SCAN
Oracle использует составной индекс (book_name, book_genre, language),
пропуская первое поле (book_name).
⚠️ Если видите в плане INDEX SKIP SCAN, это сигнал проверить:
Правильные ли созданы индексы на таблице?
Не в том ли порядке созданы поля составного индекса?
7⃣ Напоследок: есть ещё INDEX FAST FULL SCAN,
но о нём как-нибудь в другой раз. Это уже разбор не для новичков.
#️⃣ #SQLOptimization #SQL #Oracle
👍2
📚 Планы запросов (одна таблица): часть 4.
📚 Сейчас же несколько советов и логику рассуждения первичной оптимизации.
🔍 Пример 1. Поиск по шаблону
Если у Вас нет нужного конкретного функционального индекса и Вам надо искать данные по шаблону в строке, используете LIKE.
🔧 Пример 2. Выборка по нескольким условиям
Часто ли книги отправляют на реставрацию? Ну, не часто! Из миллиарда книг, может быть 50 или 500, но не больше.
Если же Вам требуется найти все книги, которые на русском языке и отправлены на реставрацию.
Оптимизатор может использовать несколько планов.
1) Выбрать все книги на русском, а потом среди них искать, тех кто на реставрации.
2) Выбрать все книги на реставрации, а потом уже на русском.
Очевидно, что 2 вариант проще. Но бывают ситуации, что оптимизатор принимает неочевидное решение.
Смотрите, здесь оптимизатор ошибся! Он доступ по индексу использует язык access("B"."LANGUAGE"='Russian'), а потом уже фильтрует по статусу filter("B"."STATUS_BOOK"='Repaired')
Задача разработчика это прочитать и исправить. Например, явно указать какой индекс использовать для поиска нужных записей.
Для этого можно использовать хинт INDEX.
Теперь мы получим, то что и надо - индекс access("B"."STATUS_BOOK"='Repaired') и фильтр filter("B"."LANGUAGE"='Russian').
Оптимизатор сделал оценку плана и ошибся, в этом случае, оценка оптимизатора невалидна. Её можно игнорировать.
🎯 Вывод:
Проверяйте, как именно Oracle строит план.
Если оптимизатор ошибается — докажите это тестом, замером времени и статистикой.
Оптимизация — это не догадки, а проверка гипотез 💪.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
👍 Палец вверх — если мои посты Вам интересны
👎 Палец вниз — если вяло или пошло.
💬 Пишите - для меня весьма важна обратная связь! 👇.
#️⃣ #SQLOptimization #SQL #Oracle
📚 Сейчас же несколько советов и логику рассуждения первичной оптимизации.
🔍 Пример 1. Поиск по шаблону
Если у Вас нет нужного конкретного функционального индекса и Вам надо искать данные по шаблону в строке, используете LIKE.
SELECT * FROM books
WHERE author_lastname LIKE '%Tol';
🔧 Пример 2. Выборка по нескольким условиям
Часто ли книги отправляют на реставрацию? Ну, не часто! Из миллиарда книг, может быть 50 или 500, но не больше.
Если же Вам требуется найти все книги, которые на русском языке и отправлены на реставрацию.
Оптимизатор может использовать несколько планов.
1) Выбрать все книги на русском, а потом среди них искать, тех кто на реставрации.
2) Выбрать все книги на реставрации, а потом уже на русском.
Очевидно, что 2 вариант проще. Но бывают ситуации, что оптимизатор принимает неочевидное решение.
SELECT * FROM books b WHERE b.language = 'Russian' AND b.status_book = 'Repaired'
Plan Hash Value : 4070381338
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2563 | 1 | 00:00:01 |
| * 1 | TABLE ACCESS BY INDEX ROWID BATCHED | BOOKS | 1 | 2563 | 1 | 00:00:01 |
| * 2 | INDEX RANGE SCAN | BOOKS_I02 | 2 | | 1 | 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
------------------------------------------
* 1 - filter("B"."STATUS_BOOK"='Repaired')
* 2 - access("B"."LANGUAGE"='Russian')
Смотрите, здесь оптимизатор ошибся! Он доступ по индексу использует язык access("B"."LANGUAGE"='Russian'), а потом уже фильтрует по статусу filter("B"."STATUS_BOOK"='Repaired')
Задача разработчика это прочитать и исправить. Например, явно указать какой индекс использовать для поиска нужных записей.
Для этого можно использовать хинт INDEX.
SELECT /*+ INDEX (b BOOKS_I08)*/ * FROM books b WHERE b.language = 'Russian' AND b.status_book = 'Repaired'
Теперь мы получим, то что и надо - индекс access("B"."STATUS_BOOK"='Repaired') и фильтр filter("B"."LANGUAGE"='Russian').
Оптимизатор сделал оценку плана и ошибся, в этом случае, оценка оптимизатора невалидна. Её можно игнорировать.
Predicate Information (identified by operation id):
------------------------------------------
* 1 - filter("B"."LANGUAGE"='Russian')
* 2 - access("B"."STATUS_BOOK"='Repaired')
🎯 Вывод:
Проверяйте, как именно Oracle строит план.
Если оптимизатор ошибается — докажите это тестом, замером времени и статистикой.
Оптимизация — это не догадки, а проверка гипотез 💪.
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница
💎 Поддержка канала⁉️
👍 Палец вверх — если мои посты Вам интересны
👎 Палец вниз — если вяло или пошло.
💬 Пишите - для меня весьма важна обратная связь! 👇.
#️⃣ #SQLOptimization #SQL #Oracle
👍8🔥2👎1
🎵 «Разлук так много на земле и разных судеб,
Надежду дарит на заре паромщик людям.
То берег левый нужен им, то берег правый…
Влюблённых много — он один у переправы.»
— Михаил Танич
🚀 Серия: Оптимизация SQL-запросов
📘 Тема: Чтение плана запроса при соединении двух и более таблиц.
Для примера возьмём две таблицы — A и B.
И самый простой запрос:
Поставьте себя на место оптимизатора.
Как бы вы соединили таблицы?
Наверное, взяли бы первую строку из таблицы A, посмотрели её id,
а потом по этому id нашли бы соответствующие строки в B.
То есть прошли бы циклом по A, а для каждой строки делали бы поиск в B.
Верно? 💡
Отлично! Именно этот способ называется Nested Loop Join.
📖 Подробности можно прочитать в официальной документации Oracle: docs.oracle.com — Joins
Но подождите…
А если в A — миллиард записей, а в B — всего две?
Проходить миллиард раз и искать в B — нерационально.
Очевидное решение — просто поменять местами таблицы.
И вот тут начинается самое интересное —
🧩 Оптимизатор должен выбрать порядок соединения таблиц.
Если таблиц много (A, B, C, D, …, Z),
то количество вариантов соединения растёт факториально:
10! = 3 628 800 вариантов при соединении 10 таблиц
Даже Терминатор T-800 устанет столько считать!
Oracle действительно оценивает разные варианты,
но чтобы не сойти с ума, ставит внутренние ограничения
(точную границу в документации не раскрывают) на разбор всех вариантов. Тут хинты требуются точно.
💡 Как подсказать оптимизатору, в каком порядке соединять таблицы?
Хинт LEADING — явно указываем порядок:
Хинт ORDERED — порядок берётся прямо из текста запроса:
📚 Сегодня мы обсудили:
1️⃣ Тип соединения таблиц — Nested Loop
2️⃣ Порядок соединения и способы его изменить (LEADING, ORDERED)
А что делать, если в A — 1 млрд строк, а в B — 2 млрд?
Даже уникальный индекс не спасёт — цикл будет слишком долгим.
В следующем посте разберём другие типы соединений:
HASH JOIN и MERGE JOIN.
💎 Поддержка канала
👍 Палец вверх — продолжаем разбор SQL-планов
👎 Палец вниз — зачем писать очевидные вещи?
💬 Если заметили неточность — напишите в комментариях 👇
#️⃣ #SQLOptimization #SQL #Oracle
Надежду дарит на заре паромщик людям.
То берег левый нужен им, то берег правый…
Влюблённых много — он один у переправы.»
— Михаил Танич
🚀 Серия: Оптимизация SQL-запросов
📘 Тема: Чтение плана запроса при соединении двух и более таблиц.
Для примера возьмём две таблицы — A и B.
И самый простой запрос:
SELECT a.*, b.*
FROM a
INNER JOIN b ON (a.id = b.id);
Поставьте себя на место оптимизатора.
Как бы вы соединили таблицы?
Наверное, взяли бы первую строку из таблицы A, посмотрели её id,
а потом по этому id нашли бы соответствующие строки в B.
То есть прошли бы циклом по A, а для каждой строки делали бы поиск в B.
Верно? 💡
Отлично! Именно этот способ называется Nested Loop Join.
📖 Подробности можно прочитать в официальной документации Oracle: docs.oracle.com — Joins
Но подождите…
А если в A — миллиард записей, а в B — всего две?
Проходить миллиард раз и искать в B — нерационально.
Очевидное решение — просто поменять местами таблицы.
И вот тут начинается самое интересное —
🧩 Оптимизатор должен выбрать порядок соединения таблиц.
Если таблиц много (A, B, C, D, …, Z),
то количество вариантов соединения растёт факториально:
10! = 3 628 800 вариантов при соединении 10 таблиц
Даже Терминатор T-800 устанет столько считать!
Oracle действительно оценивает разные варианты,
но чтобы не сойти с ума, ставит внутренние ограничения
(точную границу в документации не раскрывают) на разбор всех вариантов. Тут хинты требуются точно.
💡 Как подсказать оптимизатору, в каком порядке соединять таблицы?
Хинт LEADING — явно указываем порядок:
SELECT /*+ LEADING(z y x ... a) */
a.*, b.*
FROM a
JOIN b ON (a.id = b.id)
JOIN c ON (a.id = c.id)
...
JOIN z ON (y.id = z.id);
Хинт ORDERED — порядок берётся прямо из текста запроса:
SELECT /*+ ORDERED */
...
FROM a
JOIN b
JOIN c ...
📚 Сегодня мы обсудили:
1️⃣ Тип соединения таблиц — Nested Loop
2️⃣ Порядок соединения и способы его изменить (LEADING, ORDERED)
А что делать, если в A — 1 млрд строк, а в B — 2 млрд?
Даже уникальный индекс не спасёт — цикл будет слишком долгим.
В следующем посте разберём другие типы соединений:
HASH JOIN и MERGE JOIN.
💎 Поддержка канала
👍 Палец вверх — продолжаем разбор SQL-планов
👎 Палец вниз — зачем писать очевидные вещи?
💬 Если заметили неточность — напишите в комментариях 👇
#️⃣ #SQLOptimization #SQL #Oracle
👍16👎1
🎵 «Не кочегары мы, не плотники,
Но сожалений горьких нет, как нет!
А мы монтажники-высотники, да,
И с высоты вам шлём привет!» — Владимир Котов
🚀 Серия: Оптимизация SQL-запросов. HASH-соединения таблиц
В прошлый раз мы разобрали, как работает NESTED LOOP JOIN — пошаговое соединение,
когда для каждой строки одной таблицы ищутся совпадения в другой.
Но что, если у нас миллиарды строк с обеих сторон?
Возьмём две таблицы: A и B и в каждой — по миллиарду записей.
И самый простой запрос:
В таком случае оптимизатор выбирает другие механизмы — HASH JOIN или MERGE JOIN.
📘 HASH JOIN — тяжёлая артиллерия оптимизатора
Перед тем как соединять таблицы, Oracle считывает данные и строит hash-таблицы,
которые состоят из результата hash-функции и данных таблицы.
Как именно оптимизатор строит hash-таблицу?
Он делает это по условию соединения — в данном случае по a.id и b.id.
Точную функцию, которую использует Oracle, мы не знаем,
но, вероятно, она похожа на:
Если соединение идёт по нескольким полям:
то hash-функция будет примерно такой:
где '/' — это разделитель, помогающий избежать коллизий и проблем с NULL.
Главная идея метода — минимум обращений к диску:
данные считываются один раз (или частями, если они слишком велики).
Пример плана выполнения:
Особенности HASH JOIN
1️⃣ Соединение только по равенству.
Если условие будет вроде
то построить hash-таблицы невозможно — хэш не работает с неравенствами.
2️⃣ Порядок таблиц имеет значение.
Первая таблица (build input) должна быть меньше по размеру.
Если A — 1 млрд записей, а B — 100 млн, правильный порядок: B => A, а не наоборот.
3️⃣ Использование TEMP.
На малых объёмах hash-таблица хранится в памяти,
но при больших — оптимизатор выгружает промежуточные данные в табличное пространство TEMP.
Если там не хватает места — возможны сильные тормоза.
4️⃣ Коллизии.
Иногда hash-функция вычисляет одинаковые значения для разных строк.
Oracle умеет такие конфликты корректно обрабатывать.
5️⃣ Для глубокого понимания.
Подробный разбор алгоритма есть в книге Джонатана Льюиса
«Основы стоимостной оптимизации» — чтение полезное, но не из лёгких. Я новичкам читать не рекомендую - отобьёт всякое желание.
В итоге оптимизатор сравнивает:
что выгоднее — прочитать всё и построить hash-таблицу,
или пройтись циклом (Nested Loop).
Из практики — в сложных запросах оптимизатор может ошибаться и выбрать не самый удачный вариант.
Поэтому можно явно подсказать порядок и тип соединения:
или так:
💡 Можно строить гипотезы, какой метод лучше,
но любые гипотезы требуют проверки и замеров.
Чтобы не "перекормить" новичков информацией,
метод MERGE JOIN разберём в следующем посте.
⚠️ Хотите проверить скрипты, но нет базы под рукой — используйте онлайн-песочницу.
💎 Поддержка канала⁉️
👍 Палец вверх — машина: "Полный ход!"
👎 Палец вниз — Стоп машина!
👇 Если заметили неточность — напишите!
А если есть, что добавить — тем более.
#️⃣ #SQLOptimization #SQL #Oracle
Но сожалений горьких нет, как нет!
А мы монтажники-высотники, да,
И с высоты вам шлём привет!» — Владимир Котов
🚀 Серия: Оптимизация SQL-запросов. HASH-соединения таблиц
В прошлый раз мы разобрали, как работает NESTED LOOP JOIN — пошаговое соединение,
когда для каждой строки одной таблицы ищутся совпадения в другой.
Но что, если у нас миллиарды строк с обеих сторон?
Возьмём две таблицы: A и B и в каждой — по миллиарду записей.
И самый простой запрос:
SELECT a.*, b.*
FROM a
JOIN b ON (a.id = b.id);
В таком случае оптимизатор выбирает другие механизмы — HASH JOIN или MERGE JOIN.
📘 HASH JOIN — тяжёлая артиллерия оптимизатора
Перед тем как соединять таблицы, Oracle считывает данные и строит hash-таблицы,
которые состоят из результата hash-функции и данных таблицы.
Как именно оптимизатор строит hash-таблицу?
Он делает это по условию соединения — в данном случае по a.id и b.id.
Точную функцию, которую использует Oracle, мы не знаем,
но, вероятно, она похожа на:
STANDARD_HASH(a.id)
Если соединение идёт по нескольким полям:
a.id = b.id AND a.something = b.something
то hash-функция будет примерно такой:
STANDARD_HASH(a.id || '/' || a.something)
где '/' — это разделитель, помогающий избежать коллизий и проблем с NULL.
Главная идея метода — минимум обращений к диску:
данные считываются один раз (или частями, если они слишком велики).
Пример плана выполнения:
| 0 | SELECT STATEMENT |
| * 1 | HASH JOIN |
| 2 | TABLE ACCESS FULL | A
| 3 | TABLE ACCESS FULL | B
Особенности HASH JOIN
1️⃣ Соединение только по равенству.
Если условие будет вроде
a.id > b.id - 1 AND a.id < b.id + 1
то построить hash-таблицы невозможно — хэш не работает с неравенствами.
2️⃣ Порядок таблиц имеет значение.
Первая таблица (build input) должна быть меньше по размеру.
Если A — 1 млрд записей, а B — 100 млн, правильный порядок: B => A, а не наоборот.
3️⃣ Использование TEMP.
На малых объёмах hash-таблица хранится в памяти,
но при больших — оптимизатор выгружает промежуточные данные в табличное пространство TEMP.
Если там не хватает места — возможны сильные тормоза.
4️⃣ Коллизии.
Иногда hash-функция вычисляет одинаковые значения для разных строк.
Oracle умеет такие конфликты корректно обрабатывать.
5️⃣ Для глубокого понимания.
Подробный разбор алгоритма есть в книге Джонатана Льюиса
«Основы стоимостной оптимизации» — чтение полезное, но не из лёгких. Я новичкам читать не рекомендую - отобьёт всякое желание.
В итоге оптимизатор сравнивает:
что выгоднее — прочитать всё и построить hash-таблицу,
или пройтись циклом (Nested Loop).
Из практики — в сложных запросах оптимизатор может ошибаться и выбрать не самый удачный вариант.
Поэтому можно явно подсказать порядок и тип соединения:
SELECT /*+ LEADING(b a) USE_HASH(b a) */
a.*, b.*
FROM a
JOIN b ON (a.id = b.id);
или так:
SELECT /*+ ORDERED USE_NL(a b) */
a.*, b.*
FROM a
JOIN b ON (a.id = b.id);
💡 Можно строить гипотезы, какой метод лучше,
но любые гипотезы требуют проверки и замеров.
Чтобы не "перекормить" новичков информацией,
метод MERGE JOIN разберём в следующем посте.
⚠️ Хотите проверить скрипты, но нет базы под рукой — используйте онлайн-песочницу.
💎 Поддержка канала⁉️
👍 Палец вверх — машина: "Полный ход!"
👎 Палец вниз — Стоп машина!
👇 Если заметили неточность — напишите!
А если есть, что добавить — тем более.
#️⃣ #SQLOptimization #SQL #Oracle
👍13❤1👎1🔥1
🎵 «Капелькой дождя упал
На твои ладони этот день.
Ты меня повсюду ждал
И бродил за мною словно тень.
Прячешь ты глаза словно
Робостью навек, скован.
Сделай первый шаг,
Будь же посмелей со мной» — Леонид Агутин
🚀 Серия: Оптимизация SQL-запросов. MERGE-соединения таблиц
В прошлый раз мы говорили о HASH JOIN — тяжёлой артиллерии оптимизатора, которая идеально подходит для гигантских таблиц и равных соединений.
Сегодня разберём MERGE JOIN — метод, который сочетает в себе простоту и эффективность, но требует одного важного условия: оба набора данных должны быть отсортированы по ключу соединения.
Вернее, если набор данных отсортирован, такой вид соединения весьма эффективен. Если же нет — Oracle потратит значительные ресурсы на сортировку. ⚡
Итак, по порядку:
Если у нас есть таблицы A и B, и обе отсортированы по id,
Oracle считывает данные из двух наборов и идёт по ним “в ногу”, сравнивая строки и сдвигаясь вперёд, пока не найдёт совпадение.
Сначала он идёт по первому набору, пока не найдёт расхождение со вторым. Потом перескакивает и идёт по второму набору до расхождения, и так до конца одного из списков.
Понятней будет на примере:
Есть набор данных, по которым идёт соединение:
Два списка можно представить так:
Комментарий:
ptrA и ptrB — указатели на элементы набора A и B.
Oracle идёт по общему отсортированному потоку, сравнивая стрелки:
если A < B → двигаем A ⬇️
если A > B → двигаем B ⬇️
если A = B → найдено совпадение ✅ → двигаем обе стрелки ⬇️⬇️
Результат: пары (3,3), (5,5), (6,6)
Вот как MERGE JOIN выглядит в плане выполнения:
📊 Получается потоковое соединение без случайных обращений к данным.
🔥 Когда MERGE JOIN — лучший выбор
1️⃣ Оба набора данных уже отсортированы (например, при индексе по ключу соединения).
2️⃣ Условие соединения — равенство или диапазон (в отличие от HASH JOIN).
3️⃣ Таблицы слишком большие для полной hash-таблицы в памяти.
4️⃣ Нужно соединять результат, который уже упорядочен — например, после ORDER BY или GROUP BY.
⚠️ Особенности и подводные камни
1️⃣ Сортировка — не бесплатная.
Если данные не отсортированы, Oracle создаст временные сортировки (SORT JOIN).
Это может привести к активному использованию TEMP и росту времени выполнения.
2️⃣ MERGE JOIN может быть быстрее HASH JOIN,
если сортировки уже есть (например, индексы по id) или если соединяются диапазоны.
3️⃣ Порядок таблиц важен.
Как и в других соединениях, можно управлять порядком через хинт LEADING.
Я не нашел, прямого упоминания, что порядок имеет значение. Но и обратного утверждения в доках нет. Возможно, что разница есть, но она не значительная.
В сложных запросах, особенно с временными данными, оптимизатору сложно разобраться.
Он может выбрать далеко не самый оптимальный вариант.
Его можно понять — "Все мы люди и все ошибаемся"
Поэтому можно явно подсказать порядок и тип соединения:
или так:
💡 Если вы считаете, что оптимизатор ошибается — смело давайте ему советы. Только ваши гипотезы требуют проверки и доказательства. Нельзя вмешиваться без чёткой уверенности в своих действиях.
⚠️ Хотите проверить скрипты, но нет базы под рукой — используйте онлайн-песочницу.
💎 Поддержка канала⁉️
👍 Палец вверх — есть контакт
👎 Палец вниз — нет контакта
👇 Каждый комментарий от профи — драгоценность для начинающих.
Каждый вопрос от новичка — проверка знаний для профи.
И то, и другое приветствуется.
#️⃣ #SQLOptimization #SQL #Oracle
На твои ладони этот день.
Ты меня повсюду ждал
И бродил за мною словно тень.
Прячешь ты глаза словно
Робостью навек, скован.
Сделай первый шаг,
Будь же посмелей со мной» — Леонид Агутин
🚀 Серия: Оптимизация SQL-запросов. MERGE-соединения таблиц
В прошлый раз мы говорили о HASH JOIN — тяжёлой артиллерии оптимизатора, которая идеально подходит для гигантских таблиц и равных соединений.
Сегодня разберём MERGE JOIN — метод, который сочетает в себе простоту и эффективность, но требует одного важного условия: оба набора данных должны быть отсортированы по ключу соединения.
Вернее, если набор данных отсортирован, такой вид соединения весьма эффективен. Если же нет — Oracle потратит значительные ресурсы на сортировку. ⚡
Итак, по порядку:
Если у нас есть таблицы A и B, и обе отсортированы по id,
Oracle считывает данные из двух наборов и идёт по ним “в ногу”, сравнивая строки и сдвигаясь вперёд, пока не найдёт совпадение.
Сначала он идёт по первому набору, пока не найдёт расхождение со вторым. Потом перескакивает и идёт по второму набору до расхождения, и так до конца одного из списков.
Понятней будет на примере:
SELECT a.*, b.*
FROM a
JOIN b ON (a.id = b.id);
Есть набор данных, по которым идёт соединение:
A = 1, 2, 3, 5, 6, 8, 9
B = 3, 4, 5, 6
Два списка можно представить так:
A: 1 2 3 5 6 8 9
B: 3 4 5 6
↑ ↑
ptrA ptrB
Комментарий:
ptrA и ptrB — указатели на элементы набора A и B.
Oracle идёт по общему отсортированному потоку, сравнивая стрелки:
если A < B → двигаем A ⬇️
если A > B → двигаем B ⬇️
если A = B → найдено совпадение ✅ → двигаем обе стрелки ⬇️⬇️
Результат: пары (3,3), (5,5), (6,6)
Вот как MERGE JOIN выглядит в плане выполнения:
| 0 | SELECT STATEMENT |
| * 1 | MERGE JOIN |
| 2 | SORT JOIN | A
| 3 | SORT JOIN | B
📊 Получается потоковое соединение без случайных обращений к данным.
🔥 Когда MERGE JOIN — лучший выбор
1️⃣ Оба набора данных уже отсортированы (например, при индексе по ключу соединения).
2️⃣ Условие соединения — равенство или диапазон (в отличие от HASH JOIN).
3️⃣ Таблицы слишком большие для полной hash-таблицы в памяти.
4️⃣ Нужно соединять результат, который уже упорядочен — например, после ORDER BY или GROUP BY.
⚠️ Особенности и подводные камни
1️⃣ Сортировка — не бесплатная.
Если данные не отсортированы, Oracle создаст временные сортировки (SORT JOIN).
Это может привести к активному использованию TEMP и росту времени выполнения.
2️⃣ MERGE JOIN может быть быстрее HASH JOIN,
если сортировки уже есть (например, индексы по id) или если соединяются диапазоны.
3️⃣ Порядок таблиц важен.
Как и в других соединениях, можно управлять порядком через хинт LEADING.
Я не нашел, прямого упоминания, что порядок имеет значение. Но и обратного утверждения в доках нет. Возможно, что разница есть, но она не значительная.
В сложных запросах, особенно с временными данными, оптимизатору сложно разобраться.
Он может выбрать далеко не самый оптимальный вариант.
Его можно понять — "Все мы люди и все ошибаемся"
Поэтому можно явно подсказать порядок и тип соединения:
SELECT /*+ LEADING(b a) USE_MERGE(b a) */
a.*, b.*
FROM a
JOIN b ON (a.id = b.id);
или так:
SELECT /*+ ORDERED USE_HASH(a b) */
a.*, b.*
FROM a
JOIN b ON (a.id = b.id);
💡 Если вы считаете, что оптимизатор ошибается — смело давайте ему советы. Только ваши гипотезы требуют проверки и доказательства. Нельзя вмешиваться без чёткой уверенности в своих действиях.
⚠️ Хотите проверить скрипты, но нет базы под рукой — используйте онлайн-песочницу.
💎 Поддержка канала⁉️
👍 Палец вверх — есть контакт
👎 Палец вниз — нет контакта
👇 Каждый комментарий от профи — драгоценность для начинающих.
Каждый вопрос от новичка — проверка знаний для профи.
И то, и другое приветствуется.
#️⃣ #SQLOptimization #SQL #Oracle
👍12❤2👎1🔥1
DB developers channel
Затравка для следующего поста. Как Вы считаете, если в плане запроса есть запись MERGE JOIN CARTESIAN, насколько это плохо? и, если Вы отвечаете неплохо, то в каких именно случаях декартовых произведения множеств полезны?
🔆 Cogito, ergo sum
или по-русски «Мыслю, следовательно, существую» - Рене Декарт
🚀 Серия: Оптимизация SQL-запросов
Тема: MERGE CARTESIAN — соединения таблиц
💡 Во всех источниках по оптимизации SQL можно встретить предупреждение:
если видите в плане строку MERGE JOIN CARTESIAN, — стоит насторожиться.
Почему?
Потому что это значит, что оптимизатор решил перемножить два набора данных,
то есть выполнить декартово произведение множеств.
Такой вид соединения вы гарантированно получите, если напишете, например:
📘 Результат этих запросов — это декартово произведение множеств A и B.
Если вдруг забыли, как это выглядит — вот пример:
То есть количество строк = 3 * 3 = 9.
🧠 А теперь представьте, что таблица A содержит 1 млрд строк,
а таблица B — 100 млн строк.
И вот уже результат — 10^17 строк.
Не уверен, что во Вселенной столько атомов,
но пространства TEMP точно не хватит нигде.
Да и выполняться это будет сказочно долго.
📛 Поэтому в книгах по оптимизации справедливо пишут:
если в плане появился MERGE JOIN CARTESIAN,
скорее всего, вы забыли указать условие соединения.
У новичков такое бывает — пока рефлекс не выработался.
Но!
Я хочу поделиться своей идеей, которую придумал лет 5–6 назад.
В ней я осознанно и эффективно использую картезианское соединение.
Скорее всего, не я один, но приятно, что дошёл до этого сам.
Работа разработчика ведь часто состоит в том,
чтобы переосмысливать то, что увиденно, услышано, прочитано.
📊 Представьте, что вы читаете данные из таблицы A дважды или трижды,
но с разными условиями clause1, clause2,
а затем объединяете результаты.
Причём вам нужно рассчитать разные вычисляемые поля field1, field2
в зависимости от условий.
В выражения могут использоваться, и аналитические, и групповые функции.
Обычно это делают через UNION ALL:
🎯 Проблема в том, что таблица A должна считаться дважды (FULL ACCESS TABLE).
И по-другому вроде бы нельзя, ведь строки должны повторяться.
Попытка сделать всё в одном запросе через CASE не работает:
⚠️ Такой подход ошибочен,
потому что строки, подходящие под оба условия, обязаны дублироваться.
🧩 Чтобы не читать таблицу дважды, я придумал такой вариант:
В чём выигрыш?
Мы читаем таблицу всего один раз,
а затем просто умножаем её на 2 с помощью CROSS JOIN.
Для многих отчётных запросов это даёт отличную оптимизацию.
💬 Кто не знал про такой подход — пользуйтесь!
Пусть MERGE CARTESIAN будет вам не врагом, а инструментом ⚙️
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница.
💎 Поддержка канала⁉️.
👍 Хороший прием буду знать!
👎 Какая ерунда, честно слово!
💬 Как приятно читать Ваши комменты! Вы бы знали! 👇
#️⃣ #SQLOptimization #Cases #SQL #Oracle #PostgreSQL #PLSQL
или по-русски «Мыслю, следовательно, существую» - Рене Декарт
🚀 Серия: Оптимизация SQL-запросов
Тема: MERGE CARTESIAN — соединения таблиц
💡 Во всех источниках по оптимизации SQL можно встретить предупреждение:
если видите в плане строку MERGE JOIN CARTESIAN, — стоит насторожиться.
Почему?
Потому что это значит, что оптимизатор решил перемножить два набора данных,
то есть выполнить декартово произведение множеств.
Такой вид соединения вы гарантированно получите, если напишете, например:
SELECT * FROM a, b;
-- или
SELECT * FROM a CROSS JOIN b;
-- или
SELECT * FROM a INNER JOIN b ON (1 = 1);
📘 Результат этих запросов — это декартово произведение множеств A и B.
Если вдруг забыли, как это выглядит — вот пример:
A = {1, 2, 3}
B = {'A', 'B', 'C'}
A × B = {
{1,'A'},{1,'B'},{1,'C'},
{2,'A'},{2,'B'},{2,'C'},
{3,'A'},{3,'B'},{3,'C'}
}
То есть количество строк = 3 * 3 = 9.
🧠 А теперь представьте, что таблица A содержит 1 млрд строк,
а таблица B — 100 млн строк.
И вот уже результат — 10^17 строк.
Не уверен, что во Вселенной столько атомов,
но пространства TEMP точно не хватит нигде.
Да и выполняться это будет сказочно долго.
📛 Поэтому в книгах по оптимизации справедливо пишут:
если в плане появился MERGE JOIN CARTESIAN,
скорее всего, вы забыли указать условие соединения.
У новичков такое бывает — пока рефлекс не выработался.
Но!
Я хочу поделиться своей идеей, которую придумал лет 5–6 назад.
В ней я осознанно и эффективно использую картезианское соединение.
Скорее всего, не я один, но приятно, что дошёл до этого сам.
Работа разработчика ведь часто состоит в том,
чтобы переосмысливать то, что увиденно, услышано, прочитано.
📊 Представьте, что вы читаете данные из таблицы A дважды или трижды,
но с разными условиями clause1, clause2,
а затем объединяете результаты.
Причём вам нужно рассчитать разные вычисляемые поля field1, field2
в зависимости от условий.
В выражения могут использоваться, и аналитические, и групповые функции.
Обычно это делают через UNION ALL:
SELECT expression1 AS field1, expression2 AS field2
FROM a
WHERE clause1 = TRUE
UNION ALL
SELECT expression3 AS field1, expression4 AS field2
FROM a
WHERE clause2 = TRUE;
🎯 Проблема в том, что таблица A должна считаться дважды (FULL ACCESS TABLE).
И по-другому вроде бы нельзя, ведь строки должны повторяться.
Попытка сделать всё в одном запросе через CASE не работает:
SELECT
CASE WHEN clause1 THEN expression1 ELSE expression3 END AS field1,
CASE WHEN clause2 THEN expression2 ELSE expression4 END AS field2
FROM a
WHERE clause1 OR clause2;
⚠️ Такой подход ошибочен,
потому что строки, подходящие под оба условия, обязаны дублироваться.
🧩 Чтобы не читать таблицу дважды, я придумал такой вариант:
SELECT
CASE
WHEN c."case" = 1 AND clause1 THEN expression1
WHEN c."case" = 2 AND clause2 THEN expression2
END AS field1,
CASE
WHEN c."case" = 1 AND clause1 THEN expression3
WHEN c."case" = 2 AND clause2 THEN expression4
END AS field2
FROM a
CROSS JOIN (
SELECT 1 AS "case" FROM dual
UNION ALL
SELECT 2 AS "case" FROM dual
) c
WHERE clause1 OR clause2;
В чём выигрыш?
Мы читаем таблицу всего один раз,
а затем просто умножаем её на 2 с помощью CROSS JOIN.
Для многих отчётных запросов это даёт отличную оптимизацию.
💬 Кто не знал про такой подход — пользуйтесь!
Пусть MERGE CARTESIAN будет вам не врагом, а инструментом ⚙️
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница.
💎 Поддержка канала⁉️.
👍 Хороший прием буду знать!
👎 Какая ерунда, честно слово!
💬 Как приятно читать Ваши комменты! Вы бы знали! 👇
#️⃣ #SQLOptimization #Cases #SQL #Oracle #PostgreSQL #PLSQL
👍10❤3👎1
🎵 «Взгляд от подбородка, я весел и суров,
А моя походка мелет пыль дворов.
Я исполнен страсти жаркого огня,
Не видала счастья — полюби меня.» — Гарик Сукачев.
📚 Серия «Оптимизация SQL-запросов».
Планы запросов из множества таблиц. Тестовая структура
Давным-давно я проводил опрос среди читателей о том, что они считают самым сложным в оптимизации запросов:
Самая существенная часть — 24% — ответила так:
«Простые планы я понимаю, но если план большой и состоит из множества строк — теряюсь».
Дело в том, что тема несложная, но объёмная. И самое главное — интуитивно понятная, т.е. в принципе каждый может логически прийти к результату.
Там нет хитрых секретов — всё предельно логично.
Проблема только в одном: с чего начать повествование так, чтобы это было интересно и доступно.
Вспомните пост про библиотекаря — многим он понравился.
В первую очередь предлагаю продолжить и рассмотреть тестовые таблицы, на основе которых уже будем изучать планы запросов.
Мы постораемся запутать нашего библиотекаря как следует!
Боже, это самый скучный и утомительный этап — разбираться в структуре (при том бесплатно)!
Но что поделаешь!? Реальные структуры публиковать бессмысленно — для большинства они непонятны, слишком много специфики.
Поэтому возьмём понятные всем объекты:
📘 книги
🏷 жанры книг
🏢 издательства
✍️ авторы
🌍 страны
А также две таблицы для связки многие-ко-многим:
— книги-жанры
— книги-авторы
Позже эти таблицы заполним рандомно тысячами или миллионами строк 📊
На этом этапе возникает два вопроса:
1️⃣ Какой минимальный набор индексов необходим для работы с такой структурой?
2️⃣ Какие запросы (чисто теоретически) могут быть сформулированы к данным?
Вот уже готовые две темы для обсуждения.
Если на первый вопрос я могу ответить самостоятельно, то со вторым без помощи не обойтись.
Проявите фантазию и сформулируйте самый сложный запрос к данным — в следующем посте разберём самый интересный вариант.
Переложим его на SQL и поизучаем план этого запроса
📌 Итак, мой пример сложного запроса:
Необходимо найти всех авторов:
у которых были книги ровно в трёх жанрах — не больше и не меньше
книги этих авторов публиковались как минимум в двух странах
имена этих авторов содержат букву «a»
фамилия автора содержит фразу «ra»
время публикации между книгами автора не превышало 10 лет
Необходимо вывести:
— id автора
— фамилию автора
— имя автора
— имена всех соавторов второй книги автора, если есть книга и соавторы
— все жанры пятой книги автора, если есть пятая книга
— дату издания первой книги автора.
🔥 Челлендж: попробуйте сформулируйте запрос сложнее и интереснее к этим данным.
Пора автору канала порешать Ваши задачки, а не наоборот.
👍 Правильная дорога!
👎 Тупик!
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница.
💎 Поддержка канала⁉️.
💬 Ваши варианты пишете тут👇
#️⃣ #SQLOptimization
А моя походка мелет пыль дворов.
Я исполнен страсти жаркого огня,
Не видала счастья — полюби меня.» — Гарик Сукачев.
📚 Серия «Оптимизация SQL-запросов».
Планы запросов из множества таблиц. Тестовая структура
Давным-давно я проводил опрос среди читателей о том, что они считают самым сложным в оптимизации запросов:
Самая существенная часть — 24% — ответила так:
«Простые планы я понимаю, но если план большой и состоит из множества строк — теряюсь».
Дело в том, что тема несложная, но объёмная. И самое главное — интуитивно понятная, т.е. в принципе каждый может логически прийти к результату.
Там нет хитрых секретов — всё предельно логично.
Проблема только в одном: с чего начать повествование так, чтобы это было интересно и доступно.
Вспомните пост про библиотекаря — многим он понравился.
В первую очередь предлагаю продолжить и рассмотреть тестовые таблицы, на основе которых уже будем изучать планы запросов.
Мы постораемся запутать нашего библиотекаря как следует!
Боже, это самый скучный и утомительный этап — разбираться в структуре (при том бесплатно)!
Но что поделаешь!? Реальные структуры публиковать бессмысленно — для большинства они непонятны, слишком много специфики.
Поэтому возьмём понятные всем объекты:
📘 книги
🏷 жанры книг
🏢 издательства
✍️ авторы
🌍 страны
А также две таблицы для связки многие-ко-многим:
— книги-жанры
— книги-авторы
CREATE TABLE authors
(
author_id NUMBER PRIMARY KEY,
first_name VARCHAR2 (100) NOT NULL,
last_name VARCHAR2 (100) NOT NULL,
birthday DATE,
country_id NUMBER
);
CREATE TABLE genres
(
genre_id NUMBER PRIMARY KEY,
genre_name VARCHAR2 (100) NOT NULL
);
CREATE TABLE publishers
(
publisher_id NUMBER PRIMARY KEY,
publisher_name VARCHAR2 (200) NOT NULL,
country_id NUMBER
);
CREATE TABLE books
(
book_id NUMBER PRIMARY KEY,
title VARCHAR2 (300) NOT NULL,
published DATE,
publisher_id NUMBER
);
CREATE TABLE countries
(
country_id NUMBER PRIMARY KEY,
country_name VARCHAR2 (100) NOT NULL
);
CREATE TABLE book_authors
(
book_id NUMBER NOT NULL,
author_id NUMBER NOT NULL
);
CREATE TABLE book_genres
(
book_id NUMBER NOT NULL,
genre_id NUMBER NOT NULL
);
Позже эти таблицы заполним рандомно тысячами или миллионами строк 📊
На этом этапе возникает два вопроса:
1️⃣ Какой минимальный набор индексов необходим для работы с такой структурой?
2️⃣ Какие запросы (чисто теоретически) могут быть сформулированы к данным?
Вот уже готовые две темы для обсуждения.
Если на первый вопрос я могу ответить самостоятельно, то со вторым без помощи не обойтись.
Проявите фантазию и сформулируйте самый сложный запрос к данным — в следующем посте разберём самый интересный вариант.
Переложим его на SQL и поизучаем план этого запроса
📌 Итак, мой пример сложного запроса:
Необходимо найти всех авторов:
у которых были книги ровно в трёх жанрах — не больше и не меньше
книги этих авторов публиковались как минимум в двух странах
имена этих авторов содержат букву «a»
фамилия автора содержит фразу «ra»
время публикации между книгами автора не превышало 10 лет
Необходимо вывести:
— id автора
— фамилию автора
— имя автора
— имена всех соавторов второй книги автора, если есть книга и соавторы
— все жанры пятой книги автора, если есть пятая книга
— дату издания первой книги автора.
🔥 Челлендж: попробуйте сформулируйте запрос сложнее и интереснее к этим данным.
Пора автору канала порешать Ваши задачки, а не наоборот.
👍 Правильная дорога!
👎 Тупик!
⚠️ Хотите проверить скрипты, но нет базы под рукой - онлайн-песочница.
💎 Поддержка канала⁉️.
💬 Ваши варианты пишете тут👇
#️⃣ #SQLOptimization
👍8❤2👎1