Lar*_*row 19 arrays postgresql database-design ldap unique-constraint
我正在尝试为当前在LDAP存储中的主机数据提出PostgreSQL架构.部分数据是机器可以拥有的主机名列表,该属性通常是大多数人用来查找主机记录的关键.
我想将这些数据移动到RDBMS的一件事是能够在hostname列上设置唯一性约束,以便无法分配重复的主机名.如果主机只能有一个名称,这将很容易,但由于它们可以有多个名称,因此它更复杂.
我意识到这样做的完全规范化的方法是让一个主机名表的外键指向hosts表,但是我想避免让每个人都需要为最简单的查询做连接:
select hostnames.name,hosts.*
from hostnames,hosts
where hostnames.name = 'foobar'
and hostnames.host_id = hosts.id;
Run Code Online (Sandbox Code Playgroud)
我认为使用PostgreSQL数组可以为此工作,它们肯定使简单的查询变得简单:
select * from hosts where names @> '{foobar}';
Run Code Online (Sandbox Code Playgroud)
但是,当我在hostnames属性上设置唯一性约束时,它当然会将整个名称列表视为唯一值而不是每个名称.有没有办法让每个名称在每一行都是唯一的?
如果没有,有没有人知道另一种更有意义的数据建模方法?
Erw*_*ter 26
您可能想重新考虑规范化架构.每个人都没有必要"加入即使是最简单的查询".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
);
Run Code Online (Sandbox Code Playgroud)
代理主键hostname_id是可选的.我更喜欢有一个.在你的情况下hostname可能是主键.但是,使用简单的小integer密钥,许多操作速度更快.创建外键约束以链接到表host.
创建一个这样的视图:
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+
Run Code Online (Sandbox Code Playgroud)
从第9.1页开始,主键GROUP BY覆盖SELECT列表中该表的所有列.版本9.1的发行说明:
GROUP BY在GROUP BY子句中指定主键时,允许查询目标列表中的非列
查询可以像表一样使用视图.搜索一个域名将是多快是这样的:
SELECT *
FROM host h
JOIN hostname hn USING (host_id)
WHERE hn.hostname = 'foobar';
Run Code Online (Sandbox Code Playgroud)
如果您有索引host(host_id),应该是主键,因为它应该是主键.另外,UNIQUE约束会hostname(hostname)自动实现其他所需的索引.
在Postgres 9.2+中,如果您可以从中获取仅索引扫描,那么多列索引会更好:
CREATE INDEX hn_multi_idx ON hostname (hostname, host_id);
Run Code Online (Sandbox Code Playgroud)
从Postgres 9.3开始,您可以在MATERIALIZED VIEW情况允许的情况下使用.特别是如果你读的频率比你写的那么多.
如果我不能说服你正确的道路,我也会在黑暗的一面帮助你.我很灵活.:)
这是一个如何强制主机名唯一性的演示.我使用表hostname来收集主机名和表上的触发器host以使其保持最新.唯一违规会引发错误并中止操作.
CREATE TABLE host(hostnames text[]);
CREATE TABLE hostname(hostname text PRIMARY KEY); -- pk enforces uniqueness
Run Code Online (Sandbox Code Playgroud)
触发功能
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;
Run Code Online (Sandbox Code Playgroud)
触发:
CREATE TRIGGER host_insupdelbef
BEFORE INSERT OR DELETE OR UPDATE OF hostnames ON host
FOR EACH ROW EXECUTE PROCEDURE trg_host_insupdelbef();
Run Code Online (Sandbox Code Playgroud)
SQL小提琴测试运行.
在数组列和数组运算符上使用GIN索引来处理它:host.hostnames
如果有人仍然需要原始问题中的内容:
CREATE TABLE testtable(
id serial PRIMARY KEY,
refs integer[],
EXCLUDE USING gist( refs WITH && )
);
INSERT INTO testtable( refs ) VALUES( ARRAY[100,200] );
INSERT INTO testtable( refs ) VALUES( ARRAY[200,300] );
Run Code Online (Sandbox Code Playgroud)
这会给你:
ERROR: conflicting key value violates exclusion constraint "testtable_refs_excl"
DETAIL: Key (refs)=({200,300}) conflicts with existing key (refs)=({100,200}).
Run Code Online (Sandbox Code Playgroud)
在Windows上查看Postgres 9.5.
请注意,这将使用运算符创建索引&&.因此,当您使用时testtable,检查速度会ARRAY[x] && refs比x = ANY( refs )Postgres的内部索引更快.
PS一般我同意上面的答案,但这种方法只是一个不错的选择,当你不必真正关心性能和东西.
| 归档时间: |
|
| 查看次数: |
6956 次 |
| 最近记录: |