1

关于如何压缩此查询的想法?

select [gp1].[sID] 
from
( 
   select [ftsIndexWordOnce].[sID], [ftsIndexWordOnce].[wordID]
     from [ftsIndexWordOnce] with (nolock)
     Join [FTSindex] with (nolock) 
       On [FTSindex].[sID] = [ftsIndexWordOnce].[sID] 
      And [FTSindex].[wordID] = [ftsIndexWordOnce].[wordID] 
      And [FTSindex].[wordPOS] <= '1000' 
     join [FTSwordDef] with (nolock) 
       on [ftsIndexWordOnce].[wordID] = [FTSwordDef].[ID] 
      and [FTSwordDef].[word] in ('capital','bank')   
    group by [ftsIndexWordOnce].[sID], [ftsIndexWordOnce].[wordID]
)   [gp1] 
group by [gp1].[sID] 
having count(*) = 2

PK [ftsIndexWordOnce] 是 [sID],[wordID]
PK [FTSindex] 是 [sID],[wordPos]

以下不是问题的一部分。
这只是背景。

基本查询是

select [ftsIndexWordOnce].[sID] 
 from [ftsIndexWordOnce] with (nolock)
 join [FTSwordDef] with (nolock) 
   on [ftsIndexWordOnce].[wordID] = [FTSwordDef].[ID] 
  and [FTSwordDef].[word] in ('capital','bank')
group by [ftsIndexWordOnce].[sID]
having count(*) = 2 
order by [ftsIndexWordOnce].[sID]

这会找到包含这两个单词的文档。
问题查询将此扩展到 1000 个单词中的这两个单词。

4

2 回答 2

2

我认为最重要的问题是您需要在列中FTSindex和列ftsIndexWordOnce上都有索引wordID。您没有说这些表上是否有wordID作为第一列的非聚集索引 - 但即使它们有这样的索引,从 known 中查找单个单词的频率是sID多少?在我看来,您似乎更有可能从特定的已知单词开始并试图从中找到sIDs。如果我的猜测是正确的,那么您的 PK 应该更改为wordID放在第一位。sID可以是非聚集索引中的第一列,这样基于目标的查询sID仍然可以使用两个单独的查找(一次到非聚集,然后到聚集)。

一旦这些索引到位,我们就可以解决下一件事:看起来你正在加入,ftsIndexWordOnce因为它每次只索引每个单词sID,但由于该表没有firstWordPOS列,你还必须加入FTSindex到确保每个单词出现在前 1000 个中——所以这几乎失去了使用ftsIndexWordOnce. 现在,由于我的猜测是您必须对其中任何一个进行表扫描,ftsIndexWordOnce因此仍然会给您带来一些好处,因为它是一个较小的表,因此需要较少的读取来扫描。一旦你解决了上面的索引问题,突然间ftsIndexWordOnce不必要地花费更多,你可以ftsIndexWordOnce通过使用从查询中消除Count(DISTINCT)

SELECT
   i.sID
FROM
   dbo.FTSindex i
   INNER JOIN dbo.FTSwordDef w
      ON i.wordID = w.ID
WHERE
   i.wordPOS <= 1000
   AND w.word in ('capital','bank')   
GROUP BY
   i.sID
HAVING
   Count(DISTINCT i.wordID) = 2
;

另一个想法是,您可以向 中添加一firstWordPOSftsIndexWordOnce,并首先修改您已经用来构建它的任何过程以更新它(然后填充它)。这将允许您返回原始查询并简单地添加条件AND firstWordPOS <= 1000。随着ftsIndexWordOnce表的更小尺寸及其以 开头的新聚集索引wordID,性能将进一步提高。

wordID这是另一个疯狂的想法,如果您确实添加索引但不使其成为聚集索引的第一列,它可能会给您带来一些好处:

SELECT W1.sID
FROM
   (
      SELECT DISTINCT i.sID
      FROM
         dbo.FTSindex i
         INNER JOIN dbo.FTSwordDef w
            ON i.wordID = w.ID
      WHERE
         i.wordPOS <= 1000 
         AND w.word = 'capital'
    ) W1 INNER JOIN (
      SELECT DISTINCT i.sID
      FROM
         dbo.FTSindex i
         INNER JOIN dbo.FTSwordDef w
            ON i.wordID = w.ID
      WHERE
         i.wordPOS <= 1000 
         AND w.word = 'bank'
     ) W2 ON W1.sID = W2.sID
;

这样做的缺点是不容易修改以容纳更多单词,但可能会将某些查询从扫描切换到范围搜索 - 有时OR或者IN可以触发扫描,如果项目数量很少,单独的查询将被搜索。

最后,由于wordPOS显然是数字,请不要在查询中将其作为字符串放在引号中:wordPOS <= 1000更好。

PS 需要明确的是,不需要更改 PK。正如您所说,这样做可能会对夜间加载过程造成灾难性影响。但是您确实指出还有其他索引,因此如果您WordID在这些表中有一个索引,那么您可能已经得到了很好的服务。

我可以谦虚地建议(因为我不像您那样了解您的系统),如果您的负载主要插入新数据而不更改大量现有数据,那么您的系统可能会达到一个临界点,重新加载整个数据集会更慢而不是进行更改。您甚至可以考虑使用按 排序的临时表sID,并且仅对决赛桌进行战略更新——如果您要这样做,那么更改决赛桌的 PK 可能成为一种选择。只是一个(可能是无知的)想法。

于 2013-06-13T02:31:58.157 回答
0

试试这个,它可能会工作:

 select Distinct o.sID
 from ftsIndexWordOnce o with (nolock)
    Join FTSindex i with (nolock) 
       On i.sID = o.sID 
          And i.wordID = o.wordID 
          And i.wordPOS <= '1000' 
    join FTSwordDef w with (nolock) 
       on w.ID = o.wordID
          and w.word in ('capital','bank')   
 group by o.sID
 Having Count(*) = 2
于 2013-06-13T01:26:09.293 回答