Kombination von Volltext- und Skalarindex

8

Angenommen, wir haben eine Datenbank mit 12 Millionen Namen und Adressen, die im Volltext durchsucht werden müssen, aber jede Zeile enthält beispielsweise auch einen ganzzahligen Wert COMPANYID. Die Tabelle enthält rund 250 verschiedene COMPANYIDs in diesen 12 Millionen Zeilen.

Ist es möglich, bei der Definition der Volltextindizes jedem COMPANYeinen eigenen "Zweig" im Baum zuzuweisen?

Tim
quelle
Sehen Sie jetzt Leistungsprobleme? Wenn beispielsweise ein Index für CompanyID vorhanden ist und Sie danach filtern, sehen Sie dieselbe Volltextleistung, als ob in dieser Spalte kein Filter vorhanden wäre? Ich habe nicht viel Erfahrung mit Ich hoffe, SQL Server ist intelligent genug, um den Volltextsuchbereich auf die Zeilen zu beschränken, die zuerst dem weniger teuren Filter entsprechen.
Aaron Bertrand
Eigentlich habe ich die App bisher nur für eine geschrieben company, und alle mochten sie so sehr, dass ich sie für alle Unternehmen in Produktion bringen soll , und ich hatte keine Chance, ein Modell mit 12 Millionen aussagekräftigen Dummy-Datenzeilen zu erstellen noch. Werte wie "Nachname1", "Nachname2", "Stadt1" usw. variieren nicht ausreichend und können die Testergebnisse verzerren. Die Daten ändern sich so häufig, dass ich nicht sicher bin, ob SQL Server zuverlässig weiß, welcher Index in einer bestimmten Abfrage enger ist, und die Anzahl der Zeilen pro Unternehmen variiert stark. Ein Unternehmen hat möglicherweise nur 1000 Zeilen, ein weiteres 60.000.
Tim
Niemand hier wird angesichts der Detailgenauigkeit spekulieren können, wie gut SQL Server mit diesem Szenario umgehen wird. Sie müssen realistische, aussagekräftige Testdaten erstellen und Tests Ihrer Arbeitslast auf Ihrer Hardware ausführen ...
Aaron Bertrand
Trotzdem hätte ich gerne eine Antwort auf meine Frage. Ich bitte niemanden zu spekulieren.
Tim

Antworten:

3

Nein ist die kurze Antwort, und das brauchen Sie nicht wirklich. Volltextindizes sind invertierte Indizes, sodass sie die geteilten Wörter anhand der eindeutigen doc_id speichern, die Sie beim Erstellen des Volltextindex angeben müssen. Dies muss eine "eindeutige, nicht nullbare Spalte mit einem Schlüssel" sein, idealerweise eine Ganzzahl. Was im Wesentlichen ein Fremdschlüssel ist, wird nicht dargestellt, und es gibt keine einfache Möglichkeit, sie auf dieser Basis zu partitionieren.

Sie könnten so etwas mit einer Tabelle pro Unternehmen und einem Volltextindex pro Tabelle fälschen. Sie benötigen eine Art Codelogik, die vor Ihnen steht, um zu bestimmen, in welche Tabelle Sie einfügen oder von welcher Sie abrufen möchten. Dies wäre ein erheblicher Kopfschmerz, der sich mit ziemlicher Sicherheit nicht lohnt.

Wenn Sie ein ernsthaftes Volumen hatten (z. B. mehr als 23 Milliarden Datensätze), können Sie sich eine Sharding-Lösung ansehen, z. B. eine Azure-VM pro Unternehmen, vor der sich eine App befindet, um zu bestimmen, mit welchem ​​Computer eine Verbindung hergestellt werden soll. Aber das brauchst du natürlich auch nicht.

In SQL 2008 gab es auch eine Reihe von Verbesserungen gegenüber Volltext, der jetzt stärker in das Datenbankmodul integriert ist. Ein Szenario, in dem Sie eine WHERE-Klausel für eine normale Spalte angeben und die Volltextfunktionen verwenden, wird als "gemischte Abfrage" bezeichnet und hier erläutert . Dies ist immer noch ein großartiger Artikel, obwohl die Informationen für SQL 2008 sind.

Wenn Sie generell über Leistung und Pläne besorgt sind, können Sie einige Testdaten hochfahren, einen Versatz einführen und ausprobieren. Ich habe dieses Skript mit ~ 2 Millionen Zeilen in wenigen Minuten erstellt:

!!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
quelle
Vielen Dank, dass Sie sich die Zeit genommen haben, dieses Skript zu schreiben, für den Link zum "gemischten" Abfrageartikel und für die Milliarden-gegen-Millionen-Perspektive :-)
Tim
1
Dem Artikel zufolge wurde in SQL Server 2008 eine Lösung für das zugrunde liegende Problem eingeführt.
Tim
Ich bin froh, dass es nützlich war. Ich sollte wahrscheinlich sagen, dass der Deckungsindex und der Abfragehinweis in meinem Skript nur Experimente und keine Empfehlungen sind. Dies sind Optionen, die Sie möglicherweise haben, wenn Sie Leistungsprobleme haben. Der Index ist möglicherweise etwas breit und es gelten die üblichen Warnungen mit Hinweisen.
wBob