Postgresql只加入最特定的cidr匹配

Postgresql只加入最特定的cidr匹配,postgresql,join,cidr,Postgresql,Join,Cidr,我有一个test_networks表,它是一个网络列表,其中包含关于每个网络的内容和位置的描述 CREATE TABLE test_networks ( id serial PRIMARY KEY, address cidr, description text ); 字段地址将为以下任意一种: 10.0.0.0/8 10.1.0.0/16 10.1.1.0/24 10.2.0.0/16 10.3.0.0/16 10.3.1.0/24 10.3.2.0/24 10.3.3.0/24

我有一个test_networks表,它是一个网络列表,其中包含关于每个网络的内容和位置的描述

CREATE TABLE test_networks
(
  id serial PRIMARY KEY,
  address cidr,
  description text
);
字段地址将为以下任意一种:

10.0.0.0/8 10.1.0.0/16 10.1.1.0/24 10.2.0.0/16 10.3.0.0/16 10.3.1.0/24 10.3.2.0/24 10.3.3.0/24 10.15.1.0/24 10.15.2.0/24 10.15.3.0/24 我还有一个test_systems表,其中包含系统及其属性的列表我还有一些属性,但这些属性是不相关的:

CREATE TABLE test_systems
(
  id serial PRIMARY KEY,
  address inet,
  owner text
);
假设我的系统具有以下地址:

10.1.1.1 10.2.0.1 我想创建所有系统及其最近网络描述的报告,如果找不到网络,则创建空描述。如您所见,10.1.1.1匹配多个网络,因此我只想列出最具体的网络,即每个系统具有最高掩码的网络。示例输出为:

  hostaddr |   netaddr   |  description
 ----------+-------------+----------------
  10.1.1.1 | 10.1.1.0/24 | third network
  10.2.0.1 | 10.2.0.0/16 | 4th network
我尝试使用此查询:

SELECT s.address AS hostaddr, n.address AS netaddr, n.description AS description
FROM test_systems s
LEFT JOIN test_networks n
ON s.address << n.address;

有人知道我如何只查询每个系统最特定的网络吗?

您正在查找组查询中的前n个,在本例中n=1。您可以使用“行数”窗口功能执行此操作:

SELECT x.hostaddr, x.netaddr, x.description FROM (
  SELECT
      s.address AS hostaddr,
      n.address AS netaddr,
      n.description AS description,
      row_number() OVER (
          PARTITION BY s.address
          ORDER BY masklen(n.address) DESC
      ) AS row
  FROM test_systems s
  LEFT JOIN test_networks n
  ON s.address << n.address
) x
WHERE x.row = 1;

仅供参考,主键表示不为空。您不需要为主键指定NOTNULL。这正是我所需要的。我花了一整天的时间试图自己解决这个问题,但失败了。想解释一下这里发生了什么吗?@invictus如果您将x.row添加到外部查询选择的列中,应该会更明显一些。窗口函数row_number从1开始对结果集中的所有行进行编号。按s.address分区意味着s.address的每个不同值都有自己的行号集,从1开始,而ORDER BY子句意味着具有最大masklenn.address的值将接收1。因此,我们只需从中筛选出行数不为1的行,这就是外部选择所做的。酷。是否可以使用任何特定的索引来加快速度?NetAddress上的索引可能会加快排序速度,但如果没有大型数据集进行测试,我不知道。对于我使用的小数据集,计划者总是会决定序列扫描是最好的选择。谢谢您的输入。与上面提供的解决方案相比,此解决方案的优点/缺点是什么?@invitus我认为它更简单。在这两个上运行explain analize并检查哪一个最快。
SELECT x.hostaddr, x.netaddr, x.description FROM (
  SELECT
      s.address AS hostaddr,
      n.address AS netaddr,
      n.description AS description,
      row_number() OVER (
          PARTITION BY s.address
          ORDER BY masklen(n.address) DESC
      ) AS row
  FROM test_systems s
  LEFT JOIN test_networks n
  ON s.address << n.address
) x
WHERE x.row = 1;
SELECT distinct on (s.address)
    s.address AS hostaddr,
    n.address AS netaddr,
    n.description AS description
FROM
    test_systems s
    LEFT JOIN
    test_networks n ON s.address << n.address
order by s.address, masklen(n.address) desc