Mysql SQL联接查询以返回在联接表中未找到匹配项的行

Mysql SQL联接查询以返回在联接表中未找到匹配项的行,mysql,sql,join,Mysql,Sql,Join,更多的是一个理论/逻辑问题,但我有两个表:链接和选项。Links是一个表,我在其中添加表示产品ID(在单独的products表中)和选项之间链接的行。选项表包含所有可用选项 我试图做的(但很难创建逻辑)是连接两个表,只返回links表中没有选项链接的行,因此表示哪些选项仍然可以添加到产品中 SQL有没有一个特性可以帮助我?我对SQL还没有太多的经验。是的,你可以做一个左连接(如果是MySQL;其他方言也有变化),它将包括链接中选项不匹配的行。然后测试选项。someColumn为空,链接中的行在选

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

我试图做的(但很难创建逻辑)是连接两个表,只返回
links
表中没有选项链接的行,因此表示哪些选项仍然可以添加到产品中


SQL有没有一个特性可以帮助我?我对SQL还没有太多的经验。

是的,你可以做一个
左连接(如果是MySQL;其他方言也有变化),它将包括链接中选项不匹配的行。然后测试
选项。someColumn
为空
,链接中的行在选项中没有“匹配”行。

尝试类似的方法

计算

 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

你的桌子设计听起来不错

如果此查询返回链接到特定“产品”的“选项”的
id

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

注意,我们实际上可以将
“product_id='foo'”
谓词从连接的WHERE子句移动到ON子句,以获得等效的结果,例如

SELECT o.id
     , o.name
  FROM options o
  JOIN links k
    ON k.option_id = o.id
   AND k.product_id = 'foo'
(这并不是说它在这里有什么区别,但如果我们使用外部联接,它会有所不同(在WHERE子句中,它会否定联接的“外部性”,并使其等效于内部联接。)

但是,这些都不能回答你的问题,它只是为回答你的问题设置了舞台:

如何从“选项”中获取未链接到特定产品的行?

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

也就是说,我们将获得“选项”中的所有行,以及“链接”中的任何匹配行(在您的情况下,对于特定的产品id)。该结果集将包括“选项”中的行,这些行在“链接”中没有匹配行

“诀窍”是过滤掉在“链接”中找到的所有匹配行。这将只剩下不匹配的行

我们过滤这些行的方式是,在WHERE子句中使用一个谓词来检查是否找到了匹配项。我们通过检查一列来做到这一点,我们知道如果找到了匹配的行,该列肯定是非NULL。如果找到了匹配的行,我们肯定知道*该列肯定是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”
关键字指定一个“outer”联接操作,我们从“options”(联接“LEFT”侧的表)获取所有行,即使找不到匹配的行。(正常的内部联接将过滤掉不匹配的行。)

“诀窍”在WHERE子句中……如果我们从链接中找到匹配的行,我们知道从
“links”
返回的
“option\u id”
列不会为NULL。如果它“等于”某个值,它就不能为NULL,我们知道它必须“等于”某个值,因为ON子句中的谓词

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

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


“反连接”模式不是获得结果集的唯一方法。还有其他几种方法

一种选择是将带有
“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子句中的其他谓词过滤掉几乎每一行,并且子查询只需运行几行时。(也就是说,当我们只需要检查一堆干草中的几根针时。当我们需要处理整堆干草时,反连接模式通常更快。)

您最有可能看到的初学者查询是
不在(子查询)
。我甚至不想给出一个这样的例子。如果您有一个文本列表,那么无论如何,请使用不在。但是对于子查询,它很少是最好的执行者,尽管它似乎是最容易理解的

哦,真糟糕,我也会给你一个演示(不是我鼓励你这样做):

该子查询(在参数内)获取与产品关联的所有选项id值的列表

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

如果我们保证option\u id永远不会为NULL,那么我们可以省略测试
“option\u id不为NULL”的谓词。(在更一般的情况下,当一个空值潜入resultset时,外部查询无法判断o.id是否在列表中,并且查询不返回任何行;因此我通常会包括这些行,即使它不是必需的。
GROUP BY
也不是严格必需的;尤其是当存在唯一约束(保证唯一性)时在(product_id,option_id)元组上

但是,除了测试之外,不要在(子查询)
中使用
,除非
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
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'
                  )
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
                   )