Menggabungkan teks lengkap dan indeks skalar

8

Katakanlah kita memiliki database 12 juta nama dan alamat yang perlu dicari menggunakan teks lengkap, tetapi setiap baris juga mengandung nilai integer, katakanlah COMPANYID. Tabel ini berisi sekitar 250 PERUSAHAAN yang berbeda dari 12 juta baris itu.

Apakah mungkin, ketika mendefinisikan indeks teks lengkap, untuk memberikan masing COMPANY-masing "cabang" di pohon?

Tim
sumber
Apakah Anda melihat masalah kinerja sekarang? Misalnya jika ada indeks pada CompanyID, dan Anda memfilternya, apakah Anda melihat kinerja teks lengkap yang sama seolah-olah tidak ada filter pada kolom itu? Saya tidak punya banyak pengalaman dengan saya berharap SQL Server cukup pintar untuk mempersempit ruang pencarian teks lengkap ke baris yang cocok dengan filter yang lebih murah terlebih dahulu.
Aaron Bertrand
Sebenarnya, saya baru saja menulis aplikasi untuk companysejauh ini, dan semua orang sangat menyukainya sehingga mereka ingin saya memproduksinya untuk semua perusahaan, dan saya belum punya kesempatan untuk membuat maket dengan 12 juta baris data boneka yang berarti namun. Nilai seperti "Lastname1", "Lastname2", "City1", dll tidak akan memiliki variasi yang cukup dan dapat mengubah hasil pengujian. Data berubah begitu sering sehingga saya tidak yakin SQL Server akan dapat dipercaya untuk mengetahui indeks mana yang lebih sempit dalam kueri yang diberikan, dan jumlah baris per perusahaan sangat bervariasi. Satu perusahaan mungkin hanya memiliki 1000 baris, 60.000 lainnya.
Tim
Tidak seorang pun di sini akan dapat berspekulasi, mengingat tingkat detail di sini, seberapa baik SQL Server akan menangani skenario ini. Anda akan harus membangun realistis, bermakna uji data dan menjalankan tes dari Anda beban kerja pada Anda hardware ...
Aaron Bertrand
Tetapi saya masih ingin jawaban untuk pertanyaan saya. Saya tidak meminta siapa pun untuk berspekulasi.
Tim

Jawaban:

3

Tidak adalah jawaban singkatnya, dan Anda tidak benar-benar membutuhkan ini. Indeks teks lengkap adalah indeks terbalik sehingga mereka menyimpan kata-kata split oleh doc_id unik yang harus Anda tentukan saat membuat indeks teks lengkap. Ini harus berupa "kolom unik, tombol tunggal, tidak dapat dibatalkan" idealnya bilangan bulat. Apa yang pada dasarnya kunci asing tidak diketahui dan tidak ada cara mudah untuk mempartisi mereka atas dasar itu.

Anda bisa menipu sesuatu seperti ini dengan tabel per perusahaan dan indeks teks lengkap per tabel. Anda akan membutuhkan semacam logika kode yang duduk di depan untuk menentukan tabel mana yang akan disisipkan / diambil. Ini akan menjadi sakit kepala yang cukup untuk mengelola hampir pasti tidak sepadan.

Jika Anda memiliki volume serius (misalnya lebih dari 23 miliar catatan) maka Anda dapat melihat solusi yang sulit, misalnya sesuatu seperti Azure VM per perusahaan dengan aplikasi yang duduk di depan mereka untuk menentukan mesin mana yang akan dihubungkan. Tetapi jelas Anda juga tidak membutuhkannya.

Ada juga sejumlah perbaikan dalam SQL 2008 untuk teks lengkap yang sekarang lebih terintegrasi ke dalam mesin basis data. Satu skenario, di mana Anda menentukan klausa WHERE terhadap kolom normal dan menggunakan fungsi teks lengkap, disebut sebagai 'Permintaan Campuran' dan dibahas di sini . Ini masih merupakan artikel yang hebat meskipun informasinya untuk SQL 2008.

Jika Anda umumnya mengkhawatirkan kinerja dan rencana, mengapa tidak memutar beberapa data uji, perkenalkan beberapa kemiringan, dan cobalah. Saya mengetuk skrip ini dengan ~ 2 juta baris dalam beberapa menit:

!!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
wBob
sumber
Terima kasih banyak telah meluangkan waktu untuk menulis skrip itu, untuk tautan ke artikel kueri "campuran", dan untuk perspektif miliaran versus jutaan :-)
Tim
1
Menurut artikel itu, solusi untuk masalah yang mendasarinya diperkenalkan dalam SQL Server 2008.
Tim
Senang itu bermanfaat. Saya mungkin harus mengatakan indeks penutup dan petunjuk kueri dalam skrip saya hanya eksperimen bukan rekomendasi, ini adalah opsi yang mungkin Anda miliki jika Anda memiliki masalah kinerja. Indeks berpotensi agak lebar dan peringatan biasa dengan petunjuk berlaku.
wBob