结合全文本和标量索引


8

假设我们有一个1200万个名称和地址的数据库,这些数据库需要使用全文本进行搜索,但是每行也包含一个整数值,例如COMPANYID。该表在这1200万行中包含约250个不同的COMPANYID。

定义全文索引时,是否可以COMPANY在树中赋予每个索引自己的“分支”?


您现在看到性能问题了吗?例如,如果在CompanyID上有一个索引,并且您对此进行了过滤,您是否会看到与该列上没有过滤器相同的全文本性能?我没有很多经验,我希望 SQL Server足够聪明,可以将全文搜索空间缩小到首先匹配较便宜的筛选器的行。
亚伦·伯特兰

实际上,company到目前为止,我只是为一个应用程序编写了一个应用程序,每个人都非常喜欢它,他们希望我将其投入所有公司的生产,而且我还没有机会用1200万个有意义的虚拟数据行创建一个模型。然而。诸如“ Lastname1”,“ Lastname2”,“ City1”之类的值将没有足够的变化,并且可能会使测试结果产生偏差。数据变化如此频繁,以至于我不确定SQL Server是否可以可靠地知道哪个索引在任何给定查询中都是较窄的索引,并且每个公司的行数变化很大。一个公司可能只有1000行,另外6万行。
蒂姆(Tim)

鉴于此处的详细程度,此处没有人能够推测SQL Server将如何处理这种情况。你将不得不构建现实,有意义的测试数据和运行测试您的工作负载硬件...
阿龙贝特朗

但是我仍然想回答我的问题。我不是在问任何人猜测。
蒂姆(Tim)

Answers:


3

简短的答案不是,您实际上不需要这个。全文索引是反向索引,因此它们通过创建全文索引时必须指定的唯一doc_id存储拆分词。理想情况下,它必须是“唯一,单键,不可为空的列”。本质上没有外键,也没有简单的方法可以在此基础上对其进行分区。

可以通过每个公司的一个表和每个表的全文本索引来进行类似的欺骗。您可能需要坐在前面的某种代码逻辑来确定要插入/从哪个表插入数据。要解决这个问题将是相当头疼的事,几乎肯定是不值得的。

如果您的数据量很大(例如,更像是230亿条记录),则可以查看分片解决方案,例如,每家公司都有Azure VM,并在其前面安装了应用程序以确定要连接的计算机。但显然您也不需要。

SQL 2008还对全文进行了许多改进,现在已将其全文集成到数据库引擎中。您针对普通列指定WHERE子句并使用全文功能的一种情况称为“混合查询”,并在此处进行讨论。即使有关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 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.