0

在 PostgreSQL 数据库中,我有 table blocks,它是从 GeoLite2-City-Blocks.csv 导入的,具有以下结构:

network_start_ip cidr NOT NULL,
network_mask_length integer NOT NULL,
geoname_id bigint,
registered_country_geoname_id bigint,
represented_country_geoname_id bigint,
postal_code character(50),
latitude DOUBLE PRECISION,
longitude DOUBLE PRECISION,
is_anonymous_proxy boolean,
is_satellite_provider boolean

存储 IP 地址使用 CIDR 数据类型,但在 GeoLite2-City-Blocks.csv 中只有 START_IP_ADDRESS 和 MASK_LENGTH。

此表中的数据示例:

::ffff:1.0.0.0/128   120    2077456     2077456  ....

如何选择包含我的 IP 地址的行,例如 87.197.148.121 ?是否需要计算 END_IP_ADDRESS 来分隔列?

4

1 回答 1

0

您将需要对 inet/cidr 使用 contains 运算符:

select * from blocks
where set_masklen(network_start_ip,network_mask_length) >>= '::ffff:87.197.148.121'::inet;

有几个问题。首先,数据表示已经将网络和掩码分成了不同的字段,所以这个查询会非常低效。这两个字段可以简单地组合起来,例如:

alter table blocks add column net_ip inet;

然后会有一次数据迁移:

update blocks set net_ip = set_masklen(network_start_ip,network_mask_length);

那么上面的查询会更容易理解:

select * from blocks where net_ip >>= '87.197.148.121'::inet;

不过我还是觉得这个查询行不通,因为net_ip数据是用ipv6表示的,比较是用ip4v的。您可以编写一个转换函数,或者:

select * from blocks where net_ip >>= concat('::ffff:', '87.197.148.121')::inet;

这根本不是最优的。net_ip 未编入索引。为此,您需要 ip4r 扩展用于 postgres。

于 2014-06-17T13:52:46.310 回答