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と列の両方にインデックスが必要なことだと思います。これらのテーブルに最初の列として非クラスター化インデックスがあるかどうかは言いませんでしたが、そのようなインデックスがある場合でも、個々の単語が既知の単語から検索される頻度はどれくらいですか? 私には、あなたが特定の既知の単語から始めて、それらから s を見つけようとしている可能性が高いと思われます。私の推測が正しければ、PK を最初に配置するように変更する必要があります。を非クラスター化インデックスの最初の列にすることができるため、に基づくターゲット クエリは引き続き 2 つの個別のシークを使用できます (1 回は非クラスター化、次にクラスター化)。ftsIndexWordOncewordIDwordIDsIDsIDwordIDsIDsID

これらのインデックスが配置されたら、次のことに対処できます。ftsIndexWordOnce各単語を 1sID回だけインデックス付けするだけなので、に参加しているように見えますが、そのテーブルには列がないため、に参加するfirstWordPOS必要もあります。FTSindex各単語が最初の 1000 に含まれていることを確認してください。そのため、これを使用すると、ftsIndexWordOnce. 現時点では、いずれかでテーブル スキャンを実行する必要があると推測しているため、テーブルftsIndexWordOnceが小さいため、スキャンに必要な読み取りが少なくなるため、ある程度のメリットがあります。上記のインデックスの問題に対処すると、突然不必要に多くのコストがかかるため、次を使用してクエリからftsIndexWordOnce除外できます。ftsIndexWordOnceCount(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
;

もう 1 つのアイデアは、firstWordPOS列を に追加ftsIndexWordOnceし、最初に列を構築するために既に使用しているプロセスを変更して、それを更新する (そして入力する) ことです。これにより、元のクエリに戻って条件を追加するだけで済みます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 に答える