SQL Server 如何知道SQL全文索引填充何时完成?

pw136qt2  于 2023-01-20  发布在  其他
关注(0)|答案(7)|浏览(219)

我们正在为ASP.NET应用程序编写针对测试SQL Server数据库运行的单元测试。也就是说,ClassInitialize方法使用测试数据创建一个新数据库,ClassCleanup删除该数据库。我们通过从代码运行.bat脚本来完成此操作。
被测类被赋予一个连接字符串,该字符串连接到单元测试数据库而不是生产数据库。
我们的问题是,数据库包含一个全文索引,为了让我们的测试按预期运行,需要用测试数据完全填充该索引。
据我所知,全文索引总是在后台填充。我希望能够:
1.使用同步(transact-SQL?)语句创建完全填充的全文索引,或者
1.了解全文填充何时完成,是否有回调选项,或者我可以重复询问吗?
我目前的解决方案是在类初始化方法结束时强制延迟- 5秒似乎有效-因为我在文档中找不到任何东西。

vzgqcmou

vzgqcmou1#

对于"丹尼尔·伦肖"的回答,我想提供一个更容易阅读的版本:

DECLARE @CatalogName VARCHAR(MAX)
SET     @CatalogName = 'FTS_Demo_Catalog'

SELECT
    DATEADD(ss, FULLTEXTCATALOGPROPERTY(@CatalogName,'PopulateCompletionAge'), '1/1/1990') AS LastPopulated
    ,(SELECT CASE FULLTEXTCATALOGPROPERTY(@CatalogName,'PopulateStatus')
        WHEN 0 THEN 'Idle'
        WHEN 1 THEN 'Full Population In Progress'
        WHEN 2 THEN 'Paused'
        WHEN 3 THEN 'Throttled'
        WHEN 4 THEN 'Recovering'
        WHEN 5 THEN 'Shutdown'
        WHEN 6 THEN 'Incremental Population In Progress'
        WHEN 7 THEN 'Building Index'
        WHEN 8 THEN 'Disk Full.  Paused'
        WHEN 9 THEN 'Change Tracking' END) AS PopulateStatus

结果:

LastPopulated           PopulateStatus
----------------------- ----------------------------------
2012-05-08 14:51:37.000 Idle

(1 row(s) affected)
kmbjn2e3

kmbjn2e32#

