LINUX.ORG.RU

Enum-ы против таблиц

 , ,


0

5

Мы говорим про SQL (конкретно про postgree). Enum-ы обычно быстрее, код с ними строже и проще (со стороны бэкенда), но менее гибкие (не удалить значение без миграции и даже там нужны костылики с пересозданием enum-а, не дополнить полями, разные в разных СУБД). Что вы применяете в своих проектах и на основе чего принимаете решение?

★★★★★

Тож как-то пытались в постгресные енумы, часто менялось, куча миграций, в итоге оставили вот так.

class UserStatus(StrEnum):
    ACTIVE = "ACTIVE"
    DELETED = "DELETED"


class User(BaseModel):
    id = ...
    status: Mapped[UserStatus] = mapped_column(String(128))
ggrn ★★★★★
()

Пробовал как-то постгресовые enum, не понравилось. Юзаем enum'ы на стороне кода

hippi90 ★★★★★
()

менее гибкие (не удалить значение без миграции и даже там нужны костылики с пересозданием enum-а, не дополнить полями, разные в разных СУБД).

Видимо их надо использовать там, где изменения исключены или маловероятны: М, Ж, Др.

bbc69
()

При условии длина небольшая (а зачем тебе большая для статусов), то выключение юникода делает строки почти такими же быстрыми как enum. Во всяком случае я не вижу ни одной причины пердохаться с enum в БД.

no-such-file ★★★★★
()

Та же беда маячит на горизонте. И «юзай енумы на стороне бэка», как тут предложили выше, у меня не особо канает, так как есть еще хранимки на pl/pgsql, в коде которых эти енумы тоже используются..

То есть хочется сквозного решения от енумов в бд, к енумам на бэке и, задача со звёздочкой, эти же енумы на фронте.

У меня пока всё застряло на прототипе с кодогенерацией разных масштабов.

Подписался, предвкушаю толковые советы, хаха.

aol ★★★★★
()
Ответ на: комментарий от aol

если enum как тип pg имеет в вашем случае неудобства

чем хуже наивная таблица id_номер - семантика этого варианта из энума? - врядли вам нужна экономия на битах т.е всяко значение из энума будет размером с текущую «базовый int» (которые ща 4-8 байтные)

т.е. ныряние в доки совсем не вариант?

qulinxao3 ★★
()
Ответ на: комментарий от qulinxao3

чем хуже наивная таблица id_номер

в хранимке такое использовать вообще не хочется.

кейсы в хранимках простые - сравнение входного значения с константой енума.

aol ★★★★★
()
Ответ на: комментарий от no-such-file

Я подумал-подумал и пока остановился на поле VARCHAR(40), куда пихаю уже enum-ы из кода (в 40 символов, чтоб человекочитаемую запись пихать, вроде 'active'/'blocked'/'suspended', например, для юзеров). Всё равно postgreesql хранит строки экономно по факту, т.е. TEXT и VARCHAR для него одно и то же. Лишних «пустых» символов там нет, 40 это ограничение чтоб не попало туда случайно гигабайт текста из-за какой-то ошибки в коде/атаки.

peregrine ★★★★★
() автор топика
Последнее исправление: peregrine (всего исправлений: 2)

Жить в принципе можно с енамами, но мне иногда лень, делаю колонку smallint и конвертирую в енам на уровне модели. Оно конечно менее читаемо становится, если прям запросы в psql катать, но зато меньше места ест, даже сам родной enum, если не изменяет память, 8 байт целое. Ладно бы ещё оно просто требовало миграцию, оно вроде бы до сих пор не умеет меняться в транзакции, то есть изменение енамов просто так не откатить на упавшей миграции, может уже пофиксили, не следил.

neumond ★★
()
Ответ на: комментарий от no-such-file

varchar(40) collate «C»

Это если скорость волнует. Меня больше совместимость волнует и одинаковое поведение в разных ОС, так что БД вообще icu. Ну а где надо я уж индекс подложу, вроде такого:

CREATE INDEX idx_users_status_like ON users (status COLLATE "C");
peregrine ★★★★★
() автор топика
Ответ на: комментарий от aol

кейсы в хранимках простые - сравнение входного значения с константой енума

