全文索引とスカラーインデックスの組み合わせ


8

たとえば、全文を使用して検索できる必要のある1200万人の名前と住所のデータベースがあるが、各行には整数値も含まれているとしCOMPANYIDます。テーブルには、1,200万行を超える約250の個別のCOMPANYIDが含まれています。

フルテキストインデックスを定義するときCOMPANYに、ツリーにそれぞれ独自の「ブランチ」を与えることは可能ですか?


現在、パフォーマンスの問題が発生していますか?たとえば、CompanyIDにインデックスがあり、それにフィルターをかけると、その列にフィルターがない場合と同じフルテキストパフォーマンスが表示されますか?私はだろうと私は経験のトンを持っていない願っています SQL Serverが最初の安価なフィルタに一致する行にフルテキスト検索スペースを絞り込むためにスマートで十分です。
アーロンバートランド

実際、私はcompanyこれまでに1つだけアプリを作成したばかりで、すべての会社で本番環境に導入して欲しいとみんながとても気に入っており、意味のある1200万のダミーデータ行を含むモックアップを作成する機会がありませんでしたまだ。「Lastname1」、「Lastname2」、「City1」などの値は十分な変動がなく、テスト結果を歪める可能性があります。データは頻繁に変更されるため、SQL Serverが特定のクエリでどちらのインデックスが狭いインデックスであるかを確実に認識できるかどうかは不明です。また、会社ごとの行数は大きく異なります。ある会社は1000行しかなく、別の会社は60,000であるかもしれません。
Tim

SQL Serverがこのシナリオをどれだけうまく処理できるか、ここでは詳細レベルを考えると、ここではだれも推測できません。あなたは現実的な、意味のあるテストデータとのテストを実行し構築する必要があるとしているあなたの負荷あなたのハードウェア...
アーロン・ベルトラン

しかし、私は私の質問への答えを望んでいます。私は誰にも推測するように求めているのではありません。
Tim

回答:


3

短い答えはありません、そしてあなたは本当にこれを必要としません。フルテキストインデックスは反転インデックスなので、フルテキストインデックスを作成するときに指定する必要がある一意のdoc_idによって分割された単語を格納します。これは、「一意の、単一キー、null不可の列」、理想的には整数でなければなりません。基本的に外部キーは何であるか理解できず、それらに基づいてそれらを分割する簡単な方法はありません。

会社ごとのテーブルとテーブルごとのフルテキストインデックスを使用して、このようなものになりすますことができます。どのテーブルに挿入/フェッチするかを決定するために、前にある種のコードロジックが必要になります。これは、管理するのにかなりの頭痛の種になるでしょう。

重大なボリューム(230億レコードなど)がある場合は、シャーディングソリューションを確認できます。たとえば、企業ごとのAzure VMの前にアプリが置かれていて、接続先のマシンを決定します。しかし、明らかにそれも必要ありません。

SQL 2008では、フルテキストに多くの改良が加えられ、データベースエンジンにより統合されました。通常の列に対してWHERE句を指定し、フルテキスト関数を使用する1つのシナリオは、「混合クエリ」と呼ばれ、ここで説明されています。これはSQL 2008に関する情報ですが、これは素晴らしい記事です。

一般的にパフォーマンスと計画が気になる場合は、テストデータをスピンアップし、スキューを導入して試してみてください。このスクリプトを数分で200万行まで処理しました。

!!TODO introduce some skew
USE master
GO

SET NOCOUNT ON
GO

DBCC TRACEON(610)   -- Minimal logging
GO

GO

IF EXISTS ( SELECT * FROM sys.databases WHERE name = 'fullTextDemo' )
BEGIN
    ALTER DATABASE fullTextDemo SET SINGLE_USER WITH ROLLBACK IMMEDIATE
    DROP DATABASE fullTextDemo
END
GO

IF NOT EXISTS ( SELECT * FROM sys.databases WHERE name = 'fullTextDemo' )
CREATE DATABASE fullTextDemo
GO

ALTER DATABASE fullTextDemo SET RECOVERY SIMPLE
GO

USE fullTextDemo
GO

IF OBJECT_ID('dbo.yourAddresses') IS NOT NULL DROP TABLE dbo.yourAddresses
IF OBJECT_ID('dbo.companies') IS NOT NULL DROP TABLE dbo.companies
GO

CREATE TABLE dbo.companies (
    companyId       INT IDENTITY NOT NULL,
    companyName     NVARCHAR(50) NOT NULL,

    CONSTRAINT PK_companies PRIMARY KEY ( companyId )
)
GO

