Oracle Database 23.9 добавила не-позиционные вставки.
Это позволяет задавать значения столбцов в выражении SET (похоже на UPDATE).
Gerald Venzl подробно объясняет детали и рассказывает предысторию.
👉 @SQLPortal
Это позволяет задавать значения столбцов в выражении SET (похоже на UPDATE).
INSERT INTO employees
SET employee_id = 210, first_name = 'Gerald', ...
Gerald Venzl подробно объясняет детали и рассказывает предысторию.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤3
Охотьтесь на N+1 и убирайте их
Проблема N+1-запросов опасна и легко может ускользнуть из виду. Сценарий такой. Один запрос получает набор данных. Затем для каждой строки нужен ещё один запрос. Количество запросов при загрузке страницы зависит от размера выборки, и если N большой, производительность падает
Пример на псевдокоде
Лучше переписать на один запрос с JOIN, чтобы забрать всё сразу
Интересный момент в том, что иногда N+1 почти не заметен. Если N маленький, база настроена и находится рядом с приложением, всё может работать быстро. Но если N=100, сразу ощущается тормоз и база нагружается лишней работой. Исправив это, вы улучшите UX и снизите нагрузку на сервер БД
Иногда такие запросы пишут руками. Но часто их генерирует ORM незаметно для вас. В любом случае избавляйтесь от них
👉 @SQLPortal
Проблема N+1-запросов опасна и легко может ускользнуть из виду. Сценарий такой. Один запрос получает набор данных. Затем для каждой строки нужен ещё один запрос. Количество запросов при загрузке страницы зависит от размера выборки, и если N большой, производительность падает
Пример на псевдокоде
// один запрос
users = db.query('SELECT * FROM users')
// превращается в N дополнительных
for (const user of users) {
posts = db.query(`
SELECT * FROM posts
WHERE user_id = user->id`)
user.posts = posts
}
// только теперь можно вернуть результат
Лучше переписать на один запрос с JOIN, чтобы забрать всё сразу
results = await db.query(
`SELECT u.*, p.*
FROM users u
LEFT JOIN posts p
ON u.id = p.user_id`)
Интересный момент в том, что иногда N+1 почти не заметен. Если N маленький, база настроена и находится рядом с приложением, всё может работать быстро. Но если N=100, сразу ощущается тормоз и база нагружается лишней работой. Исправив это, вы улучшите UX и снизите нагрузку на сервер БД
Иногда такие запросы пишут руками. Но часто их генерирует ORM незаметно для вас. В любом случае избавляйтесь от них
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤8
Skip scan для B-деревьев в Postgres 18
В Postgres 18 появится заметный прирост производительности для некоторых многоколонковых B-tree индексов.
Классический B-tree-поиск работает так: индекс читается, начиная с позиции, определяемой условиями запроса по колонкам индекса в их порядке. Если в запросе нет условия (WHERE или LIMIT BY) по первой (ведущей) колонке, то база не может использовать этот метод и вынуждена выполнять более медленный полный скан таблицы.
Skip scanning позволяет «пропускать» части индекса и всё же эффективно использовать его.
Этот подход работает, когда в запросе отсутствует условие по ведущей колонке, и при этом у пропущенной колонки низкая кардинальность (небольшое количество уникальных значений).
Оптимизация выполняется следующим образом:
I. Определяются все уникальные значения в пропущенных ведущих колонках.
II. Для каждого значения выполняется целевой проход по индексу.
Пример:
Допустим, у нас есть таблица sales со столбцами status и date.
Создаём многоколонковый индекс:
Далее выполняется запрос без фильтрации по status:
До версии 18 приходилось выполнять полный скан.
В Postgres 18+, если у status низкая кардинальность и всего несколько различных значений, планировщик сможет выполнять отдельные индексные проходы (в подходящих случаях).
Всё это работает прозрачно для пользователя — никаких ручных включений не требуется. Идея состоит в том, чтобы ускорить аналитические сценарии, где фильтры и условия часто меняются и не всегда совпадают с индексами.
Планировщик сам решает, стоит ли использовать skip scan, опираясь на статистику таблицы и количество уникальных значений в пропускаемых колонках. Если уникальных значений слишком много, последовательное сканирование может оказаться быстрее.
👉 @SQLPortal
В Postgres 18 появится заметный прирост производительности для некоторых многоколонковых B-tree индексов.
Классический B-tree-поиск работает так: индекс читается, начиная с позиции, определяемой условиями запроса по колонкам индекса в их порядке. Если в запросе нет условия (WHERE или LIMIT BY) по первой (ведущей) колонке, то база не может использовать этот метод и вынуждена выполнять более медленный полный скан таблицы.
Skip scanning позволяет «пропускать» части индекса и всё же эффективно использовать его.
Этот подход работает, когда в запросе отсутствует условие по ведущей колонке, и при этом у пропущенной колонки низкая кардинальность (небольшое количество уникальных значений).
Оптимизация выполняется следующим образом:
I. Определяются все уникальные значения в пропущенных ведущих колонках.
II. Для каждого значения выполняется целевой проход по индексу.
Пример:
Допустим, у нас есть таблица sales со столбцами status и date.
Создаём многоколонковый индекс:
CREATE INDEX idx_status_date
ON sales (status, date);
Далее выполняется запрос без фильтрации по status:
SELECT * FROM orders
WHERE order_date > '2025-01-01';
До версии 18 приходилось выполнять полный скан.
В Postgres 18+, если у status низкая кардинальность и всего несколько различных значений, планировщик сможет выполнять отдельные индексные проходы (в подходящих случаях).
Всё это работает прозрачно для пользователя — никаких ручных включений не требуется. Идея состоит в том, чтобы ускорить аналитические сценарии, где фильтры и условия часто меняются и не всегда совпадают с индексами.
Планировщик сам решает, стоит ли использовать skip scan, опираясь на статистику таблицы и количество уникальных значений в пропускаемых колонках. Если уникальных значений слишком много, последовательное сканирование может оказаться быстрее.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8👍3🔥2
Я понял, почему никто не пользуется MongoDB.
Путь перегорожен B-деревьями.😈
Карта из книги Designing Data-Intensive Applications
👉 @SQLPortal
Путь перегорожен B-деревьями.
Please open Telegram to view this post
VIEW IN TELEGRAM
😁16❤5
Преобразование строк в список уникальных значений, разделённых запятыми, в Oracle SQL с помощью
Функция LISTAGG появилась в 11g Release 2.
Поддержка DISTINCT добавлена в 19c.
👉 @SQLPortal
LISTAGG (DISTINCT val, ', ') WITHIN GROUP (ORDER BY ...)
Функция LISTAGG появилась в 11g Release 2.
Поддержка DISTINCT добавлена в 19c.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍4🤔2
Doom в SQL уже здесь
Представьте себе шутер уровня AAA, но… целиком построенный на SQL-запросах.
Вся игровая реальность живёт в таблицах базы данных.
Герой двигается и стреляет не с клавиатуры, а через запросы.
Даже такие штуки как raycasting, отрисовка спрайтов, работа с HUD и обработка видимости — реализованы с помощью VIEW.
Короче, хотите сделать хэдшот, то пишите правильный SELECT.🎯
https://github.com/cedardb/DOOMQL
👉 @SQLPortal
Представьте себе шутер уровня AAA, но… целиком построенный на SQL-запросах.
Вся игровая реальность живёт в таблицах базы данных.
Герой двигается и стреляет не с клавиатуры, а через запросы.
Даже такие штуки как raycasting, отрисовка спрайтов, работа с HUD и обработка видимости — реализованы с помощью VIEW.
Короче, хотите сделать хэдшот, то пишите правильный SELECT.
https://github.com/cedardb/DOOMQL
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍4🤯4😁1
Учись работать с высокоуровневой аналитикой производительности, такой как коэффициент попаданий в кэш, на учебном сайте:
https://crunchydata.com/developers/playground/high-level-performance-analytics
👉 @SQLPortal
https://crunchydata.com/developers/playground/high-level-performance-analytics
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Алгоритм хэш-соединения
1. Строит хэш-таблицу по колонкам соединения меньшей таблицы.
2. Читает большую таблицу и проверяет, хэшируются ли её колонки соединения в таблицу, построенную на шаге 1.
Сложность растёт линейно от размеров таблиц: O(n + m)
Vlad Mihalcea объясняет здесь
👉 @SQLPortal
1. Строит хэш-таблицу по колонкам соединения меньшей таблицы.
2. Читает большую таблицу и проверяет, хэшируются ли её колонки соединения в таблицу, построенную на шаге 1.
Сложность растёт линейно от размеров таблиц: O(n + m)
Vlad Mihalcea объясняет здесь
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
Самый частый вопрос на собеседованиях по SQL:
Как обработать дубликаты?
Вот простой способ:
I. Использовать
II. Сделать
III. Добавить
👉 @SQLPortal
Как обработать дубликаты?
Вот простой способ:
I. Использовать
COUNT(*)II. Сделать
GROUP BY по полю, которое должно быть уникальнымIII. Добавить
HAVING > 1 (как WHERE, но для сгруппированных данных), чтобы показать строки, встречающиеся более одного разаPlease open Telegram to view this post
VIEW IN TELEGRAM
👍16❤8🤔1
Этот курс по PostgreSQL для начинающих поможет понять, подходит ли она для ваших проектов.
В курсе разбираются оператор SELECT, выражения WHERE, агрегатные функции и другие темы.
https://freecodecamp.org/news/posgresql-course-for-beginners/
👉 @SQLPortal
В курсе разбираются оператор SELECT, выражения WHERE, агрегатные функции и другие темы.
https://freecodecamp.org/news/posgresql-course-for-beginners/
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍2
Для больших наборов данных использование LIMIT + OFFSET работает не всегда эффективно, особенно если значение OFFSET большое.
Вместо этого можно применять постраничную выборку на основе курсора, где фильтрация идёт по значениям сортировки.
Именно поэтому сервисы вроде GitHub используют такой подход к пагинации
👉 @SQLPortal
Вместо этого можно применять постраничную выборку на основе курсора, где фильтрация идёт по значениям сортировки.
Именно поэтому сервисы вроде GitHub используют такой подход к пагинации
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍4🔥3
Нельзя понять производительность SQL-запроса просто по его коду. Для этого нужен план выполнения.
Пример:
Из этого кода не видно, как быстро выполнится запрос.
Если на колонке grade нет индекса, база сделает полное сканирование таблицы, и выполнение займёт несколько секунд в зависимости от числа строк.
Если добавить индекс на grade, тот же запрос выполнится быстрее.
Если сделать индекс покрывающим (добавить id как включённый столбец), то это будет уже index-only scan, и запрос станет ещё быстрее.
Если вставить миллион записей и сразу выполнить запрос (без работы сборщика мусора MVCC), он снова замедлится, так как нужно проверять видимость строк в heap.
Во всех этих случаях код на бэкенде не менялся — один и тот же запрос.
Но его производительность разная и зависит от состояния базы.
План выполнения показывает, как именно запрос исполнялся. Это ключ к пониманию.
👉 @SQLPortal
Пример:
select id from student where grade >= 99
Из этого кода не видно, как быстро выполнится запрос.
Если на колонке grade нет индекса, база сделает полное сканирование таблицы, и выполнение займёт несколько секунд в зависимости от числа строк.
Если добавить индекс на grade, тот же запрос выполнится быстрее.
Если сделать индекс покрывающим (добавить id как включённый столбец), то это будет уже index-only scan, и запрос станет ещё быстрее.
Если вставить миллион записей и сразу выполнить запрос (без работы сборщика мусора MVCC), он снова замедлится, так как нужно проверять видимость строк в heap.
Во всех этих случаях код на бэкенде не менялся — один и тот же запрос.
Но его производительность разная и зависит от состояния базы.
План выполнения показывает, как именно запрос исполнялся. Это ключ к пониманию.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤1
Будьте осторожны при фильтрации внутренней таблицы в OUTER JOIN.
Например, правой таблицы в LEFT OUTER JOIN.
Если применить фильтр к ней в секции WHERE, то соединение фактически превращается в INNER JOIN.
Чтобы сохранить именно OUTER JOIN, фильтровать внутреннюю таблицу нужно в секции ON.
На схеме показан OUTER JOIN по числовой колонке с внутренними фильтрами.
👉 @SQLPortal
Например, правой таблицы в LEFT OUTER JOIN.
Если применить фильтр к ней в секции WHERE, то соединение фактически превращается в INNER JOIN.
Чтобы сохранить именно OUTER JOIN, фильтровать внутреннюю таблицу нужно в секции ON.
На схеме показан OUTER JOIN по числовой колонке с внутренними фильтрами.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5🔥3👍2
С oracle sql 25.2 теперь можно запускать MCP-серверы.
Они позволяют взаимодействовать с вашей Oracle Database через AI-агентов.
That Jeffsmith подготовил FAQ, который помогает понять, как это работает, включая:
• Работает ли это с 11g или 12c? Скорее всего, да
• Как сделать использование безопасным
👉 @SQLPortal
Они позволяют взаимодействовать с вашей Oracle Database через AI-агентов.
That Jeffsmith подготовил FAQ, который помогает понять, как это работает, включая:
• Работает ли это с 11g или 12c? Скорее всего, да
• Как сделать использование безопасным
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍4
Работа с SQL на больших данных — опыт перехода с 4 млн на 42 млн клиентов
Разработчик поделился историей. В новой компании с 42 млн пользователей его первый SQL-запрос «упал». Масштаб данных потребовал изменить подход.
Что изменилось
I. Дробление запросов
Вместо монолитных SQL-скриптов в 500 строк используются промежуточные staging-таблицы, пошаговая логика и CTE. Это помогает базе работать стабильнее и быстрее.
II. Разделение данных
Огромная таблица заменена на две структуры
- Current содержит только актуальный срез для быстрых дашбордов
- History хранит полную историю для комплаенса
Так удалось сбалансировать скорость и полноту.
III. Точность требований
Формулировки вроде «дайте все данные» больше не работают. Теперь разработчик уточняет какой период нужен, какие сегменты важны, какой бизнес-вопрос стоит за задачей.
При работе с десятками миллионов клиентов важно дробить запросы, разделять данные на текущие и архивные и добиваться конкретных требований. Такой подход ускоряет аналитику и снижает нагрузку на базы.
👉 @SQLPortal
Разработчик поделился историей. В новой компании с 42 млн пользователей его первый SQL-запрос «упал». Масштаб данных потребовал изменить подход.
Что изменилось
I. Дробление запросов
Вместо монолитных SQL-скриптов в 500 строк используются промежуточные staging-таблицы, пошаговая логика и CTE. Это помогает базе работать стабильнее и быстрее.
II. Разделение данных
Огромная таблица заменена на две структуры
- Current содержит только актуальный срез для быстрых дашбордов
- History хранит полную историю для комплаенса
Так удалось сбалансировать скорость и полноту.
III. Точность требований
Формулировки вроде «дайте все данные» больше не работают. Теперь разработчик уточняет какой период нужен, какие сегменты важны, какой бизнес-вопрос стоит за задачей.
При работе с десятками миллионов клиентов важно дробить запросы, разделять данные на текущие и архивные и добиваться конкретных требований. Такой подход ускоряет аналитику и снижает нагрузку на базы.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍4
Внешние таблицы — отличный способ читать текстовые файлы в Oracle Database с помощью SQL.
Но что, если у тебя есть поля длиннее 4000 символов?
Anti Kyte разбирает:
I. Маппинг CHAR в ACCESS PARAMETERS на CLOB
II. Увеличение READSIZE
III. Использование big data driver
👉 @SQLPortal
Но что, если у тебя есть поля длиннее 4000 символов?
Anti Kyte разбирает:
I. Маппинг CHAR в ACCESS PARAMETERS на CLOB
II. Увеличение READSIZE
III. Использование big data driver
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👍3
У Postgres есть огромное преимущество перед MySQL в плане поддержки транзакций.
Большинство операторов CREATE, ALTER и DROP в Postgres являются полноценными участниками многооператорных транзакций. Это означает, что их можно использовать между BEGIN и COMMIT, и все изменения либо успешно применяются, либо откатываются атомарно вместе со всеми другими операторами.
В MySQL это не так. Подобные операторы работают по модели неявного коммита: при выполнении CREATE, ALTER или DROP происходит автоматический коммит текущей транзакции.
Таким образом, в Postgres можно, например:
- создать таблицу и вставить в неё данные в рамках одного атомарного действия,
- атомарно изменить несколько таблиц.
👉 @SQLPortal
Большинство операторов CREATE, ALTER и DROP в Postgres являются полноценными участниками многооператорных транзакций. Это означает, что их можно использовать между BEGIN и COMMIT, и все изменения либо успешно применяются, либо откатываются атомарно вместе со всеми другими операторами.
В MySQL это не так. Подобные операторы работают по модели неявного коммита: при выполнении CREATE, ALTER или DROP происходит автоматический коммит текущей транзакции.
Таким образом, в Postgres можно, например:
- создать таблицу и вставить в неё данные в рамках одного атомарного действия,
- атомарно изменить несколько таблиц.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤5👍2
Нашёлся интересный ресурс для тех, кто готовится к техническим собеседованиям: на нём собрано очень много практических SQL-вопросов из самых разных областей.
Есть задачи на работу с запросами, оптимизацию, подзапросы, агрегации и даже отдельный блок, посвящённый вопросам, встречающимся в интервью у компаний уровня FAANG.🥸
👉 @SQLPortal
Есть задачи на работу с запросами, оптимизацию, подзапросы, агрегации и даже отдельный блок, посвящённый вопросам, встречающимся в интервью у компаний уровня FAANG.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Консистентность «read-your-writes» означает, что любая запись, сделанная в базу данных, сразу становится доступной для чтения тем же клиентом.
Звучит очевидно, но бывают ситуации, когда это правило не выполняется, особенно при работе на больших масштабах.
На сервере с одной нодой это легко гарантировать при использовании последовательных транзакций.
В среде с primary и replica всё сложнее. Во многих базах данных все записи и часть чтений обрабатываются на primary, а большая часть чтений выполняется на репликах. Из-за задержки репликации может возникнуть ситуация, когда клиент записывает данные в базу и тут же пытается их прочитать с реплики, но записи там ещё нет.
Такое возможно при использовании асинхронной или полусинхронной репликации, так как они не требуют, чтобы все реплики получили данные до ответа клиенту. При синхронной репликации primary отвечает только после того, как все реплики подтвердят получение записи.
Если используется асинхронная или полусинхронная репликация, чтения на реплики лучше направлять только тогда, когда консистентность «read-your-writes» не критична.
👉 @SQLPortal
Звучит очевидно, но бывают ситуации, когда это правило не выполняется, особенно при работе на больших масштабах.
На сервере с одной нодой это легко гарантировать при использовании последовательных транзакций.
В среде с primary и replica всё сложнее. Во многих базах данных все записи и часть чтений обрабатываются на primary, а большая часть чтений выполняется на репликах. Из-за задержки репликации может возникнуть ситуация, когда клиент записывает данные в базу и тут же пытается их прочитать с реплики, но записи там ещё нет.
Такое возможно при использовании асинхронной или полусинхронной репликации, так как они не требуют, чтобы все реплики получили данные до ответа клиенту. При синхронной репликации primary отвечает только после того, как все реплики подтвердят получение записи.
Если используется асинхронная или полусинхронная репликация, чтения на реплики лучше направлять только тогда, когда консистентность «read-your-writes» не критична.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6