全文を使用して検索できる必要のある1200万人の名前と住所のデータベースがあるとしましょう。ただし、各行には整数値も含まれています。たとえば、COMPANYID
としましょう。テーブルには、1,200万行を超える約250の個別のCOMPANYIDが含まれています。
フルテキストインデックスを定義するときに、各COMPANY
にツリー内の独自の「ブランチ」を与えることは可能ですか?
いいえ、短い答えはありません、そしてあなたは本当にこれを必要としません。フルテキストインデックスは 逆インデックス なので、フルテキストインデックスを作成するときに指定する必要がある一意のdoc_idによって分割された単語を格納します。これは、「一意の、単一キー、null不可の列」、理想的には整数でなければなりません。基本的に外部キーは何であるか理解できず、それらに基づいて分割する簡単な方法はありません。
あなたはできますこのような何かを偽装して、会社ごとのテーブルとテーブルごとのフルテキストインデックスを使用します。どのテーブルに挿入/フェッチするテーブルを決定するために、前にある種のコードロジックが必要になります。これは、管理するのにかなりの頭痛の種になるでしょう。
重大なボリューム(230億レコードなど)がある場合は、シャーディングソリューションを確認できます。たとえば、会社ごとのAzure VMのようなアプリをそれらの前に置いて、接続するマシンを決定します。 。しかし、明らかにそれも必要ありません。
SQL 2008では、データベースエンジンにより統合されたフルテキストへの多くの改善も行われました。通常の列に対してWHERE句を指定してフルテキスト関数を使用する1つのシナリオは、「混合クエリ」と呼ばれ、 here で説明されています。この情報は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