CREATE TABLE dbo.yourAddresses (
    rowId           INT IDENTITY,
    companyId       INT NOT NULL FOREIGN KEY REFERENCES dbo.companies ( companyId ),
    searchTerms     NVARCHAR(2048) NOT NULL

    CONSTRAINT PK_yourAddresses PRIMARY KEY ( rowId )
)
GO

-- Populate the companies
;WITH cte AS (
SELECT TOP 250 ROW_NUMBER() OVER ( ORDER BY ( SELECT 1 ) ) rn
FROM master.sys.columns c1
    CROSS JOIN master.sys.columns c2
    CROSS JOIN master.sys.columns c3
)
INSERT INTO dbo.companies ( companyName )
SELECT NEWID()
FROM cte
GO

-- Generate 2,636,000 records
INSERT dbo.yourAddresses ( companyId, searchTerms )
SELECT c.companyId, m.[text]
FROM dbo.companies c
    CROSS JOIN ( SELECT * FROM sys.messages ) m
WHERE m.language_id = 1033
AND m.[text] Like '[a-z]%'
GO

CREATE INDEX _idx ON dbo.yourAddresses ( companyId ) INCLUDE ( searchTerms )
GO

-- !!TODO look at compression
--ALTER INDEX PK_yourAddresses ON dbo.yourAddresses REBUILD WITH ( DATA_COMPRESSION = PAGE )
--GO

-- Create the catalog
IF NOT EXISTS ( SELECT * FROM sys.fulltext_catalogs WHERE name = N'ftc_yourAddresses' )
CREATE FULLTEXT CATALOG ftc_yourAddresses
GO

-- Create the full-text index
CREATE FULLTEXT INDEX ON dbo.yourAddresses ( searchTerms ) KEY INDEX PK_yourAddresses ON ftc_yourAddresses WITH CHANGE_TRACKING MANUAL  -- CHANGE_TRACKING OFF, NO POPULATION
GO

SELECT 'before' ft, * FROM sys.fulltext_indexes
GO

ALTER FULLTEXT INDEX ON dbo.yourAddresses START FULL POPULATION;
GO


DECLARE @i INT 
SET @i = 0

WHILE EXISTS ( SELECT * FROM sys.fulltext_indexes WHERE has_crawl_completed = 0 )
BEGIN

        SELECT outstanding_batch_count, *
        FROM sys.dm_fts_index_population
        WHERE database_id = DB_ID()

        --SELECT *
        --FROM sys.dm_fts_outstanding_batches
        --WHERE database_id = DB_ID()

    WAITFOR DELAY '00:00:05'

    SET @i = @i + 1
    IF @i > 60 BEGIN RAISERROR( 'Too many loops!', 16, 1 ) BREAK END

END

SELECT 'after' ft, * FROM sys.fulltext_indexes
GO



SELECT TOP 1000 *
FROM dbo.yourAddresses ft
WHERE companyId = 42
 AND CONTAINS ( searchTerms, 'data' )
GO

SELECT TOP 1000 *
FROM dbo.yourAddresses a
    INNER JOIN CONTAINSTABLE ( dbo.yourAddresses, searchTerms, 'data' ) ct ON a.rowId = ct.[key]
WHERE a.companyId = 42
GO

SELECT TOP 1000 *
FROM dbo.yourAddresses a
    INNER JOIN CONTAINSTABLE ( dbo.yourAddresses, searchTerms, 'data' ) ct ON a.rowId = ct.[key]
WHERE a.companyId = 42
OPTION ( MERGE JOIN )
GO

SELECT TOP 100 *
FROM sys.dm_fts_index_keywords (DB_ID(), OBJECT_ID('dbo.yourAddresses') )

SELECT TOP 100 *
FROM sys.dm_fts_index_keywords_by_document(DB_ID(), OBJECT_ID('dbo.yourAddresses') )
ORDER BY document_id
GO

スクリプトを作成する時間を割いていただき、 "混合"クエリの記事へのリンク、および数十億対数百万の視点をありがとうございました:-)
Tim

1
記事によると、根本的な問題に対する解決策は、SQL Server 2008で導入されました
ティム・

よかったです。私のスクリプトのカバーするインデックスとクエリヒントは、単なる推奨ではなく単なる実験であると言えるでしょう。これらは、パフォーマンスの問題がある場合に使用できるオプションです。インデックスは少し幅が広い可能性があり、ヒント付きの通常の警告が適用されます。
wBob
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.