微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

数组 – PostgreSQL可以对数组元素有唯一约束吗?

我试图提出一个目前在LDAP存储中的主机数据的Postgresql架构.该数据的一部分是机器可以具有的主机名列表,该属性通常是大多数人用来查找主机记录的关键.

有一件事我想将这些数据移到RDBMS上是能够在主机名列上设置唯一性约束,以便不能分配重复的主机名.如果主机只能有一个名字,那么这将很容易,但是由于它们可以有多个名称,因此更复杂.

我意识到,完全正常化的方法是将一个主机名表与一个外键指向主机表,但我想避免每个人都需要连接,即使是最简单的查询

select hostnames.name,hosts.*
  from hostnames,hosts
 where hostnames.name = 'foobar'
   and hostnames.host_id = hosts.id;

我认为使用Postgresql数组可以为此工作,他们肯定使简单的查询简单:

select * from hosts where names @> '{foobar}';

当我在hostnames属性上设置唯一性约束时,它当然将整个名称列表视为唯一值而不是每个名称.有没有办法使每个名称在每一行都独一无二?

如果没有,有没有人知道另一种数据建模方法会更有意义?

正义之路

您可能需要重新考虑规范化您的模式.每个人都不需要“加入即使是最简单的查询”.为此创建一个VIEW.

表格可能如下所示:

CREATE TABLE hostname (
 hostname_id serial PRIMARY KEY,host_id     int    REFERENCES host(host_id) ON UPDATE CASCADE ON DELETE CASCADE,hostname    text   UNIQUE
);

代理主键hostname_id是可选的.我喜欢有一个.在你的情况下,主机名可以是主键.但是,使用简单的小整数键,许多操作更快.创建外键约束以链接到表主机.
创建如下视图:

CREATE VIEW v_host AS
SELECT h.*,array_agg(hn.hostname) AS hostnames
--,string_agg(hn.hostname,',') AS hostnames  -- text instead of array
FROM   host h
JOIN   hostname hn USING (host_id)
GROUP  BY h.host_id;   -- works in v9.1+

从第9.1开始,GROUP BY中的主键覆盖SELECT列表中该表的所有列. release notes for version 9.1

Allow non-GROUP BY columns in the query target list when the primary
key is specified in the GROUP BY clause

查询可以像桌面一样使用视图.通过这种方式搜索主机名将会更快:

SELECT *
FROM   host h
JOIN   hostname hn USING (host_id)
WHERE  hn.hostname = 'foobar';

如果您在主机(host_id)上有一个索引,应该是主键.此外,主机名(hostname)上的UNIQUE约束自动实现其他需要的索引.

在Postgres 9.2中,如果您可以获得一个index-only scan,多列索引将会更好:

CREATE INDEX hn_multi_idx ON hostname (hostname,host_id)

从Postgres 9.3开始,您可以在情况允许的情况下使用MATERIALIZED VIEW.特别是如果你阅读的次数比你写的更多.

黑暗的一面(你真正问的是什么)

如果我不能说服你的正义之路,我也会在黑暗的一面协助.我很灵活:)

这是演示如何强制主机名的唯一性.我使用表主机名来收集主机名和表主机上的触发器来保持最新.唯一的违规行为引发错误并中止操作.

CREATE TABLE host(hostnames text[]);
CREATE TABLE hostname(hostname text PRIMARY KEY);  --  pk enforces uniqueness

触发功能

CREATE OR REPLACE FUNCTION trg_host_insupdelbef()
  RETURNS trigger AS
$func$
BEGIN
-- split UPDATE into DELETE & INSERT
IF TG_OP = 'UPDATE' THEN
   IF OLD.hostnames IS disTINCT FROM NEW.hostnames THEN  -- keep going
   ELSE RETURN NEW;  -- exit,nothing to do
   END IF;
END IF;

IF TG_OP IN ('DELETE','UPDATE') THEN
   DELETE FROM hostname h
   USING  unnest(OLD.hostnames) d(x)
   WHERE  h.hostname = d.x;

   IF TG_OP = 'DELETE' THEN RETURN OLD;  -- exit,we are done
   END IF;
END IF;

-- control only reaches here for INSERT or UPDATE (with actual changes)
INSERT INTO hostname(hostname)
SELECT h
FROM   unnest(NEW.hostnames) h;

RETURN NEW;
END
$func$LANGUAGE plpgsql;

触发:

CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();

SQL Fiddle带测试运行.

在数组列host.hostnames和array operators上使用GIN索引来处理它:

> Why isn’t my PostgreSQL array index getting used (Rails 4)?
> Check if any of a given array of values are present in a Postgres array

原文地址:https://www.jb51.cc/postgresql/192550.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