更多的理论/逻辑问题,但我所拥有的是两个表:links
和options
。链接是一个表格,我在其中添加表示产品ID(在单独的products
表中)和选项之间的链接的行。 options
表包含所有可用选项。
我正在尝试做什么(但努力创建逻辑)是加入两个表,只返回links
表中没有选项链接的行,因此表示哪些选项是仍然可以添加到产品中。
SQL的功能是否可以帮助我?我对SQL还没有太多的经验。
答案 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