有没有更好的方法来查找具有多个子查询的记录

时间:2013-03-13 11:54:42

标签: tsql database-performance

我正在查询具有以下架构的数据库:

People
------
PersonId

PeopleTags
----------
PeopleId
TagId

Tags
----
TagId
Guid

一个人可能有多个标签,并且当查询要求时,要查找具有所有一组强制标签和任何一组可选标签的人。

目前,我正在为每个强制标记执行单独的子查询,为可选标记执行单个子查询,这样就可以得到这样的结果:

SELECT People.*
FROM People
WHERE
    PeopleId IN (SELECT PeopleId FROM PeopleTags WHERE TagId IN (SELECT TagId FROM Tags WHERE Guid = '83c55f3d...')
    AND PeopleId IN (SELECT PeopleId FROM PeopleTags WHERE TagId IN (SELECT TagId FROM Tags WHERE Guid = '8159248a...')
    AND PeopleId IN (SELECT PeopleId FROM PeopleTags WHERE TagId IN (SELECT TagId FROM Tags WHERE Guid IN ('737d9015...', 'b7a5974e...')

可以有任意数量的强制和可选标签,我们偶尔也会收到此错误:

  

查询处理器耗尽了内部资源而无法使用   制定查询计划。这是一个罕见的事件,只有预期   非常复杂的查询或引用非常大的查询   表或分区的数量。请简化查询。如果你   相信您错误地收到了此消息,请联系客户   支持服务以获取更多信息。

研究此错误表明它是由于具有大型IN列表的子查询(完全可以使用现有的代码)。

有没有更好的方法来编写此查询?我已尝试多次加入该表,但如果我这样做,则不会返回任何结果。

SELECT People.*
FROM People
INNER JOIN PeopleTags ON PeopleTags.PersonId = People.PersonId
INNER JOIN Tags t1 on t1.TagId = PeopleTags.TagId and t1.Guid = '8159248A...'
INNER JOIN Tags t2 on t2.TagId = PeopleTags.TagId and t2.Guid = '415DF7A3...'

我可以改变

PeopleId IN (SELECT PeopleId FROM PeopleTags WHERE TagId IN (SELECT TagId FROM Tags WHERE Guid = '83c55f3d...')

PeopleId IN (SELECT PeopleId FROM PeopleTags INNER JOIN Tags ON Tags.TagId = PeopleTags.TagId WHERE Guid = '83c55f3d...')

对于每个强制性标签,会有所作为吗?

1 个答案:

答案 0 :(得分:0)

<强>更新

您尝试将其转换为JOIN很好,但是您需要LEFT JOIN,否则您将无法获得结果,就像我在原始答案中所解释的那样。此外,我添加了一个ORDER BY,因此那些没有可选标签的人会列在具有可选标签的人的下方。然后你可以限制结果并获得用户最有可能寻找的结果。

SELECT People.*
FROM People
INNER JOIN PeopleTags ON PeopleTags.PersonId = People.PersonId
INNER JOIN Tags t1 on t1.TagId = PeopleTags.TagId and t1.Guid = '8159248A...'
LEFT JOIN Tags t2 on t2.TagId = PeopleTags.TagId and t2.Guid = '415DF7A3...'
ORDER BY CASE WHEN t2.TagId IS NULL THEN 99 ELSE 1 END
LIMIT 10 /*or whatever*/

原始答案:

你的问题有点不清楚。第二个中的可选标签是否加入了标签(我的答案中我会这么说)?是否必须至少有一个可选标签或者没有可选标签?如果没有(我可以选择定义),为什么要关心它们呢?你也没有SELECT他们。所以你可以忘记第二次加入,顺便提一下你的情况就是错误的。您将PeopleTags中的同一列多次连接到同一列上的同一个表。但PeopleTags中的行不能同时具有两个不同的值。因此,你没有结果。

如果您需要确保至少有一个可选标签,但您不关心它是什么,您可以像这样优化它:

SELECT People.*
FROM People
INNER JOIN PeopleTags ON PeopleTags.PersonId = People.PersonId
INNER JOIN Tags t1 on t1.TagId = PeopleTags.TagId 
WHERE t1.Guid = '8159248A...'
AND EXISTS (SELECT 1 FROM Tags t2 WHERE t2.TagId = PeopleTags.TagId)