0

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

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...')

对于每个强制性标签,这会有所不同吗?

4

1 回答 1

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)
于 2013-03-13T12:32:42.780 回答