SQL JOIN查询返回我们在联接表中找不到匹配项的行

时间:2014-04-09 23:01:30

标签: mysql sql join

更多的理论/逻辑问题,但我所拥有的是两个表:linksoptions。链接是一个表格,我在其中添加表示产品ID(在单独的products表中)和选项之间的链接的行。 options表包含所有可用选项。

我正在尝试做什么(但努力创建逻辑)是加入两个表,只返回links表中没有选项链接的行,因此表示哪些选项是仍然可以添加到产品中。

SQL的功能是否可以帮助我?我对SQL还没有太多的经验。

3 个答案:

答案 0 :(得分:18)

你的桌子设计听起来不错。

如果此查询返回"选项"的id值。链接到特定的产品" ...

SELECT k.option_id
  FROM links k
 WHERE k.product_id = 'foo'

然后此查询将获得与"产品相关的所有选项的详细信息"

SELECT o.id
     , o.name
  FROM options o
  JOIN links k
    ON k.option_id = o.id
 WHERE k.product_id = 'foo'

请注意,我们实际上可以将"product_id='foo'"谓词从WHERE子句移动到JOIN的ON子句,以获得相同的结果,例如

SELECT o.id
     , o.name
  FROM options o
  JOIN links k
    ON k.option_id = o.id
   AND k.product_id = 'foo'

(并不是说它在这里有任何区别,但是如果我们使用OUTER JOIN会有所不同(在WHERE子句中,它会否定连接的"外部"和使它等同于INNER JOIN。)

但是,这些都没有回答你的问题,只会为回答你的问题奠定基础:

我们如何从"选项中获取行"哪些与特定产品无关?

最有效的方法是(通常)反连接模式。

那是什么,我们将从"选项"中获取所有行,以及来自"链接"的任何匹配行。 (对于特定的product_id,在您的情况下)。该结果集将包括来自" options"的行。在"链接"。

中没有匹配的行

"技巧"是过滤掉在"链接"中找到的匹配行的所有行。这将使我们只有 那些没有匹配的行。

我们过滤这些行的方式,我们在WHERE子句中使用谓词来检查是否找到匹配。我们通过检查我们确定的列 NOT NULL (如果找到匹配的行)来做到这一点。如果找到匹配的行 NOT ,我们知道 *肯定该列将 NULL

这样的事情:

SELECT o.id
     , o.name
  FROM options o
  LEFT
  JOIN links k
    ON k.option_id = o.id
   AND k.product_id = 'foo'
 WHERE k.option_id IS NULL

"LEFT"关键字指定"外部"加入操作,我们得到"选项"的所有行(即使找不到匹配的行,也可以在" JOIN左侧和#34;一侧的表格)。 (正常的内部联接会筛选出没有匹配的行。)

"技巧"在WHERE子句中...如果我们从链接中找到匹配的行,我们知道从"option_id"返回的"links"列不会为NULL。如果"等于"它不能为NULL。什么,我们知道它必须"等于"因为ON子句中的谓词而产生的东西。

因此,我们知道没有匹配的选项中的行将具有该列的NULL值。

让你的大脑缠绕它需要一点点,但反连接很快成为一种熟悉的模式。


"反加入"模式不是获得结果集的唯一方法。还有其他几种方法。

一种选择是使用带有"NOT EXISTS"谓词的查询和相关子查询。这有点容易理解,但通常也没有表现出来:

SELECT o.id
     , o.name
  FROM options o
 WHERE NOT EXISTS ( SELECT 1
                      FROM links k
                     WHERE k.option_id = o.id
                       AND k.product_id = 'foo'
                  )

那就是从选项表中获取所有行。但是对于每一行,运行一个查询,看看是否存在匹配的行""在链接表中。 (在选择列表中返回的内容并不重要,我们只测试它是否至少返回一行...我在选择列表中使用" 1"来提醒我,我正在寻找" 1行"。

这通常不会像反连接那样执行,但有时它运行得更快,特别是如果外部查询的WHERE子句中的其他谓词几乎每行都会过滤掉,并且子查询只需要跑了几排。 (也就是说,当我们只需要在大海捞针中检查几根针时。当我们需要处理整堆干草时,反连接模式通常会更快。)

您最有可能看到的初学者查询是NOT IN (subquery)。我甚至不打算给出一个例子。如果你有一个文字列表,那么一定要使用NOT IN。但是使用子查询,它很少表现最佳,但它似乎最容易理解。

哦,干草什么的,我也会做一个演示(不是我鼓励你这样做):

SELECT o.id
     , o.name
  FROM options o
 WHERE o.id NOT IN ( SELECT k.option_id
                       FROM links k
                      WHERE k.product_id = 'foo'
                        AND k.option_id IS NOT NULL
                      GROUP BY k.option_id
                   )

该子查询(在parens中)获取与产品关联的所有option_id值的列表。

现在,对于选项中的每一行(在外部查询中),我们可以检查id值以查看子查询返回的列表中是否有它。

如果我们保证option_id永远不会为NULL,我们可以省略测试"option_id IS NOT NULL"的谓词。 (在更一般的情况下,当NULL爬进结果集时,外部查询无法判断o.id是否在列表中,并且查询没有返回任何行;所以我通常包括,即使它不是必需的。GROUP BY也不是必须的;特别是如果(product_id,option_id)元组有唯一约束(保证唯一性)

但是,除了测试之外,不要再使用NOT IN (subquery),除非有一些令人信服的理由(例如,它设法比反连接更好)。

您不太可能注意到与小集合的任何性能差异,传输语句,解析它,生成访问计划以及返回结果的开销使实际的"执行相形见绌。计划的时间。它有较大的集合,执行"执行的差异"时间变得明显。

EXPLAIN SELECT ...是一个非常好的方法来处理执行计划,看看MySQL在你的陈述中真正做了什么。

适当的索引,特别是覆盖索引,可以显着提高某些陈述的性能。

答案 1 :(得分:4)

是的,您可以执行LEFT JOIN(如果MySQL;其他方言有变化),其中包含链接中没有选项匹配的行。然后测试是否options.someColumn IS NULL并且您将确切地拥有链接中没有"匹配"的行。选项中的行。

答案 2 :(得分:1)

尝试一下这个

的内容

要计算

 SELECT Links.linkId, Count(*)
    FROM Link
    LEFT JOIN Options ON Links.optionId = Options.optionId
    Where Options.optionId IS NULL
    Group by Links.linkId

查看行

SELECT Links.linkId
    FROM Link
    LEFT JOIN Options ON Links.optionId = Options.optionId
    Where Options.optionId IS NULL