postgresql-cidr查找子块



我正在寻找一种方法,使用内置的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')

所需查询应为

  • searchkey 10.1.0.0/16返回10.1.0.0/20
  • 10.1.1.8/29和10.1.1.32/28用于搜索键10.1.1.0/24

我想到的是(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

这会得到所需的结果,但不会按比例缩放。即使数据库中只有几千个条目,这也会变得非常缓慢。

有没有其他方法可以实现这一点,而不向数据库模型添加显式的父/子关系?

更新:这是一个变体,从嵌套集模型转换而来,它可能会更快(主要在9.4+上使用inet_ops GiST索引):

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_opsGiST索引:

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))

最新更新