您可以使用FULLTEXTCATALOGPROPERTY查询状态(请参见此处:http://technet.microsoft.com/en-us/library/ms190370.aspx)。
例如:

SELECT
    FULLTEXTCATALOGPROPERTY(cat.name,'ItemCount') AS [ItemCount],
    FULLTEXTCATALOGPROPERTY(cat.name,'MergeStatus') AS [MergeStatus],
    FULLTEXTCATALOGPROPERTY(cat.name,'PopulateCompletionAge') AS [PopulateCompletionAge],
    FULLTEXTCATALOGPROPERTY(cat.name,'PopulateStatus') AS [PopulateStatus],
    FULLTEXTCATALOGPROPERTY(cat.name,'ImportStatus') AS [ImportStatus]
FROM sys.fulltext_catalogs AS cat

您可能还想使用SQL事件探查器来监视在您打开目录的属性对话框时SQL Server Management Studio发出的命令。该对话框包括填充状态的指示,并且显示的所有信息都是使用T-SQL查询的。

mmvthczy

mmvthczy3#

这是我们根据GarethOwen的答案创建的一个存储过程。它接受逗号分隔的表列表作为参数,并等待所有表的全文索引更新。它每十分之一秒进行一次检查,以防止磁盘颠簸,并在10秒后超时,以防运行缓慢或损坏。如果FT搜索跨越多个索引,则非常有用。
按以下方式调用:

EXECUTE [dbo].[WaitForFullTextIndexing] 'MY_TABLE,ALTERNATE_NAMES,TAG_GROUP_VALUES,TAG_GROUPS,FIELD_OPTION';

来源:

CREATE PROCEDURE WaitForFullTextIndexing
    @TablesStr varchar(max)
AS
BEGIN
    DECLARE @Tables AS TABLE( [word] [varchar](8000) NULL)

    INSERT INTO @Tables (word) SELECT items from dbo.Split(@TablesStr, ',');

    DECLARE @NumberOfTables int;
    SELECT @NumberOfTables = COUNT(*) from @Tables;

    DECLARE @readyCount int;
    SET @readyCount = 0;

    DECLARE @waitLoops int;
    SET @waitLoops = 0;

    DECLARE @result bit;

    WHILE @readyCount <> @NumberOfTables AND @waitLoops < 100
    BEGIN

        select @readyCount = COUNT(*)
        from @Tables tabs
        where OBJECTPROPERTY(object_id(tabs.word), 'TableFulltextPopulateStatus') = 0;

        IF @readyCount <> @NumberOfTables
        BEGIN
            -- prevent thrashing
            WAITFOR DELAY '00:00:00.1';
        END

        set @waitLoops = @waitLoops + 1;

    END

END
GO

split是一个表值函数,现在每个人都必须拥有它,它将分隔符上的字符串拆分到临时表中:

CREATE FUNCTION [dbo].[Split](@String varchar(8000), @Delimiter char(1))        
returns @temptable TABLE (items varchar(8000))        
as        
begin        
    declare @idx int        
    declare @slice varchar(8000)        

    select @idx = 1        
        if len(@String)<1 or @String is null  return        

    while @idx!= 0        
    begin        
        set @idx = charindex(@Delimiter,@String)        
        if @idx!=0        
            set @slice = left(@String,@idx - 1)        
        else        
            set @slice = @String        

        if(len(@slice)>0)   
            insert into @temptable(Items) values(@slice)        

        set @String = right(@String,len(@String) - @idx)        
        if len(@String) = 0 break        
    end    
return        
end 

GO
ugmeyewa

ugmeyewa4#

谢谢丹尼尔,你的回答让我走上了正确的轨道。
我实际上使用下面的T-SQL语句来询问全文索引的填充状态是否为Idle:

SELECT OBJECTPROPERTY(object_id('v_doc_desc_de'), 'TableFulltextPopulateStatus')

'v_doc_desc_de'是我们索引的数据库视图的名称。
如果填充状态不是Idle,我将等待几秒钟,然后再次询问,直到填充状态变为Idle。在两次检查之间等待一小段时间以确保全文填充不会因持续检查填充状态而减慢速度,这一点很重要。
MSDN documentation声明建议使用OBJECTPROPERTYEX函数(表级),而不是使用属性为“PopulateStatus”的FULLTEXTCATALOGPROPERTY语句。它声明如下:
SQL Server的未来版本中将删除以下属性:避免在新的开发工作中使用这些属性,并计划修改当前使用其中任何属性的应用程序。

vbopmzt1

vbopmzt15#

要等待全文目录完成其所有表和视图的填充而不必指定它们的名称,可以使用以下存储过程。这是JohnB对此问题的回答和cezarm对related question的回答的组合:

CREATE PROCEDURE WaitForFullTextIndexing
@CatalogName VARCHAR(MAX)
AS
BEGIN
    DECLARE @status int;
    SET @status = 1;
    DECLARE @waitLoops int;
    SET @waitLoops = 0;

    WHILE @status > 0 AND @waitLoops < 100
    BEGIN       
        SELECT @status = FULLTEXTCATALOGPROPERTY(@CatalogName,'PopulateStatus')
        FROM sys.fulltext_catalogs AS cat;

        IF @status > 0
        BEGIN
            -- prevent thrashing
            WAITFOR DELAY '00:00:00.1';
        END
        SET @waitLoops = @waitLoops + 1;
    END
END
kokeuurv

kokeuurv6#

我做了以下工作:

var indexIsPopulating = true;
        var stopWatch = new Stopwatch();
        stopWatch.Start();
        while (indexIsPopulating)
        {
            System.Threading.Thread.Sleep(500);
            using var con = new SqlConnection(databaseConnectionString);
            // using dapper here - but you just need to run query on databsae
            var status = await con.QueryFirstAsync<int>("SELECT OBJECTPROPERTY(OBJECT_ID('dbo.MyTableName'), 'TableFulltextPopulateStatus'); ");
            if (status == 0)
            {
                indexIsPopulating = false;
            }
            else if (stopWatch.ElapsedMilliseconds > 60000) // 1 minute
            {
                stopWatch.Stop();
                throw new Exception("Full Text Index failed to populate within 1 minute.");
            }
        }
        stopWatch.Stop();
zrfyljdw

zrfyljdw7#

我在使用一个带有全文搜索的dockerized sql server时也遇到了同样的问题,数据库成功地植入了种子,但是当我运行测试时,索引填充状态还没有完成,所以在我的测试中没有返回结果。
为了确保在运行测试之前重建索引,我使用了一个自旋锁和一个查询,该查询识别我的表中是否有正在重建的索引:

public MyApplication SeedDatabase( Action<MyDbContext> seed )
{
    using var scope = Services.CreateScope();
    var scopedServices = scope.ServiceProvider;
    var db = scopedServices.GetRequiredService<MyDbContext>();

    db.Database.EnsureDeleted();
    db.Database.EnsureCreated();

    CreateFullTextCatalog();
    CreateFullTextIndexes();

    seed.Invoke( db );
    db.SaveChanges();

    SpinWait.SpinUntil( () => IsFullTextPopulateStatusIdle(), TimeSpan.FromSeconds( 5 ) );

    return this;

    bool IsFullTextPopulateStatusIdle() => db.Database.SqlQuery<int>( $"select case when EXISTS(SELECT OBJECTPROPERTY(object_id, 'TableFulltextPopulateStatus') from sys.tables where OBJECTPROPERTY(object_id, 'TableFulltextPopulateStatus') <> 0) then 1  else 0  end as value" ).Single() == 0;

    void CreateFullTextCatalog() => db.Database.ExecuteSqlRaw( File.ReadAllText( Path.Combine( Directory.GetCurrentDirectory(), "Sql", "FT_Catalog.sql" ) ) );

    void CreateFullTextIndexes() => db.Database.ExecuteSqlRaw( File.ReadAllText( Path.Combine( Directory.GetCurrentDirectory(), "Sql", "FullTextIndexes.sql" ) ) );
}

相关问题