我正在寻找一种方法,使用内置cidr类型从postgresql中存储的cidr块获取直接子网络。
示例数据库
CREATE TABLE nets (
id serial primary key,
net cidr
);
INSERT INTO nets (net) VALUES
('10.1.0.0/16'),
('10.1.0.0/20'),
('10.1.1.0/24'),
('10.1.1.8/29'),
('10.1.1.32/28'),
('10.2.15.0/24'),
('10.2.15.64/27')
所需的查询应该是f.e.
我想出的是(http://sqlfiddle.com/#!15/2b4b5/1):
SELECT
id,
net
FROM
nets n
WHERE
net << '10.1.1.0/24' AND
'10.1.1.0/24' IN (
SELECT
net
FROM
nets
WHERE
net >> n.net
ORDER BY
net DESC
LIMIT 1
)
ORDER BY
net
这给出了期望的结果,但它没有缩放。即使数据库中只有几千个条目,这也非常慢。
是否有其他方法可以实现此目的,而无需向数据库模型添加明确的父/子关系?
答案 0 :(得分:2)
更新:这是一个变体,从Nested set model转换而来,它可能更快(主要是使用inet_ops
GiST索引的9.4+):
SELECT c.id, c.net
FROM nets c
WHERE c.net << '10.1.1.0/24'
AND NOT EXISTS(
SELECT 1
FROM nets AS m
WHERE c.net << m.net AND m.net << '10.1.1.0/24'
);
原始答案:
简单的EXCEPT
应该使用更大的输入集更好地扩展(它不会为每个子网络计算子计划):
(SELECT id, net
FROM nets
WHERE net << '10.1.1.0/24')
EXCEPT
(SELECT c.id, c.net
FROM nets p
JOIN nets c ON c.net << p.net
WHERE p.net << '10.1.1.0/24')
ORDER BY net;
注意:对于较小的输入集,EXCEPT
变体可能比您的查询更慢。
但为了最大限度地提高性能(使用此选项和使用您的查询),您应该使用一些索引。
如果你有PostgreSQL 9.4+,你应该使用新的inet_ops
GiST索引:
CREATE INDEX nets_inet_net_gist ON nets USING gist (inet(net) inet_ops);
否则,您可以使用network_ops
btree索引:
CREATE INDEX nets_inet_net_btree ON nets USING btree (inet(net) network_ops);
虽然inet_ops
可以直接使用<<
运算符,network_ops
会将您的表达式转换为类似的结果:
Index Cond: (((net)::inet > '10.1.1.0/24'::inet) AND ((net)::inet <= '10.1.1.255'::inet))