更多的理论/逻辑问题,但我有两个表:links和options.链接是一个表格,我在其中添加代表产品ID(在单独的products表中)和选项之间的链接的行.该options表包含所有可用选项.
我正在尝试做什么(但努力创建逻辑)是连接两个表,只返回表中没有选项链接的行links,因此表示哪些选项仍可用于添加到产品.
是否有SQL的功能可以帮助我?我对SQL还没有太大的经验.
spe*_*593 18
你的桌子设计听起来不错.
如果此查询返回id链接到特定"产品"的"选项" 的值...
SELECT k.option_id
FROM links k
WHERE k.product_id = 'foo'
Run Code Online (Sandbox Code Playgroud)
然后这个查询将获得与"产品"相关的所有选项的详细信息
SELECT o.id
, o.name
FROM options o
JOIN links k
ON k.option_id = o.id
WHERE k.product_id = 'foo'
Run Code Online (Sandbox Code Playgroud)
请注意,我们实际上可以将"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'
Run Code Online (Sandbox Code Playgroud)
(不,这使得这里的任何区别,但它如果我们使用OUTER JOIN(WHERE子句中有所作为,这将否定的加入了"外部性",并使其等效于INNER JOIN. )
但是,这些都没有回答你的问题,它只是为回答你的问题奠定了基础:
我们如何从"选项"中获取未链接到特定产品的行?
最有效的方法是(通常)反连接模式.
那是什么,我们将从"选项"获取所有行,以及"链接"中的任何匹配行(对于特定的product_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
Run Code Online (Sandbox Code Playgroud)
该"LEFT"关键字指定的"外部"联接操作,我们从"选项"中的所有行(该表上的"左"侧的JOIN),即使没有找到匹配的行.(正常的内连接会过滤掉没有匹配的行.)
"技巧"在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'
)
Run Code Online (Sandbox Code Playgroud)
那就是从选项表中获取所有行.但是对于每一行,运行查询,并查看链接表中是否存在匹配的行"存在".(在选择列表中返回的内容无关紧要,我们只测试它是否至少返回一行...我在选择列表中使用"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
)
Run Code Online (Sandbox Code Playgroud)
该子查询(在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在你的声明中做了什么.
适当的索引,特别是覆盖索引,可以显着提高某些语句的性能.