给定一个数字序列如何识别缺失的数字

Posted

技术标签:

【中文标题】给定一个数字序列如何识别缺失的数字【英文标题】:Given a sequence of numbers how to identify the missing numbers 【发布时间】:2016-09-11 03:51:45 【问题描述】:

我想在一个数字序列中获取所有缺失的数字。 只是想知道是否有比下面更好的方法?

SELECT x
  FROM 
  (
    SELECT x,
           LAG(x,1) OVER ( ORDER BY x ) prev_x
      FROM 
       ( SELECT * FROM
        ( SELECT 1 AS x ),
        ( SELECT 2 AS x ),
        ( SELECT 3 AS x ),
        ( SELECT 4 AS x ),
        ( SELECT 5 AS x ),
        ( SELECT 6 AS x ),
        ( SELECT 8 AS x ),
        ( SELECT 10 AS x ),
        ( SELECT 11 AS x )
       )
  ) 
 WHERE x-prev_x > 1;

【问题讨论】:

it really 取决于您使用的 sql 风格!根据您添加的标签 - 我认为这是针对 BigQuery 的?请确认或澄清! 感谢任何解决方案... SO 更喜欢有特定答案的特定问题。不鼓励提出开放性问题(可能的答案太多)。 【参考方案1】:

让我对你说实话! 任何其他可行的解决方案都会比提出问题更好 - 原因很简单 - 这是错误的!它根本不返回丢失的数字!它而是在下一个间隙后显示数字。仅此而已(希望您会感谢我对此睁大眼睛)

现在,关于更好的解决方案 - 您有很多选择。 注意:以下选项仅适用于 BigQuery!

选项一

BigQuery 标准 SQL - 请参阅 How to Enable Standard SQL

WITH YourTable AS (
  SELECT 1 AS x UNION ALL
  SELECT 2 AS x UNION ALL
  SELECT 3 AS x UNION ALL
  SELECT 6 AS x UNION ALL
  SELECT 8 AS x UNION ALL
  SELECT 10 AS x UNION ALL
  SELECT 11 AS x
),
nums AS (
  SELECT num 
  FROM UNNEST(GENERATE_ARRAY((SELECT MIN(x) FROM YourTable), (SELECT MAX(x) FROM YourTable))) AS num
)
SELECT num FROM nums
LEFT JOIN YourTable ON num = x
WHERE x IS NULL
ORDER BY num

选项 2

