获取未安装软件包的主机数量

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

我想在以 Postgres 作为数据源的 Grafana 仪表板中显示未安装给定软件包的主机/服务器的数量(例如:“abc”)。

Grafana 中显示的存在“abc”包的单个服务器的示例表:

主机名 套餐 版本 服务器上安装的软件包
服务器1 abc 10.0.1 真实
服务器1 abc 10.0.2
服务器1 套餐2 3.1 真实
服务器1 套餐3 4.1.1 真实
服务器2 套餐2 3.1 真实
服务器2 套餐3 4.1.1 真实
服务器2 套餐4 10.0.1 真实
服务器3 套餐2 3.1 真实
服务器3 套餐3 4.1.1 真实

例如,我有 10,000 台服务器,其中 400 台服务器中未安装软件包“abc”。然后 Grafana 应该显示不存在“abc”版本的服务器的数量。

这是我尝试过的。对于上面的示例,输出应为 2,其中包 abc 不存在。但我没有得到预期的输出(或 2)。

SELECT count(*) 
FROM (
     SELECT DISTINCT hostname     
     FROM scanner_table     
     GROUP BY hostname     
     HAVING COUNT(CASE WHEN package = 'abc' THEN 1 END) = 0 
) AS host_without_abc;

如何解决这个问题?

sql postgresql greatest-n-per-group
2个回答
0
投票

简单,幼稚,缓慢,第一个实现 - 计算所有主机 - 计算所有带 abc 包的主机 = 不带 abc 包的主机:

SELECT COUNT(*)
FROM (
  (
    SELECT DISTINCT hostname  
    FROM scanner_table
  ) 
  EXCEPT
  (
    SELECT DISTINCT hostname  
    FROM scanner_table     
    WHERE package = 'abc'
  ) 
)

更成熟的解决方案:与选定的包和计数主机名相同的主机名进行左连接,其中连接为空/空:

SELECT COUNT(DISTINCT a.hostname) 
FROM scanner_table AS a
LEFT JOIN scanner_table AS b
  ON a.hostname=b.hostname AND b.package = 'abc'
WHERE b.hostname IS NULL

0
投票

根据未公开的细节,一个或另一个查询更简单/更快。

设置

CREATE TABLE scanner_table (
  hostname text NOT NULL  -- !
, package text NOT NULL  -- !
, version text
, package_installed_on_server bool
);

INSERT INTO scanner_table VALUES
  ('server1', 'abc'     , '10.0.1', true)
, ('server1', 'abc'     , '10.0.2', false)
, ('server1', 'package2', '3.1'   , true)
, ('server1', 'package3', '4.1.1' , true)
, ('server2', 'package2', '3.1'   , true)
, ('server2', 'package3', '4.1.1' , true)
, ('server2', 'package4', '10.0.1', true)
, ('Server3', 'package2', '3.1'   , true)
, ('Server3', 'package3', '4.1.1' , true)
;

这个多列索引有助于以下所有查询(如果表足够真空),有些多一些,有些少;

CREATE INDEX scanner_table_hostname_package_idx ON scanner_table (hostname, package);

应该有一个

hostname
表,每个相关主机一行。 如果您不这样做,请考虑创建一个:

CREATE TABLE hostname (hostname text PRIMARY KEY);
INSERT INTO hostname VALUES
  ('server1')
, ('server2')
, ('Server3')
;

查询

如果表

hostname
存在则最快。 (总体来说最快。)

SELECT count(*)
FROM  hostname h
WHERE NOT EXISTS (
   SELECT FROM scanner_table s
   WHERE  s.hostname = h.hostname
   AND    s.package = 'abc'
   );

没有表

hostname
最快,每个主机名只有很少行。
与您尝试过的类似(实际上应该有效,但效率低下)。

SELECT count(*)
FROM  (
   SELECT FROM scanner_table  -- SELECT list can stay empty
   GROUP  BY hostname
   HAVING bool_and(package <> 'abc') -- assuming column package defined NOT NULL
   ) sub;

模拟索引跳过扫描速度最快,无需表

hostname
和每个主机名有 many 行。
要求索引速度快。参见:

WITH RECURSIVE cte AS (
   (
   SELECT hostname, package
   FROM   scanner_table s
   ORDER  BY hostname, package <> 'abc'
   LIMIT  1
   )

   UNION ALL
   (
   SELECT s.hostname, s.package
   FROM   cte c
   JOIN   scanner_table s ON s.hostname > c.hostname
   ORDER  BY s.hostname, s.package <> 'abc'
   LIMIT  1
   )
   )
SELECT count(*)
FROM   cte
WHERE  package <> 'abc';

小提琴

package <> 'abc'
中的表达式
ORDER BY
将包“abc”排序在顶部。为什么?

如果列

package
可以为NULL,则需要对某些查询执行更多操作。

© www.soinside.com 2019 - 2024. All rights reserved.