SQL Anti-Patterns 💬 Длинная дискуссия
—
Комментарии (163)
- Проблема с DISTINCT часто указывает на плохое моделирование данных или непонимание теории множеств.
- Использование DISTINCT для "исправления" дубликатов часто является симптомом плохой архитектуры БД или неправильного использования JOIN.
- Создание вьюх на вьюхах может привести к проблемам с производительностью и читаемости кода.
- Использование CASE WHEN вместо отдельной таблицы для статусов может усложнить поддержку и интернационализацию.
- Не используйте SELECT * в продакшене; это может сломать код, если схема изменится.
Subtleties of SQLite Indexes
SQLite использует составные индексы слева направо, без пропусков, останавливаясь на первом диапазонном условии. Например, если индекс включает столбцы A, B, C, а запрос содержит A BETWEEN x AND y AND B = z, оптимизатор сможет использовать только A для фильтрации по диапазону, затем просканирует все подходящие строки для B, игнорируя C. Это объясняет, почему добавление отдельных индексов на каждый столбец бесполезно — SQLite редко объединяет их.
Ключевой вывод: порядок столбцов в индексе критичен. Первым должен идти самый селективный столбец, но если на нём используется диапазон (например, BETWEEN или <=), последующие столбцы в индексе не будут эффективно фильтроваться. Для запросов с несколькими условиями лучше создать индекс, где диапазонные условия стоят последними, чтобы максимизировать использование индекса.
Комментарии (51)
- Критика статьи за отсутствие технической глубины и общие заблуждения о работе индексов, не специфичных для SQLite.
- Обсуждение ментальных моделей индексов как вложенных карт или отсортированных списков, объясняющих важность порядка колонок и ограничения диапазонных запросов.
- Замечания о простоте планировщика запросов SQLite по сравнению с другими СУБД, но признание его адекватности для базовых сценариев.
- Рекомендации по использованию официальной документации SQLite и инструментов вроде
.expertдля анализа индексов. - Предостережения против автоматического добавления индексов из-за затрат на дисковое пространство и замедления записи.
That boolean should probably be something else
Булево значение почти всегда маскирует более точный тип.
Проверь, не дата-время ли это: is_confirmed лучше заменить на nullable confirmed_at. Вы получите момент подтверждения и сможете анализировать баги по времени.
Если поле описывает роль или статус (is_admin, failed), превращайте в enum.
enum UserRole { User, Admin, Guest, SuperAdmin }
enum JobStatus { Queued, Started, Failed, Done }
Enum упрощает добавление новых состояний и защищает от забытых веток.
Проверка прав тоже не должна возвращать bool.
enum PermissionCheck { Allowed, NotPermitted(reason: String) }
Так код читабельнее и можно вернуть причину отказа.
Когда же использовать bool? Только как временную переменную-флаг для сложного условия, чтобы не вычислять его дважды или дать имя.
Комментарии (103)
- Основной спор: стоит ли хранить «события» как булевы флаги или как nullable-даты/enum’ы, чтобы не терять данные (время события).
- Противники: это нарушает KISS, раздувает схему и вводит двусмысленность (null = не случилось или ошибка?).
- Сторонники: булевы поля не «помнят» контекст, легко образуют недопустимые комбинации флагов, а дата или enum выразительнее.
- Для параметров функций булевы флаги считаются плохо читаемыми; спасают именованные аргументы, отдельные функции или бит-маски.
- Встраиваемые/индустриальные системы часто считают булевы типы оптимальными по памяти и не применяют совет к себе.