【问题标题】:Am I using "NOT IN" correctly?我是否正确使用“NOT IN”?
【发布时间】:2020-10-20 19:19:11
【问题描述】:

我正在尝试链接 here 的 HackerRank 问题。

我很好奇为什么我尝试的解决方案没有为二叉树的叶子返回任何行。这是我的完整查询:

SELECT N, 'Root'
FROM BST
WHERE ISNULL(P)

UNION

SELECT N, 'Inner'
FROM BST
WHERE N IN (SELECT P FROM BST) AND NOT ISNULL(P)

UNION

SELECT N, 'Leaf'
FROM BST
WHERE N NOT IN (SELECT P FROM BST)

ORDER BY N

在我看来,我识别叶子的方法是正确的——叶子被定义为不是父节点的节点。但是,当我自己尝试 SELECT N, 'Leaf' ... 查询时,我没有得到任何输出。

谁能发现我的错误?我是否正确使用了NOT IN 语句?

edit:只是为了澄清一下,N是节点的值,对应的P是它的父节点。 N 和 P 都是整数。

【问题讨论】:

  • not in(...) 如果括号内可以遇到 NULL,则始终是个问题
  • @Strawberry 知道了,我会努力做到的。 fiddle 等是否比 SQL hackerrank 提交界面更受欢迎?
  • “基本上”是轻描淡写的。

标签: mysql sql subquery case hierarchical-data


【解决方案1】:

如果你运行叶子查询,你什么也得不到:

mysql> select n, 'Leaf' from bst where n not in (select p from bst);
Empty set (0.00 sec)

原因是子查询的结果中包含NULL:

mysql> select distinct p from bst;
+------+
| p    |
+------+
| NULL |
|    1 |
|    3 |
|    4 |
+------+

比较 N not in (NULL, 1, 3, 4) 必然不会返回任何匹配项,因为 NULL。这相当于:

WHERE NOT (N = NULL OR N = 1 OR N = 3 OR N = 4)

但是任何与 NULL 的比较都是 NULL,而不是 false。对于其他术语,您可以得到真/假,但不是第一个术语。这简化为:

WHERE NOT (NULL OR FALSE OR FALSE OR FALSE)

这又简化为:

WHERE NOT (NULL)

但是NULL的否定也是NULL,不是真的。因此,查询中的条件必然会阻止所有节点,无论它们是否为叶子。

您可以通过消除 NULL 来解决此问题:

mysql> select n, 'Leaf' from bst where n not in (select p from bst where not isnull(p));
+---+------+
| n | Leaf |
+---+------+
| 2 | Leaf |
| 5 | Leaf |
| 6 | Leaf |
+---+------+

如果您有此类数据,您还应该考虑使用recursive CTE queries in MySQL 8.0

WITH RECURSIVE cte (N, Label) AS
( 
  SELECT bst.N, CAST('Root' AS CHAR(20)) FROM bst WHERE ISNULL(P)
  UNION ALL
  SELECT bst.N, IF(below.P IS NULL, 'Leaf', 'Inner') FROM bst JOIN cte ON bst.P=cte.N
  LEFT OUTER JOIN bst AS below ON bst.N=below.P
)
SELECT DISTINCT N, Label FROM cte

+------+-------+
| N    | Label |
+------+-------+
|    1 | Root  |
|    2 | Leaf  |
|    3 | Inner |
|    4 | Inner |
|    5 | Leaf  |
|    6 | Leaf  |
+------+-------+

【讨论】:

  • 知道了,这就解释了。我将更改代码以在 WHERE 子句中排除 NULL。谢谢比尔!
【解决方案2】:

您可以使用case 表达式:

select n, 
    case when p is null then 'root'
         when not exists (select 1 from bst b1 where b1.p = b.n) then 'leaf'
         else 'inner'
    end as res
from bst b

这句话为:

  • 如果pnull,则该节点没有父节点,因此它是一个(?)根

  • 如果没有节点的p等于当前节点的n,则该节点没有子节点:它是叶子

  • 否则,它是一个内部节点

请注意,至少在理论上,一个节点既可以是根也可以是叶;挑战问题没有提到这种可能性。如果发生这种情况,查询会将其标记为根(因为这是在 case 表达式中匹配的第一个分支)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-24
    • 2014-05-15
    • 2010-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多