Квиз: Погружение в Postgres. Часть 2
Вы SQL-кремень? Или SQL-слабак?
Часть 2 из 2. Назад к части 1
Добро пожаловать во вторую часть моего глубокого квиза по PostgreSQL!
Этот квиз охватывает смесь знакомых и малоизвестных фич и нюансов PostgreSQL: JOIN, NULL, ANSI SQL, транзакции, внутреннее устройство, индексация, выборка данных… и не только!
Удачи! 🍀
Какое из выражений НЕ является ❌ валидным типом JOIN в PostgreSQL?
Вы ведь знали про CROSS JOIN, верно?
Возможно, JOIN ALL заставил вас задуматься о другой операции — UNION ALL?
Правильный ответ здесь — JOIN ALL ❌.
Корректные типы JOIN это:
INNER JOIN(JOIN по умолчанию)LEFT JOIN(илиLEFT OUTER JOIN)RIGHT JOIN(илиRIGHT OUTER JOIN)FULL JOIN(илиFULL OUTER JOIN)CROSS JOIN(декартово произведение, если только оно не фильтруется последующим предложениемWHERE)
Какой синтаксис (согласно стандарту SQL) используется для автоинкрементного столбца IDENTITY в PostgreSQL (v10+)?
Хотя SERIAL все еще часто используется, способ, соответствующий стандарту SQL, выглядит так:
id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEYid INT IDENTITY(1,1)— это синтаксис SQL Server.id INT AUTO_INCREMENT— это синтаксис MySQL.id INTEGER AUTO_INCREMENT PRIMARY KEY— это синтаксис MySQL.
У этого подхода есть несколько преимуществ перед SERIAL:
- Соответствие стандарту SQL
- Улучшенное управление владением последовательностью (sequence ownership)
- Более четкая семантика при ручных вставках
- Лучшая работа с репликацией
На самом деле SERIAL — это просто сокращение для создания последовательности и установки значения по умолчанию, что может привести к проблемам при создании дампов и восстановлении данных.
Какая из этих возможностей PostgreSQL НЕ ❌ является частью стандарта ANSI SQL?
Примеры расширений PostgreSQL сверх стандарта ANSI SQL:
- Предложение
RETURNING. - Оператор
ILIKEдля регистронезависимого поиска. - Типы массивов и операции с ними.
- Типы JSON/JSONB.
Оригинальный стандарт ANSI SQL включает:
WITH(обобщенные табличные выражения — CTE).LIKEдля сопоставления с шаблоном.- Соединения
LATERAL. - Оконные функции.
За исключением варианта «Зависит от побочных эффектов», что происходит после возникновения ошибки?
BEGIN;UPDATE students SET status = 'alumni' WHERE grade = 12;UPDATE students SET graduation_date = CURRENT_DATE WHERE grade = 12;UPDATE students SET grade = 'GRADUATED'; -- 🚨 Error: type mismatchCOMMIT;Когда здесь возникает ошибка:
- Вся транзакция откатывается
- Никакие изменения не фиксируются
- Транзакция помечается как неудавшаяся
Для частичной фиксации:
BEGIN;UPDATE ...;SAVEPOINT my_save;UPDATE ...; -- ErrorROLLBACK TO my_save;COMMIT;Какое условие ON PostgreSQL может использовать для выполнения HASH JOIN?
SELECT *FROM students aJOIN grades bON /* What goes here? ⁉ */;Hash join возможен только при наличии условия равенства (=) в предложении ON.
=— это основной предикат для хеш-соединений>и>=не являются предикатами для hash joinHASH JOINне является валидным синтаксисом в PostgreSQL
Планировщик все равно может выбрать nested loop или merge join в зависимости от размера таблиц, статистики, индексов и настроек памяти.
Какой индекс лучше всего подходит для этого запроса?
-- Query:SELECT * FROM studentsWHERE grade_level = 42 AND last_name IN ('Levy', 'Smith');Удивительно, но составной индекс может использоваться даже тогда, когда порядок колонок в тексте запроса не совпадает с определением индекса.
Примечание: В B-tree индексах порядок ведущих колонок имеет решающее значение. Поскольку этот запрос фильтрует по grade_level и last_name, индекс, начинающийся с grade_level, а затем содержащий last_name, отлично подходит.
Чем быстрее SQL сможет отсеять ненужные строки, тем выше будет производительность.
Например, если у вас есть таблица task и вы знаете, что колонка status обладает более высокой селективностью, чем created_at, стоит выбрать индекс, начинающийся со status для запроса вида status = 'done' AND created_at > '2024-01-01'. Важен не порядок условий в блоке WHERE, а порядок колонок в индексе и форма предикатов.
B+Tree индекс может использоваться для:
- Ведущей колонки или самого левого префикса колонок.
- Операторов
=,>,<,BETWEENиIN, в зависимости от структуры запроса. - Сопоставления префиксов, например
LIKE 'prefix%'.
Спасибо u/mwdb2 за исправление ошибки в предыдущей версии этого объяснения!
Какой вариант правильный для имен столбцов, чувствительных к регистру?
CREATE TABLE grades ( "Student Name" VARCHAR(100), 'Student Grade' INTEGER);В PostgreSQL:
- Двойные кавычки (
") используются для идентификаторов (имена таблиц, имена столбцов) - Одинарные кавычки (
') используются для строковых литералов
Лучшие практики:
- Избегайте смешанного регистра или пробелов в именах
- Используйте snake_case для идентификаторов
- Используйте кавычки для идентификаторов только в случае крайней необходимости
Как указать имя столбца, содержащее точку?
CREATE TABLE teachers ( id INT GENERATED BY DEFAULT AS IDENTITY, first.name TEXT, -- How do you include the column: first.name? salary NUMERIC);PostgreSQL использует двойные кавычки для экранирования идентификаторов:
- Двойные кавычки
"..."для идентификаторов (имена столбцов, таблиц) - Одинарные кавычки
'...'для строковых литералов - Запись
first.nameинтерпретируется какимя_таблицы.имя_столбца
Без кавычек точка в имени столбца будет воспринята как разделитель схемы или таблицы! Кроме того, все идентификаторы без кавычек по умолчанию приводятся к нижнему регистру.
Чтобы избежать сюрпризов, лучше использовать snake_case и не применять специальные символы.
Какой синтаксис является допустимым для случайной выборки 10% студентов?
Этот вопрос с подвохом! Здесь расставлено около 6 ловушек, вот некоторые из них:
- Описание высокого уровня + фрагменты синтаксиса. Проверяет, как вы адаптируете и собираете неполную информацию.
- Требуется хорошее знание синтаксиса
TABLESAMPLE, так как ни один из вариантов сTABLESAMPLEне использует правильный синтаксис! (Коварно, я знаю! Продолжайте, я в вас верю!!! ❤️) - Варианты
ROW_NUMBER() OVER (ORDER BY RAND())иORDER BY RANDOM() > 10— это отвлекающие маневры. Они выглядят похожими на правду, но это не так. ФункцииRAND()не существует. АRANDOM() > 10— это логическое выражение, которое всегда будет ложным, так какRANDOM()всегда возвращает значение в диапазоне0.0-1.0. Еще одна проблема:ORDER BY RANDOM() > 10будет выполнять сортировку по логическому значению, а не по случайному числу. Но это> 10выглядело так, будто это проценты, верно? BERNOULLI— это валидный метод, однакоSAMPLETABLE— нет.WHERE RANDOM() >= 0.1— это ловушка!RANDOM()возвращает значение в диапазоне0.0 <= x < 1.0, поэтому>= 0.1вернет около 90% строк, а не 10%.
Что касается метода TABLESAMPLE, PostgreSQL поддерживает два метода выборки:
BERNOULLI: Каждая строка имеет равную вероятность попадания в выборку.SYSTEM: Выборка на уровне блоков (быстрее, но менее случайно).
Чтобы узнать больше о выборках, ознакомьтесь с этой статьей от Render.
Пример:
-- Sample 10% of rowsSELECT * FROM students TABLESAMPLE BERNOULLI (10);
-- For repeatable results, use REPEATABLE:SELECT * FROM studentsTABLESAMPLE BERNOULLI (10) REPEATABLE (42);Также стоит упомянуть, что сообщалось, что TABLESAMPLE не всегда обеспечивает высокую степень случайности. Если вам нужна более случайная выборка фиксированного размера, рассмотрите использование ORDER BY RANDOM() LIMIT 10. Но имейте в виду, что это вернет 10 строк, а не 10%, и может работать медленно на больших таблицах.
Существуют также сторонние расширения, предоставляющие лучшие методы случайной выборки, но это выходит за рамки данного вопроса! 😅
Что делает это заклинание?
CREATE INDEX idx_active_studentsON students(last_name)WHERE status = 'active';Этот трюк называется Partial Index (частичный индекс).
Они полезны для экономии места на диске и ускорения запросов, когда обычно запрашивается только подмножество строк.
Postgres будет использовать этот индекс только в том случае, если в запросе присутствует условие status = 'active'.
Напоминание: Postgres не поддерживает «подсказки к запросам» (query hints), как другие СУБД. Планировщик запросов сам решает, когда использовать подходящие индексы.
Сколько строк вернет этот запрос?
SELECT COUNT(*)FROM studentsWHERE grade = NULL;Ладно, это было с подвохом. Ловушка в том, что x = NULL никогда не найдет совпадений ни в одной строке, и запрос ВСЕГДА вернет одну строку со значением 0.
Так как NULL — это неизвестное значение, его нельзя сравнивать ни с чем, даже с самим собой.
Правильный способ проверки на NULL — использовать оператор IS NULL.
Пример:
postgres=# SELECT count(*) FROM students WHERE age = null;count------- 0(1 row)
postgres=#postgres=# SELECT count(*) FROM students WHERE age is null;count------- 3(1 row)Что делает EXPLAIN ANALYZE для операторов UPDATE?
EXPLAIN ANALYZEUPDATE studentsSET grade = grade + 1WHERE status = 'active';EXPLAIN ANALYZE на самом деле выполняет запрос! Будьте осторожны при анализе операций записи, а именно операторов UPDATE, DELETE и INSERT. Возможно, вам придется выполнять операции очистки после каждого EXPLAIN ANALYZE, чтобы получить повторяемые результаты.
Лучшие практики:
- Используйте
EXPLAIN(без ANALYZE) для UPDATE/DELETE/INSERT - Оборачивайте модификации в транзакции:
BEGIN;EXPLAIN ANALYZE ...;ROLLBACK;Отлично! Вы глубоко погрузились в нюансы PostgreSQL! 🐘
Надеюсь, вы узнали что-то новое или хотя бы набрали достаточно баллов, чтобы было чем похвастаться! 🏆