Postgresql inet 查找重复/重叠的网络条目

问题描述 投票:0回答:2

我有以下用于存储 IP 的表结构(PostgreSQL 11.14):

CREATE TABLE ips (
  ip INET 
);

INSERT INTO ips VALUES ('10.0.0.4');
INSERT INTO ips VALUES ('10.0.0.0/24');
INSERT INTO ips VALUES ('10.1.0.0/23');
INSERT INTO ips VALUES ('10.1.0.0/27');

我需要知道哪个网络范围重复才能找到重叠的网络条目。

sql postgresql inet
2个回答
1
投票

要检测已经存在的重叠,您可以使用

inet <<= inet → boolean
,类似于 @a_horse_with_no_name 建议演示

CREATE TABLE ips (
  id SERIAL PRIMARY KEY,
  ip INET        );

INSERT INTO ips (ip) 
VALUES  ('10.0.0.4'),
        ('10.0.0.0/24'),
        ('10.1.0.0/23'),
        ('10.1.0.0/27');
CREATE INDEX ON ips USING gist(ip inet_ops,id);

SELECT 
  a.id AS id1, 
  a.ip AS ip1,
  b.id AS id2,
  b.ip AS ip2 
FROM         ips AS a 
  INNER JOIN ips AS b 
    ON a.ip <<= b.ip 
    AND a.id<>b.id;

-- id1 |     ip1     | id2 |     ip2
-------+-------------+-----+-------------
--   1 | 10.0.0.4    |   2 | 10.0.0.0/24
--   4 | 10.1.0.0/27 |   3 | 10.1.0.0/23
--(2 rows)

如果您希望防止插入重叠,您可以在该表的排除约束

中使用可交换的
inet && inet → boolean运算符demo

CREATE TABLE ips (
  ip INET,
  CONSTRAINT no_ip_overlaps EXCLUDE USING gist (ip inet_ops WITH &&));

INSERT INTO ips (ip) 
VALUES ('10.0.0.4'),
       ('10.1.0.0/27');
-- You can let the unhandled conflict throw an error
INSERT INTO ips (ip) VALUES ('10.0.0.0/24');
--ERROR:  conflicting key value violates exclusion constraint "no_ip_overlaps"
--DETAIL:  Key (ip)=(10.0.0.0/24) conflicts with existing key (ip)=(10.0.0.4).

您可以决定在冲突出现时处理它们,要么忽略它们,要么有选择性地处理:

INSERT INTO ips (ip) VALUES ('10.0.0.0/24')
  ON CONFLICT ON CONSTRAINT no_ip_overlaps DO NOTHING;

--You might one day decide to keep the bigger network in the overlapping pair: 
--right now, only 'do nothing' is supported for conflicts on exclusion constraints
INSERT INTO ips (ip) VALUES ('10.1.0.0/23') 
  ON CONFLICT ON CONSTRAINT no_ip_overlaps DO UPDATE 
    SET ip=CASE WHEN ips.ip<<excluded.ip THEN excluded.ip ELSE ips.ip END;
--ERROR:  ON CONFLICT DO UPDATE not supported with exclusion constraints

--Until that day you can revert to a MERGE in place of your INSERT
MERGE INTO ips AS present
USING (SELECT '10.1.0.0/23'::inet AS ip) AS incoming 
  ON (present.ip << incoming.ip)
WHEN MATCHED THEN UPDATE 
  SET ip=incoming.ip
WHEN NOT MATCHED THEN 
  INSERT (ip)
  VALUES (incoming.ip);

由于

MERGE
最近才被添加到 PostgreSQL 15,在早期版本中,您可以通过 PL/pgSQL upsert 来完成。


0
投票

我们可以在这里使用

SUBSTRING()
以及正则表达式模式:

WITH cte AS (
    SELECT *, COUNT(*) OVER (PARTITION BY SUBSTRING(ip::text FROM '[^/]+')) cnt
    FROM ips
)

SELECT *
FROM cte
WHERE cnt > 1;
© www.soinside.com 2019 - 2024. All rights reserved.