BigQuery Legacy SQL您可以在下面尝试(这里您需要在 nums 表的选择表达式中设置开始/最小值和结束/最大值

SELECT num FROM (
  SELECT num FROM (
    SELECT ROW_NUMBER() OVER() AS num, * 
    FROM (FLATTEN((SELECT SPLIT(RPAD('', 11, '.'),'') AS h FROM (SELECT NULL)), h))
  ) WHERE num BETWEEN 1 AND 11
) AS nums
LEFT JOIN (
  SELECT x FROM
    (SELECT 1 AS x),
    (SELECT 2 AS x),
    (SELECT 3 AS x),
    (SELECT 6 AS x),
    (SELECT 8 AS x),
    (SELECT 10 AS x),
    (SELECT 11 AS x)
) AS YourTable
ON num = x
WHERE x IS NULL

选项 3

BigQuery Legacy SQL - 如果您不想依赖最小值和最大值并且需要设置这些值 - 您可以使用以下解决方案 - 它只需要设置足够高的最大值以适应您的预期增长(例如我放 1000)

SELECT num FROM (
  SELECT num FROM (
    SELECT ROW_NUMBER() OVER() AS num, * 
    FROM (FLATTEN((SELECT SPLIT(RPAD('', 1000, '.'),'') AS h FROM (SELECT NULL)), h))
  ) WHERE num BETWEEN 1 AND 1000
) AS nums
LEFT JOIN YourTable
ON num = x
WHERE x IS NULL
AND num BETWEEN (SELECT MIN(x) FROM YourTable) AND (SELECT MAX(x) FROM YourTable) 

选项 4(出于某种原因 - 我目前最喜欢的)

BigQuery 标准 SQL - 没有显式连接

WITH YourTable AS (
  SELECT 1 AS x UNION ALL
  SELECT 2 AS x UNION ALL
  SELECT 3 AS x UNION ALL
  SELECT 6 AS x UNION ALL
  SELECT 8 AS x UNION ALL
  SELECT 10 AS x UNION ALL
  SELECT 11 AS x
)
SELECT num
FROM (SELECT x, LEAD(x) OVER(ORDER BY x) AS next_x FROM YourTable),  
     UNNEST(GENERATE_ARRAY(x + 1,next_x - 1)) AS num
WHERE next_x - x > 1
ORDER BY x

【讨论】:

感谢您的解决方案。但是如果 table(x) 不断增加怎么办? 在我的回答中使用第一个解决方案(标准 sql) - 它会处理这个问题! 为 bq legacy sql 添加,因此它也可以处理它 在第一个解决方案中,我尝试使用 postgresql 实现,但这些功能不起作用...尝试使用 sqlfiddle 链接,如下所示。 sqlfiddle.com/#!15/19495/4 选项 2 工作正常,但它有限......在选项 1 中,cte 在 Bigquery 上不起作用......现在就运行它......【参考方案2】:

Postgres 中的最短 解决方案使用标准 SQL EXCEPT

WITH tbl(x) AS (SELECT unnest ('1,2,3,4,5,6,8,10,11'::int[]))
-- the CTE provides a temp table - might be an actual table instead
SELECT generate_series(min(x), max(x)) FROM tbl
EXCEPT ALL
TABLE  tbl;

返回集合的函数unnest() 是 Postgres 特有的,是提供您的一组数字作为表格的最短语法。

也适用于数据中的重复值或 NULL 值。

TABLE tbl 是(标准 SQL!)SELECT * FROM tbl 的简短语法:

Is there a shortcut for SELECT * FROM in psql?

相关(有更多解释):

Select rows which are not present in other table How to check a sequence efficiently for used and unused values in PostgreSQL

【讨论】:

我很喜欢这个 postgres 解决方案。有什么方法可以在不使用函数的情况下实现这一点..?【参考方案3】:

您的查询可以写得更简洁:

SELECT x
FROM (
    SELECT x,
           lag(x, 1) OVER ( ORDER BY x ) prev_x
    FROM ( VALUES (1), (2), (3), (4), (5), (6), (8), (10), (11) ) v(x)
) sub
WHERE x-prev_x > 1;

这将返回未命中后的下一个最大值 (8, 10),而不是缺失值本身 (7, 9)。但是当然你没有方便的值。

如果你知道序列中值的范围,那么你可以使用这个:

SELECT s.x
FROM generate_series(<<min>>, <<max>>) s(x)
LEFT JOIN my_table t ON s.x = t.x
WHERE t.x IS NULL;

这会返回实际的缺失值。

如果不知道取值范围,则需要添加子查询:

SELECT s.x
FROM ( SELECT min(x), max(x) FROM my_table ) r
JOIN generate_series(r.min, r.max) s(x) ON true
LEFT JOIN my_table t ON s.x = t.x
WHERE t.x IS NULL;

或者,而不是LEFT JOIN

SELECT x
FROM ( SELECT min(x), max(x) FROM my_table ) r,
     generate_series(r.min, r.max) s(x)
WHERE NOT EXISTS (SELECT 1 FROM my_table t WHERE t.x = s.x);

【讨论】:

这是一个可靠的答案。我看不出拒绝投票的理由。令人困惑的小细节:OP 的示例省略了 7 和 9(不是 8 和 9)。 @ErwinBrandstetter 只需单击一下即可投反对票(但我在最初的回答中确实弄乱了 VALUES 子句中的括号)。感谢您的赞许。已更正的值列表。

以上是关于给定一个数字序列如何识别缺失的数字的主要内容,如果未能解决你的问题,请参考以下文章

268. 缺失数字

缺失的数字

如何在一个序列中找到缺失的数字?

268. 缺失数字

268. 缺失数字

leetcode 缺失数字