代码之家  ›  专栏  ›  技术社区  ›  Damien_The_Unbeliever

SQL Server中的索引视图有问题,错误8646

  •  0
  • Damien_The_Unbeliever  · 技术社区  · 15 年前

    我只是设计了一个新系统的原型,将我们其中一个数据库中的某些操作推迟到几个小时之后。我想出了一个非常简单的模式。我是SQLServer2005Express上的第一个原型,但在2008开发人员上确认了同样的问题。我得到的错误是:

    消息8646,级别21,状态1,程序 取消,第6行找不到索引 表的索引ID 1中的条目 277576027,在数据库“XXXXXX”中。这个 指示的索引已损坏或存在 当前更新有问题 计划。运行DBCC CHECKDB或DBCC 检查表。如果问题仍然存在, 联系产品支持。

    我使用的模式是:

    create schema Writeback authorization dbo
        create table Deferrals (
            ClientID uniqueidentifier not null,
            RequestedAt datetime not null,
            CompletedAt datetime null,
            CancelledAt datetime null,
            ResolvedAt as ISNULL(CompletedAt,CancelledAt) persisted,
            constraint PK_Writeback_Deferrals PRIMARY KEY (ClientID,RequestedAt) on [PRIMARY],
            constraint CK_Writeback_Deferrals_NoTimeTravel CHECK ((RequestedAt <= CompletedAt) AND (RequestedAt <= CancelledAt)),
            constraint CK_Writeback_Deferrals_NoSchrodinger CHECK ((CompletedAt is null) or (CancelledAt is null))
            /* TODO:FOREIGN KEY */
        )
        create view Pending with schemabinding as
        select
            ClientID
        from
            Writeback.Deferrals
        where
            ResolvedAt is null
    go
    alter table Writeback.Deferrals add constraint
        DF_Writeback_Deferrals_RequestedAt DEFAULT CURRENT_TIMESTAMP for RequestedAt
    go
    create unique clustered index PK_Writeback_Pending on Writeback.Pending (ClientID)
    go
    create procedure Writeback.Defer
        @ClientID uniqueidentifier
    as
        set nocount on
    
        insert into Writeback.Deferrals (ClientID)
        select @ClientID
        where not exists(select * from Writeback.Pending where ClientID = @ClientID)
    go
    create procedure Writeback.Cancel
        @ClientID uniqueidentifier
    as
        set nocount on
    
        update
            Writeback.Deferrals
        set
            CancelledAt = CURRENT_TIMESTAMP
        where
            ClientID = @ClientID and
            CompletedAt is null and
            CancelledAt is null
    go
    create procedure Writeback.Complete
        @ClientID uniqueidentifier
    as
        set nocount on
    
        update
            Writeback.Deferrals
        set
            CompletedAt = CURRENT_TIMESTAMP
        where
            ClientID = @ClientID and
            CompletedAt is null and
            CancelledAt is null
    go
    

    引发错误的代码如下:

    declare @ClientA uniqueidentifier
    declare @ClientB uniqueidentifier
    select @ClientA = newid(),@ClientB = newid()
    
    select * from Writeback.Pending
    exec Writeback.Defer @ClientA
    select * from Writeback.Pending
    exec Writeback.Defer @ClientB
    select * from Writeback.Pending
    exec Writeback.Cancel @ClientB  --<-- Error being raised here
    select * from Writeback.Pending
    exec Writeback.Complete @ClientA
    select * from Writeback.Pending
    select * from Writeback.Deferrals
    

    我见过其他一些人遇到这样的问题,但他们的视图中似乎有聚合(从MS发回的一条消息说,他们将在2005年的SP1中删除创建此类索引视图的能力),或者他们通过在join子句中应用merge join来解决这个问题(但我没有)。

    最初,deferrals表中没有计算列,视图中的where子句分别测试completedat和cancelledat列是否为空。但我改为上面的内容,只是想看看我是否能挑起不同的行为。

    我所有的设置选项都适合使用索引视图,如果没有,我希望抛出一个不那么猛烈的错误。

    有什么想法吗?

    2 回复  |  直到 15 年前
        1
  •  0
  •   Mladen Prajdic    15 年前

    索引损坏。运行checkdb并查看它会给您带来什么错误。最简单的方法是重建索引。

    还要看看这个 KB article 如果它适用于你的坐姿。

    另外请注意,将主键放在guid列上会在其上创建聚集索引,这是性能方面最差的做法。

        2
  •  0
  •   Damien_The_Unbeliever    8 年前

    我设法找出了导致这个错误的原因,通过尝试从头开始构建这个脚本,在我进行的过程中添加一些片段。

    如果视图是作为create schema语句的一部分创建的,就会产生某种bug。如果我将create模式分为自己的批处理,然后在单独的批处理中创建表和视图,那么一切都可以正常工作。


    过期很久的编辑-我在Connect上提出了这个问题 here . 它在SQL Server 2008中被确认为一个问题。

    内部版本(2010年)表明它不再是一个问题,我已经(刚才,2016年)确认了问题中的脚本不会在SQL Server 2012中生成相同的错误。修复程序没有返回到SQL Server 2008。