А можете рассказать, что имеется в виду? Какое-то IF p_mode = 'create' THEN ... ELSIF p_mode = 'update' THEN...? Не доходит в чём кайф от enum (

Toxo2 ★★★★★
()

не удалить значение без миграции

А как ты с таблицей без миграции значение удалишь?

ya-betmen ★★★★★
()
Ответ на: комментарий от peregrine

Меня больше совместимость волнует и одинаковое поведение в разных ОС, так что БД вообще icu.

Увы, но использование системной ICU никак не помогает сохранению совместимости. В разных версиях ОС разные версии ICU с разным поведением. Единственный надёжный вариант сохранения совместимости — компелять самому postgres с фиксированной версией ICU и никогда её не обновлять.

anonymous
()
Ответ на: комментарий от anonymous

ICU всяко реже ломают, чем когда

sudo apt update
sudo apt upgrade

И прилетает glibc, которая ломает все индексы.

peregrine ★★★★★
() автор топика
Ответ на: комментарий от peregrine

Ага, у нас из-за такого прдхода в базе было 4 разных обозначения удаленной записи - deleted, Deleted, Removed и erased.

PPP328 ★★★★★
()
Ответ на: комментарий от peregrine

Если докрутить, то можно в отдельную таблицу вынести и внешнюю ссылку использовать. Хотя зависит от задачи, конечно.

bbc69
()
Ответ на: комментарий от aol

эээ хз как бест практис у дедов и джунов(ждунов) НО:

чем плох вариант таблица номер - комментарий семантики

и тригер на добавленние на таблице используещей сей таблоенум для вылова деприкейтнутых значений?

qulinxao3 ★★
()
Ответ на: комментарий от qulinxao3

и тригер на добавленние на таблице

Меня учили, что любой триггер - зло. Если есть хоть какой-то шанс сделать без триггера - надо делать без. Только когда совсем, ну никак иначе нельзя.

Но он же говорит про

сравнение входного значения с константой енума

Вот это никак не доходит. Зачем тут обязательно енум нужен? У нас полно проц, которые работают по p_mode. И не факт, что только обычные `create`/`update`/etc, а еще и какое-нибудь внезапное `create_and_refresh_mv` (например).

Toxo2 ★★★★★
()
Ответ на: комментарий от qulinxao3

отвечу пачкой и для @Toxo2. Пример надуманный, но суть передаёт вполне.

CREATE TYPE user_role AS ENUM ('guest', 'member', 'moderator', 'admin');

CREATE OR REPLACE FUNCTION update_user_role(
    p_username TEXT,
    p_new_role user_role  -- ENUM input parameter
) 
RETURNS TEXT AS $$
DECLARE
    v_clearance_level INT;
BEGIN
    -- Evaluate the ENUM input using a CASE statement
    CASE p_new_role
        WHEN 'guest'     THEN v_clearance_level := 1;
        WHEN 'member'    THEN v_clearance_level := 2;
        WHEN 'moderator' THEN v_clearance_level := 3;
        WHEN 'odmen'     THEN v_clearance_level := 4;
    END CASE;
    -- ... итокдалие
END;
$$ LANGUAGE plpgsql;

При выполнении получим:

SQL Error [22P02]: ERROR: invalid input value for enum user_role: "odmen"

Каков объем пердолинга с синтетической таблией и представлять не нужно…

aol ★★★★★
()
Последнее исправление: aol (всего исправлений: 1)
Ответ на: комментарий от aol

Всё равно не доходит, чем это лучше чем руками:

CASE
...
ELSE
   RAISE 'Неверный параметр'
END
Один шут - процу будете править, если вдруг новая «роль» появится. А у вас - и процу, и ещё и енум править.

Toxo2 ★★★★★
()
Ответ на: комментарий от Toxo2

защита от опечаток в коде (ты ведь заметил эту опечатку в моем примере?). в рантайме, конечно, не в момент написания, но всё же.

Всё равно не доходит

до меня твой пример тоже. придется тебе его расширить, чтобы показать, как без енума тоже быть уверенным в корректности кода.

aol ★★★★★
()
Последнее исправление: aol (всего исправлений: 1)
Ответ на: комментарий от aol

А, всё, дошло.

Имеется в виду - в коде самих процедур. Защита от самого себя и коллег БДшников. Не от запрашивающей стороны. Хорошо.

Toxo2 ★★★★★
()

не удалить значение без миграции и даже там нужны костылики с пересозданием enum-а

Это обычно не то чтобы проблема и довольно редко нужно. Но мы в прошлых проектах в какой-то момент стали использовать строки с рефами на отдельные таблицы.

yorshka
()
  • Markdown
Пустая строка (два раза Enter) начинает новый абзац. Знак '>' в начале абзаца выделяет абзац курсивом цитирования.
Внимание: прочитайте описание разметки Markdown.
Используйте Ctrl-Enter для размещения комментария