存储和比较独特的组合
Storing and comparing unique combinations
我需要网站上的搜索功能,您应该能够 select 多个类别。搜索将存储在数据库中,但搜索参数的每个唯一组合只能存储一次,这也包括 selected 类别的唯一组合。
问题是我不知道如何存储 selected 类别的组合。我查看了数组并发现了这个 http://blog.2ndquadrant.com/postgresql-9-3-development-array-element-foreign-keys/ 但显然该功能从未实现过。
所以如果我需要使用多个 tables 我想我可以有一个 table 用于组合,每个唯一组合都有一个 id,可以很容易地引用和比较然后另一个 table 将类别链接到组合。但是使用这种方法我如何检查组合是否已经存在?我能想到的唯一方法是遍历所有现有组合并检查其中是否有任何一个等于比较组合。
我觉得这不是一个罕见的问题,但我找不到任何这样的例子。我也觉得我的方法可能不是最好的。非常欢迎任何建议。
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
对于您当前的方法,您可以使用 string_agg
在每个 CategoriesCombinations
中获得所有 Categories
的字符串表示形式,并根据新搜索进行检查:
SELECT CombinationId
FROM CombinedCategories
WHERE string_agg(CategoryId, ',') = '84,95,102'
GROUP BY CombinationId
但更简单的方法是根据所有参数为每个搜索计算一个唯一的哈希值,并将其存储在 Searches
table 中,并将新搜索的哈希值与搜索历史记录进行比较。
存储为数组(非规范化)
我会考虑附加模块 intarray,它提供方便(快速)的功能 uniq()
和 sort()
。在典型的现代 Postgres 安装中,它非常简单:
CREATE EXTENSION intarray;
- How to create an index for elements of an array in PostgreSQL?
使用这些,一个简单的 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是可选的,还有其他方式:
- Ordering distinct column values by (first value of) other column in aggregate function
但是 intarray 函数提供了卓越的性能。
然后你可以在数组列上创建一个UNIQUE
约束来强制整个数组的唯一性。
UNIQUE (cat_arr)
我在两天前的相关回答中写了更多关于将(非常严格和可靠的)约束与(不太可靠但更方便的)触发器相结合的优点:
如果对于每个组合,每个类别您需要存储的只是 ID(没有其他信息),这就足够了。
但是,这种方式不容易确保参照完整性。数组元素没有外键约束(还)——比如 documented in your link:如果删除其中一个类别或更改 ID,引用将中断 ...
规范化架构
如果您需要存储更多,或者您更愿意使用规范化模式来强制执行参照完整性或出于某种原因,您也可以这样做,并添加一个触发器来填充手工制作的物化视图 (冗余 table) 并以类似的方式强制唯一性:
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)
);
演示触发器的相关答案(不是针对唯一组合,而是针对唯一元素):
- Can PostgreSQL have a uniqueness constraint on array elements?
我需要网站上的搜索功能,您应该能够 select 多个类别。搜索将存储在数据库中,但搜索参数的每个唯一组合只能存储一次,这也包括 selected 类别的唯一组合。
问题是我不知道如何存储 selected 类别的组合。我查看了数组并发现了这个 http://blog.2ndquadrant.com/postgresql-9-3-development-array-element-foreign-keys/ 但显然该功能从未实现过。
所以如果我需要使用多个 tables 我想我可以有一个 table 用于组合,每个唯一组合都有一个 id,可以很容易地引用和比较然后另一个 table 将类别链接到组合。但是使用这种方法我如何检查组合是否已经存在?我能想到的唯一方法是遍历所有现有组合并检查其中是否有任何一个等于比较组合。
我觉得这不是一个罕见的问题,但我找不到任何这样的例子。我也觉得我的方法可能不是最好的。非常欢迎任何建议。
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
对于您当前的方法,您可以使用 string_agg
在每个 CategoriesCombinations
中获得所有 Categories
的字符串表示形式,并根据新搜索进行检查:
SELECT CombinationId
FROM CombinedCategories
WHERE string_agg(CategoryId, ',') = '84,95,102'
GROUP BY CombinationId
但更简单的方法是根据所有参数为每个搜索计算一个唯一的哈希值,并将其存储在 Searches
table 中,并将新搜索的哈希值与搜索历史记录进行比较。
存储为数组(非规范化)
我会考虑附加模块 intarray,它提供方便(快速)的功能 uniq()
和 sort()
。在典型的现代 Postgres 安装中,它非常简单:
CREATE EXTENSION intarray;
- How to create an index for elements of an array in PostgreSQL?
使用这些,一个简单的 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是可选的,还有其他方式:
- Ordering distinct column values by (first value of) other column in aggregate function
但是 intarray 函数提供了卓越的性能。
然后你可以在数组列上创建一个UNIQUE
约束来强制整个数组的唯一性。
UNIQUE (cat_arr)
我在两天前的相关回答中写了更多关于将(非常严格和可靠的)约束与(不太可靠但更方便的)触发器相结合的优点:
如果对于每个组合,每个类别您需要存储的只是 ID(没有其他信息),这就足够了。
但是,这种方式不容易确保参照完整性。数组元素没有外键约束(还)——比如 documented in your link:如果删除其中一个类别或更改 ID,引用将中断 ...
规范化架构
如果您需要存储更多,或者您更愿意使用规范化模式来强制执行参照完整性或出于某种原因,您也可以这样做,并添加一个触发器来填充手工制作的物化视图 (冗余 table) 并以类似的方式强制唯一性:
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)
);
演示触发器的相关答案(不是针对唯一组合,而是针对唯一元素):
- Can PostgreSQL have a uniqueness constraint on array elements?