PostgreSQL:如何使用generate_series()找出列中缺少的数字?

Man*_*eta 5 postgresql postgresql-8.4 generate-series

SELECT commandid 
FROM results 
WHERE NOT EXISTS (
    SELECT * 
    FROM generate_series(0,119999) 
    WHERE generate_series = results.commandid 
    );
Run Code Online (Sandbox Code Playgroud)

我有一个results类型的列,int但各种测试失败,并没有添加到表中.我想创建一个返回commandid未找到的列表的查询results.我认为上面的查询会做我想要的.但是,如果我使用超出预期可能范围的范围commandid(如负数),它甚至不起作用.

Cra*_*ger 14

给出样本数据:

create table results ( commandid integer primary key);
insert into results (commandid) select * from generate_series(1,1000);
delete from results where random() < 0.20;
Run Code Online (Sandbox Code Playgroud)

这有效:

SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE NOT EXISTS (SELECT 1 FROM results WHERE commandid = s.i);
Run Code Online (Sandbox Code Playgroud)

这个替代配方也是如此:

SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
LEFT OUTER JOIN results ON (results.commandid = s.i) 
WHERE results.commandid IS NULL;
Run Code Online (Sandbox Code Playgroud)

以上两种情况似乎都会在我的测试中产生相同的查询计划,但您应该与数据库中的数据进行比较,EXPLAIN ANALYZE以查看哪种方法最佳.

说明

请注意,NOT IN我没有NOT EXISTS在一个公式中使用子查询,而在另一个公式中使用普通子查询OUTER JOIN.这对DB服务器更容易优化这些,它避免了能与出现的混乱问题,NULL以s NOT IN.

我最初赞成这个OUTER JOIN表述,但至少在9.1中我的测试数据NOT EXISTS表格优化到同一个计划.

NOT IN当系列很大时,两者都会比下面的配方表现更好,就像你的情况一样.NOT IN曾经要求Pg IN对每个被测试的元组进行线性搜索,但是对查询计划的检查表明Pg可能足够聪明,现在可以对其进行散列.该NOT EXISTS(转化为JOIN被查询规划)和JOIN更好地工作.

NOT IN在存在NULL commandid的情况下,该公式既令人困惑又效率低下:

SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE s.i NOT IN (SELECT commandid FROM results);
Run Code Online (Sandbox Code Playgroud)

所以我会避免它.有1,000,000行,另外两行在1.2秒内完成,NOT IN配方运行CPU限制,直到我感到无聊并取消它.


pic*_*ypg 6

正如我在评论中提到的,您需要执行与上述查询相反的操作.

SELECT
    generate_series
FROM
    generate_series(0, 119999)
WHERE
    NOT generate_series IN (SELECT commandid FROM results);
Run Code Online (Sandbox Code Playgroud)

此时,您应该找到commandid所选范围内的列中不存在的值.

  • 如果``commandid`可能是'NULL`,结果将不是你所期望的.在这种情况下可能不是问题,但值得铭记. (2认同)
  • 我没意见.SO的要点是获得最佳答案,而不一定是第一个答案. (2认同)