2

我有一个表“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 匹配多个网络,因此我只想为每个系统列出最具体的一个(即具有最高 masklen() 的那个)。示例输出为:

  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;

但是,这会给我一个所有系统+网络对的列表,例如:

 hostaddr |   netaddr   |  description
----------+-------------+----------------
 10.1.1.1 | 10.0.0.0/8  | first network
 10.1.1.1 | 10.1.0.0/16 | second network
 10.1.1.1 | 10.1.1.0/24 | third network
 10.2.0.1 | 10.0.0.0/8  | first network
 10.2.0.1 | 10.2.0.0/16 | 4th network

有谁知道我如何只查询每个系统的最具体的网络?

4

2 回答 2

6

您正在寻找“组中前 n 个”查询,在这种情况下,n = 1。您可以使用row_number()窗口函数执行此操作:

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;
于 2013-04-23T14:45:21.397 回答
2
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
于 2013-04-23T15:09:55.650 回答