【问题标题】:Understanding this SQL LEFT JOIN (with IS NULL) example理解这个 SQL LEFT JOIN (with IS NULL) 例子
【发布时间】:2016-04-21 03:21:06
【问题描述】:

在数据库中,有一个表叫category:

CREATE TABLE category(
        category_id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(20) NOT NULL,
        parent INT DEFAULT NULL
);

创建它是为了制作相邻列表模型树。以下是当前表格内的内容:

在这个例子中(你可以在底部找到源链接),下面的sql代码用于获取表中的“叶”元素,“叶”元素是表中没有的行让任何其他行在“父”列中使用它们的“category_id”。运行了以下代码:

SELECT t1.name FROM
category AS t1 LEFT JOIN category as t2
ON t1.category_id = t2.parent
WHERE t2.category_id IS NULL;

前面的SQL代码的结果是这样的:

例如,表中的父列中没有值为 3 的行,因此 TUBE(具有 category_id == 3)是一个“叶”元素。

问题:为什么那个 sql 代码在逻辑上会给出这个结果?我很高兴它确实如此,因为它是我所需要的,但我无法理解它背后的原因。


示例来源:http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

【问题讨论】:

  • Outer joinsnull 检查是查看表中是否存在记录的常见模式,在这种情况下检查 category_id 是否为 parent 。非常类似于使用not existsnot in

标签: mysql sql tree adjacency-list


【解决方案1】:

每当你看到这种模式时:

SELECT    ...
FROM      TableA
LEFT JOIN TableB ON TableA.column = TableB.column
WHERE     TableB.id is null

思考:“在 TableA 中找到 TableB 中不存在的行。

您的查询意味着“查找没有子项的类别”。如果您删除 WHERE 子句并在查询中添加更多列,则更容易理解:

SELECT      t1.category_id,
            t1.name,
            t2.name  AS ChildName
FROM        category AS t1
LEFT JOIN   category as t2 ON t1.category_id = t2.parent

以下是您的查询中发生的情况:

  1. category 表开始,别名为t1t2。从现在开始,我将引用别名。
  2. 对于t1 中的每一行,查找t2 中将t1 行标识为其父行的所有记录
  3. 如果t1 行没有子行,t2.category_id 将为空
  4. 我们只想过滤 t1 没有子节点的行

【讨论】:

  • 好的,我已经非常接近理解了!第 1 行完美,第 2 行完美,第 3 行 如果 t1 行没有子行,则 t2.category_id 将为空 是我感到困惑的地方。 t2.category_id 列中的所有单元格从一开始就没有值吗?
  • 想想“如果我找不到你的孩子,我会留下null代替你孩子的category_id
  • 那么这个推理是否正确?:FROM category AS t1 LEFT JOIN category as t2 ON t1.category_id = t2.parent 首先列出了 t1 的所有信息,并且只添加了 MATCHING t2 列的信息。由于某些 t1.category_id 与任何 t2.parent 都不匹配,因此它们的 t2.parent 和 t2.category_id 都将具有 NULL 值。从那里,代码说:WHERE t2.category_id IS NULL(我只想要那些不匹配的)并且只想要名称列SELECT t1.name?这是否也意味着我可以用WHERE t2.parent_id IS NULL; 替换WHERE t2.category_id IS NULL; 并得到相同的结果?
  • 是的,你在这两种情况下都是对的。虽然您的描述相当技术性
  • 如果你还没有,我亲爱的朋友应该得到一个来自 stackoverflow 的天才徽章
【解决方案2】:

我认为如果你重命名你的表别名会更容易看到发生了什么:

    SELECT tparent.name 
      FROM category AS tparent 
      LEFT JOIN category as tchild
        ON tparent.category_id = tchild.parent
     WHERE tchild.category_id IS NULL;

现在可能更容易看到您要求的是没有子项的所有父类别名称的列表 [tchild.category_id IS NULL]

【讨论】:

    猜你喜欢
    • 2015-09-22
    • 1970-01-01
    • 2021-11-29
    • 2019-03-25
    • 1970-01-01
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多