sql - 存储和比较独特的组合

标签 sql arrays postgresql database-design unique-constraint

我需要网站上的搜索功能,除其他外,您应该能够选择多个类别。搜索将存储在数据库中,但搜索参数的每个唯一组合只能存储一次,这也包括所选类别的唯一组合。

问题是我不知道如何存储所选类别的组合。我查看了数组并找到了这个 http://blog.2ndquadrant.com/postgresql-9-3-development-array-element-foreign-keys/但显然该功能从未实现。

因此,如果我需要使用多个表,我想我可以为组合创建一个表,每个组合都有一个 id,可以轻松引用和比较,然后另一个表将类别链接到组合。但是使用这种方法我如何检查组合是否已经存在?我能想到的唯一方法是遍历所有现有组合并检查其中是否有任何一个等于比较组合。

我觉得这不是一个不常见的问题,但我找不到任何这样的例子。我也觉得我的方法可能不是最好的。非常欢迎任何建议。

I have these two tables currently:

Categories
- CategoryId (int)
- Name (string)

Searches
- SearchId (int)
- Keywords (string)
- ExampleOption1 (bool)
- ExampleOption2 (bool)
- CategoriesCombinationId (int) -- this would represent the unique combination of categories and links to the combination table

这就是我可能会尝试解决问题的方法(如果有检查组合是否已存在的好方法):

CategoriesCombinations -- unique combinations
- CombinationId (int)

CombinedCategories
- CombinationId (int) -- links to id in combinations table
- CategoryId (int) -- links to id in categories table

最佳答案

存储为数组(非规范化)

我会考虑附加模块 intarray它提供了方便(快速)的函数 uniq()sort()。在典型的现代 Postgres 安装中,它非常简单:

CREATE EXTENSION intarray;

使用这些,一个简单的CHECK 约束可以强制使用distinct 元素的ascending 数组。

CHECK (uniq(sort(cat_arr)) = cat_arr)

您可以另外(可选)有一个触发器自动规范化数组值ON INSERT OR UPDATE。然后你可以传递 any 数组(可能是未排序的并且有欺骗)并且一切正常。喜欢:

CREATE OR REPLACE FUNCTION trg_search_insup_bef()
  RETURNS trigger AS
$func$
BEGIN
   NEW.cat_arr := uniq(sort(NEW.cat_arr);
   RETURN NEW;
END
$func$ LANGUAGE plpgsql;

CREATE TRIGGER insup_bef
BEFORE INSERT OR UPDATE OF cat_arr ON search
FOR EACH ROW
EXECUTE PROCEDURE trg_search_insup_bef();

附加模块intarray是可选的,还有其他方式:

但是 intarray 函数提供了卓越的性能。

然后您可以在数组列上创建一个UNIQUE 约束,以强制整个数组的唯一性。

UNIQUE (cat_arr)

我在两天前的这个相关回答中写了更多关于将(非常严格和可靠的)约束与(不太可靠但更方便的)触发器相结合的优势:

如果对于每个组合,每个类别您需要存储的只是 ID(没有其他信息),这就足够了。
但是,这种方式并不容易确保参照完整性。数组元素(还)没有外键约束——比如 documented in your link : 如果其中一个类别被删除或您更改 ID,引用将中断 ...

规范化模式

如果您需要存储更多,或者您更愿意使用规范化模式来强制执行参照完整性或出于某种原因,您也可以这样做,并添加一个触发器来填充一个手工制作的物化 View (一个冗余的表)并以类似的方式强制唯一性:

CREATE TABLE search (
  search_id serial PRIMARY KEY
, ... more columns
);

CREATE TABLE cat (
  cat_id serial PRIMARY KEY
, cat text NOT NULL
);

CREATE TABLE search_cat (
  search_id int REFERENCES search ON DELETE CASCADE
, cat_id    int REFERENCES cat
, PRIMARY KEY (search_id, cat_id)
);

演示触发器的相关答案(不是针对唯一组合,而是针对唯一元素):

关于sql - 存储和比较独特的组合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29732650/

相关文章:

java - 如何相对于彼此对两个数组进行排序。

java - 将 JSON 字符串转换为 Java/Python (Jython) 对象?

javascript - 在 JavaScript 中进行除法运算时,如何将 NaN 替换为 0?

python - PostgreSQL NUMERIC 数据类型的 Django 模型字段

sql - 表声明中的列别名

sql - 在 Postgresql 中删除生成的 json/jsonb 上的空对象

sql - 使用 Google bigquery 的日期中的两个日期之间的区别?

mysql - 插入 MySQL 表或更新(如果存在)

Laravel firstOrCreate 方法抛出重复 ID 错误

Django Admin 搜索优化