PostgreSQL: Как пометить каждую таблицу в моей базе данных? Общие внешние ключи, столбец массива или другой метод
В настоящее время я разрабатываю базу данных PostgreSQL, в которой мне необходимо реализовать функциональность тегов. По сути, любой элемент из любой таблицы в моей базе данных может быть помечен с помощью тегов с ключевыми значениями, очень похожих на теги Azure.
Я рассматриваю различные подходы к реализации этой функции и нахожусь в замешательстве относительно лучшего подхода:
- Использование Generic Foreign Keys в Django:
Один из подходов, который я рассматриваю, заключается в использовании функции Generic Foreign Keys в Django для создания универсальной таблицы "Tags", которая может ссылаться на любую другую таблицу в базе данных. Это обеспечивает гибкость и позволяет избежать сложной логики. Однако могут возникнуть такие недостатки, как снижение производительности или база данных не будет иметь смысла без ORM Django. - Ручная реализация в одной таблице, без общих внешних ключей:
Другой вариант - реализовать таблицу "Tags" вручную , не полагаясь на Generic Foreign Keys в Django. Я бы создал отдельную таблицу, которая может ссылаться на любую другую таблицу в базе данных, но без использования формальных внешних ключей. Это потребует либо хранения некоторой логики в базе данных, либо в бэкенде, что будет сложнее в обслуживании. - Отдельная таблица перекрестных ссылок для каждой таблицы:
В качестве альтернативы я мог бы создать отдельную таблицу (например,car_tags
,house_tags
,person_tags
) для каждой таблицы в моей базе данных, для которой требуются теги. Такой подход обеспечит четкое разделение и потенциально лучшую производительность, но может привести к избыточности и усложнению запросов и обслуживания. - Добавление столбца
tags TEXT[]
массива в каждую таблицу:
Введение столбца
Какой подход наиболее подходит для данного случая использования? Меня больше волнует поддерживаемость и масштабируемость базы данных, а не производительность. Если эти варианты не подходят, вы можете предложить какие-либо лучшие практики для реализации гибкой системы тегов в PostgreSQL.
Django's
GenericForeignKey
are just weak, unenforced/unguarded references from db's perspective. TheTags
table would have two additional columns, one identifying what type of thing it's tagging, the other one holding a unique identifier of that thing.It shouldn't be too hard to make sense out of that, with or without Django ORM. It's actually how PostgreSQL manages some things internally:
pg_class.relkind
tells you what type of thing it's listing,pg_class.oid
identifies the specific thing. When you look things up in system views orinformation_schema
, it'll filter down to the specific type of thing listed inpg_class
and join to it onoid
.You can easily implement this on your own a similar way Django does:
create table tag ( id bigint generated by default as identity primary key, name text); create table tag_anything ( tag_id bigint references tag(id), other_thing_type text, other_thing_id text); create view cat_tagged as select cat.*,array_agg(tag.name) as tags from cat join tag_anything ta on ta.other_thing_type='cat' and ta.other_thing_id=cat.id join tag on tag.id=ta.tag_id; group by cat.id;
One problem is setting up the views and joins for those things, another that each thing can use a different, possibly multi-column primary key, which you need to map to the single generic foreign key reference. Some systems use
uuid
as the pk for everything, making each identifier of each thing, globally unique and uniform in type, which simplifies this.
You also need to implement your own mechanics to handleON UPDATE
,ON DELETE
behaviour as well as the link validation to avoid consistency loss.That's the normalized approach. If you think about what Django does with the
GenericForeignKey
and what you may try to reimplement, this is just a single table-per-thing more than that:tag_anything
from the example above becomestag_cat
, loses thething_type
column and gets a proper foreign key reference, with adequate type.You get the
on update
,on delete
and consistency checks for free.Adding a
text[]
or ajsonb
everywhere sounds like a nightmare. You'd duplicate tags all over the place and weigh every tagged thing down. Querying based on those would have to hit every tagged table in the db.