Não é possível remover o grupo de arquivos sem arquivos associados

8

Estou tendo algumas mensagens de erro estranhas no SQL Server 2017 CU3. Estou migrando bancos de dados e reorganizando grupos de arquivos. Ao "reorganizar", quero dizer que utilizo um procedimento armazenado que cria uma função de partição e um esquema de partição no novo grupo de arquivos para um objeto, reconstrói os índices durante o particionamento e remove o particionamento.

No final, tenho alguns grupos de arquivos vazios. Seus arquivos são removidos . Além disso, o próprio grupo de arquivos é removido. Isso funciona bem na maioria dos casos. No entanto, para dois bancos de dados, removi os arquivos ... resta um grupo de arquivos sem nenhum arquivo associado, mas

ALTER DATABASE REMOVE FILEGROUP

gera um erro 5042:

O grupo de arquivos 'xyz' não pode ser removido porque não está vazio.

Questão

Como posso me livrar desse grupo de arquivos vazio ... qual poderia ser o problema?

Eu já li alguns problemas comuns, mas eles não estão presentes no meu sistema:

  • Verificado:

    SELECT * FROM sys.partition_schemes;
    SELECT * FROM sys.partition_functions;

    0 linhas ... nenhum objeto de particionamento restante no banco de dados

  • UPDATE STATISTICS para todos os objetos no banco de dados

    sem efeito

  • Verifica se há índices no grupo de arquivos:

    SELECT * FROM  sys.data_spaces ds
    INNER JOIN sys.indexes i
    ON ds.data_space_id = i.data_space_id
    WHERE ds.name = 'xyz'

    0 linhas

  • Verifica objetos no grupo de arquivos:

    SELECT
        au.*,
        ds.name AS [data_space_name],
        ds.type AS [data_space_type],
        p.rows,
        o.name AS [object_name]
    FROM sys.allocation_units au
        INNER JOIN sys.data_spaces ds
            ON au.data_space_id = ds.data_space_id
        INNER JOIN sys.partitions p
            ON au.container_id = p.partition_id
        INNER JOIN sys.objects o
            ON p.object_id = o.object_id
    WHERE au.type_desc = 'LOB_DATA'
    AND ds.name ='xyz'

    0 linhas

Também tentei DBCC SHRINKFILEcom o parâmetro EMPTYFILEantes de remover o arquivo do grupo de arquivos. Realmente não faz sentido para mim, no entanto, leio soluções para descrever isso como uma correção. Não teve efeito de qualquer maneira.


Eu tenho alguma esperança de ler esta pergunta com falha no servidor e tentei o seguinte:

  • Atualizar todas as estatísticas
  • Descarte todas as estatísticas que não estão relacionadas a índices

No entanto, isso não teve efeito. Ainda tenho um grupo de arquivos sem nenhum arquivo associado e o grupo de arquivos não pode ser excluído. Estou totalmente intrigado, pois isso acontece em alguns bancos de dados e não em outros (com a mesma estrutura). Quando executo DBCC CHECK FILEGROUPneste grupo de arquivos vazio, recebo várias mensagens de erro como as seguintes:

Não é possível processar a identificação do conjunto de linhas 72057594712162304 do objeto "STORY_TRANSLATIONSCCC" (ID 120387498), índice "Ref90159CCC" (ID 2), porque reside no grupo de arquivos "CCC_APPLICATION_new" (ID 8), que não foi verificado.

Resultados do DBCC para 'STORY_TRANSLATIONSCCC'. Existem 0 linhas em 0 páginas para o objeto "STORY_TRANSLATIONSCCC".

Isso é normal ou aponta para algo incomum?

Essa pergunta pode ser uma duplicata, no entanto, não consigo encontrar uma correção de trabalho para mim em outras perguntas no dba.stackexchange. Por favor, dê uma olhada na lista do que eu já tentei. Isso é idêntico às soluções descritas em Não é possível remover grupos de arquivos não utilizados .

Mais detalhes

Talvez ajude a entender o que faço antes que o erro ocorra. Estou planejando uma migração para um novo servidor. Atualmente, estou testando isso em uma instância de teste. Os bancos de dados são restaurados a partir do servidor prod e o modelo de recuperação é alterado para simples. Meu objetivo é reestruturar os grupos de arquivos e passar de um modelo com um arquivo por grupo de arquivos para um modelo com dois arquivos por grupo de arquivos. Para conseguir isso, crio novos grupos de arquivos vazios com dois arquivos cada e movo os dados. Infelizmente, a maioria dos objetos possui dados LOB (XML e binários) ... então eu aproveito o particionamento como um auxiliar para mover os dados lob também. No final, todos os dados residem nos novos grupos de arquivos e os grupos de arquivos antigos estão vazios. Depois, removo todos os arquivos e também o respectivo grupo de arquivos. O grupo de arquivos primário permanece e apenas adiciona outro arquivo.pergunta minha . Esse processo funciona bem, mas em dois bancos de dados os arquivos podem ser excluídos, mas o grupo de arquivos não. Surpreendentemente, a estrutura desses bancos de dados deveria ser a mesma de outros bancos de dados, pois não foram encontrados problemas no processo de mover os dados e remover os grupos de arquivos antigos.

Então, aqui está uma lista de grupos de arquivos e arquivos dos dois bancos de dados em que o problema ocorre:

  1. CCC_GENTE

antes

+-----------------+------------+
| Filegroup       | Filename   |
+-----------------+------------+
| CCC_APPLICATION | CCC_APP    |
+-----------------+------------+
| CCC_ARCHIVE     | CCC_ARCHIV |
+-----------------+------------+
| CCC_AXN         | CCC_AXN    |
+-----------------+------------+
| CCC_GDV         | CCC_GDV    |
+-----------------+------------+
| PRIMARY         | CCC        |
+-----------------+------------+

depois de

    +-----------------+--------------------------+--------------------+----------------------------------------------------+
| Filegroup name  | Filegroup temporary name | Filename (logical) | Status                                             |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| CCC_APPLICATION | -                        | CCC_APP            | file removed, filegroup  cannot be removed (error) |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| CCC_ARCHIVE     | -                        | CCC_ARCHIV         | file and filegroup removed                         |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| CCC_AXN         | -                        | CCC_AXN            | file and filegroup removed                         |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| CCC_GDV         | -                        | CCC_GDV            | file and filegroup removed                         |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| PRIMARY         | -                        | CCC                | file renamed to PRIMARY_1                          |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| PRIMARY         | -                        | PRIMARY_2          | new file added                                     |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| CCC_APPLICATION | CCC_APPLICATION_new      | CCC_APPLICATION_1  | new filegroup renamed at the end                   |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| CCC_APPLICATION | CCC_APPLICATION_new      | CCC_APPLICATION_2  | new filegroup renamed at the end                   |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| CCC_ARCHIVE     | CCC_ARCHIVE_new          | CCC_ARCHIVE_1      | new filegroup renamed at the end                   |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| CCC_ARCHIVE     | CCC_ARCHIVE_new          | CCC_ARCHIVE_2      | new filegroup renamed at the end                   |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| CCC_AXN         | CCC_AXN_new              | CCC_AXN_1          | new filegroup renamed at the end                   |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| CCC_AXN         | CCC_AXN_new              | CCC_AXN_2          | new filegroup renamed at the end                   |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| CCC_GDV         | CCC_GDV_new              | CCC_GDV_1          | new filegroup renamed at the end                   |
+-----------------+--------------------------+--------------------+----------------------------------------------------+
| CCC_GDV         | CCC_GDV_new              | CCC_GDV_2          | new filegroup renamed at the end                   |
+-----------------+--------------------------+--------------------+----------------------------------------------------+

Espero que isto ajude um pouco. Há também um segundo banco de dados onde os nomes dos grupos de arquivos são diferentes, mas deixo isso de fora por uma questão de brevidade.

Martin Guth
fonte
11
Eu sei que não menciona SQL2017, mas gostaria de saber se o bug ainda está lá em 2017? support.microsoft.com/en-us/help/3132058/…
SqlWorldWide
@SqlWorldWide: Obrigado pelo link. No entanto, eu não tenho nenhum índice de texto completo e removi as funções e esquemas de partição ... portanto, acho que esse link não se aplica ao meu problema.
Martin Guth
Você pode imprimir todo o erro 5042, o estado indica qual é o problema.
Sean Gallardy - Usuário aposentado
@ SeanGallardy-Microsoft: Mensagem 5042, Nível 16, Status 8, Linha 1 O grupo de arquivos 'FG_AUDIT' não pode ser removido porque não está vazio.
Martin Guth
Obrigado :) O estado 8 significa que o grupo de arquivos está sendo usado por um objeto. Isso é diferente do artigo de suporte (esse é o estado 12, o que significa que o fg é usado por um esquema de partição). O que é estranho é que você disse que o grupo de arquivos não possui arquivos. Estou assumindo que vários backups de log (se completos / em massa) foram executados desde que o problema ocorreu?
Sean Gallardy - Usuário aposentado

Respostas:

4

Grupos de arquivos de verificação dupla no banco de dados

Verifique se o grupo de arquivos não tem nenhum arquivo anexado emitindo o seguinte comando:

use [DB]
go
sp_helpfilegroup 

Isso produzirá uma lista de grupos de arquivos:

 groupname | groupid | filecount
-----------+---------+-----------
 PRIMARY   | 1       | 1
 xyz       | 2       | 1

... e, em seguida, para cada grupo de arquivos listado, execute

use [DB]
go
sp_helpfilegroup @filegroupname='PRIMARY'
go
sp_helpfilegroup @filegroupname='xyz'

A saída pode ser assim:

 groupname | groupid | filecount
-----------+---------+------------
 xyz       | 2       | 1

.... e a segunda saída pode ser:

  file_in_group    | fileid | filename                          | size    | maxsize   | growth  
 ------------------+--------+-----------------------------------+---------+-----------+---------
  xyz_logical_name | 3      | X:\SQL\SQL_DATA\xyz_filegroup.ndf | 5120 KB | Unlimited | 1024 KB  

Excluindo o grupo de arquivos

Se você ainda tiver um arquivo associado a um de seus grupos de arquivos, o comando completo para excluir o arquivo lógico e o próprio grupo de arquivos será:

USE [DB]
GO
ALTER DATABASE [DB] REMOVE FILE [xyz_logical_name]
GO
ALTER DATABASE [DB] REMOVE FILEGROUP [xyz]
GO

O grupo de arquivos 'xyz' é padrão

Se você receber uma mensagem de erro ao tentar remover o arquivo lógico do grupo de arquivos parecido com este:

Msg 5031, Level 16, State 1, Line 88
Cannot remove the file 'xyz_logical_name' because it is the only file in the DEFAULT filegroup.

... então você terá que definir o grupo de PRIMARYarquivos como o grupo de DEFAULTarquivos:

ALTER DATABASE [DB] MODIFY FILEGROUP [PRIMARY] DEFAULT

O grupo de arquivos 'xyz' é somente leitura

No entanto, se a mensagem de erro for a seguinte:

Msg 5055, Level 16, State 2, Line 88 
Cannot add, remove, or modify file 'xyz_logical_name'. The file is read-only.

... você precisará remover a propriedade READ_ONLY no grupo de xyzarquivos:

ALTER DATABASE [DB] MODIFY FILEGROUP [xyz] READWRITE

Agora você deve poder descartar o arquivo lógico do grupo de arquivos e o próprio grupo de arquivos.

Transações em aberto

Se você realmente não possui um arquivo (nome_lógico / nome_do_arquivo_pilífico) associado ao grupo de arquivos xyzque está tentando excluir, a execução de um backup do log de transações pode liberar qualquer transação que impeça a exclusão adicional do grupo de arquivos.

Dial 911

Se tudo mais falhar, considere abrir uma chamada com a Microsoft.


Incompatibilidade de Metadados

Adicionado após pesquisas adicionais

Aparentemente, existem casos em que os metadados no banco de dados não refletem a localização real dos objetos.

Referência:
- CORRECÇÃO: Erro de inconsistência de metadados após alternar partições de tabela e soltar arquivos e grupos de arquivos correspondentes (Suporte da Microsoft)
- CORREÇÃO: Erro ocorre quando você tenta soltar ou excluir grupos de arquivos ou esquemas e funções de partição no SQL Server (Suporte da Microsoft)

Esses dois casos parecem ter sido resolvidos com a Atualização Cumulativa 3 para o SQL Server 2014 SP1 e a Atualização Cumulativa 1 para o SQL Server 2016, respectivamente. Eles não se aplicam à sua situação, mas mostram que às vezes os metadados podem estar errados.

O item que parece estar bloqueando a exclusão do grupo de arquivos é o índice, que pode ser armazenado com metadados incorretos.

Solução possível

Considere a possibilidade de reconstruir o índice Ref90159CCCmencionado na mensagem de erro.

Cannot process rowset ID 72057594712162304 of object 
"STORY_TRANSLATIONSCCC" (ID 120387498), index "Ref90159CCC" (ID 2), 
because it resides on filegroup "CCC_APPLICATION_new" (ID 8), 
which was not checked.

O artigo a seguir descreve uma situação semelhante e mostra como o autor detectou o culpado e resolveu a situação.

Referência: SQL Server: problema de inconsistência na partição e nos metadados do comutador (Blog dbi-services.com)


Localizar objetos relacionados ao grupo de arquivos obsoleto

Eu montei esse script para verificar o máximo possível de esconderijos para tabelas / índices / partições / etc. que ainda possam estar relacionados ao arquivo do grupo de arquivos descartado:

Substitua DEFAULTROpelo nome do seu grupo de arquivos obsoleto (por exemplo CCC_APPLICATION)

 /* ==================================================================
  Author......: hot2use
  Date........: 16.02.2018
  Version.....: 0.1
  Server......: LOCALHOST (first created for)
  Database....: StackExchange
  Owner.......: -
  Table.......: -
  Type........: Script
  Name........: ADMIN_Filegroup_Statement_All_Objects.sql
  Description.: Checks all objects related to filegroups based on the 
  ............  relationship between the data_space_id ID.
  ............      
  History.....:  0.1    h2u First created
  ............      
  ............      
 ================================================================== */
DECLARE @nvObsoleteFG AS NVARCHAR(50)
SET @nvObsoleteFG = N'DEFAULTRO'

SELECT -- DISTINCT use in conjunction with sys.allocation_units table and objects
       '-->'                            AS DataSpaceNfo
      ,ds.name                          AS DataSpaceName
      ,ds.data_space_id                 AS DatSpacID_DataSpace
      ,'-->'                            AS FileGroupNfo
      ,f.name                           AS FileGrpName
      ,f.data_space_id                  AS DatSpacID_FileGrp
      ,f.[type]                         AS FileGrpType
      ,'-->'                            AS DataBaseFilesNfo
      ,df.data_space_id                 AS DatSpacID_DBFiles
      ,df.[type]                        AS DBFilesType
      ,df.name                          AS DBFilesName
      ,'-->'                            AS ObjectNfo
      ,o.[object_id]                    AS OjbID
      ,o.name                           AS ObjName4HeapsClusters
      ,o.type_desc                      AS ObjTypeDesc
      ,'-->'                            AS IndexNfo
      ,i.name                           AS ObjName4Indexes
      ,i.type_desc                      AS IndTypeDesc
      ,i.[object_id]                    AS IndObjID
      ,i.index_id                       AS IndIndID
      ,'-->'                            AS PartSchemaNfo
      ,ps.name                          AS PartSchemaName
      ,ps.data_space_id                 AS DatSpacID_PartSchema
       -- ,au.type_desc                     AS AllocUnitTypeDesc
       -- ,au.data_space_id                 AS DatSpacID_AllocUnit
FROM   sys.data_spaces                  AS ds
       FULL JOIN sys.filegroups         AS f
            ON  ds.data_space_id = f.data_space_id
       FULL JOIN sys.database_files     AS df
            ON  f.data_space_id = df.data_space_id
       FULL JOIN sys.indexes            AS i
            ON  f.data_space_id = i.data_space_id
       FULL JOIN sys.partition_schemes  AS ps
            ON  f.data_space_id = ps.data_space_id
       FULL JOIN sys.objects            AS o
            ON  i.[object_id] = o.[object_id]         
       -- FULL JOIN sys.allocation_units   AS au
       --      ON  au.data_space_id = f.data_space_id

-- If you omit the whole WHERE clause you get an overview of everything (incl. MS objects)
WHERE  o.is_ms_shipped = 0
       -- if you omit the lower AND you'll get all items related to all filegroups
       AND (
               df.data_space_id=(
                   SELECT data_space_id
                   FROM   sys.filegroups
                   WHERE  NAME = @nvObsoleteFG
               )
               OR f.data_space_id=(
                      SELECT data_space_id
                      FROM   sys.filegroups
                      WHERE  NAME = @nvObsoleteFG
                  ) 
               OR df.data_space_id=(
                      SELECT data_space_id
                      FROM   sys.filegroups
                      WHERE  NAME = @nvObsoleteFG
                  )
               OR ps.data_space_id=(
                      SELECT data_space_id
                      FROM   sys.filegroups
                      WHERE  NAME = @nvObsoleteFG
                  )
           )

Referência: Meu script pessoal

Execute-o e veja se algum objeto é exibido contendo seu grupo de arquivos obsoleto. Vá com o data_space_ide não com o nome. As junções são intencionalmente FULLpara capturar quaisquer referências "órfãs".

Como alternativa, use esse script menor para verificar rapidamente itens no grupo de arquivos obsoleto:

SELECT o.[name]
      ,o.[type]
      ,i.[name]
      ,i.[index_id]
      ,f.[name]
FROM   sys.indexes i
       INNER JOIN sys.filegroups f
            ON  i.data_space_id = f.data_space_id
       INNER JOIN sys.all_objects o
            ON  i.[object_id] = o.[object_id]
WHERE  i.data_space_id = f.data_space_id
       AND o.type = 'U' -- User Created Tables

Referência: SQL SERVER - Listar todos os objetos criados em todos os grupos de arquivos no banco de dados (SQLAuthority.com)

John aka hot2use
fonte
sp_helpfilegroup: FG_AUDIT groupid 2 filecount 0 sem arquivos
Martin Guth
banco de dados está no modelo de recuperação simples e não há atualmente transações não abertos (no testserver como um todo)
Martin Guth
Obrigado pelo feedback. Você poderia adicionar essas informações à sua pergunta? Darei à sua mensagem de erro ( ...CCC_APPLICATION_new...; esse é o grupo de arquivos temporário?) Mais algumas idéias e tentarei reproduzir no meu ambiente.
John aka hot2use
11
CCC_APPLICATION_new não é o grupo de arquivos temporário ... é o grupo de arquivos para o qual o conteúdo foi movido ... deve ser renomeado para "CCC_APPLICATION" ... no entanto, isso só funciona se o grupo de arquivos sem arquivo associado chamado CCC_APPLICATION tiver sido descartado ou renomeado (mas não quer tê-lo pendurado ao redor)
Martin Guth
11
para esclarecimento: Estou com o problema em dois bancos de dados diferentes ... um com o antigo grupo de arquivos CCC_APPLICATION e o novo grupo de arquivos CCC_APPLICATION_new e o outro com o antigo grupo de arquivos FG_AUDIT e o novo grupo de arquivos CCC_AUDIT
Martin Guth
2

Após quatro meses, o Suporte da Microsoft encontrou uma solução. Havia de fato uma tabela referente a esse grupo de arquivos presumivelmente vazio.

A tabela foi identificada pela seguinte declaração:

SELECT t.[name] FROM sys.tables t
   inner join sys.filegroups f
         on t.lob_data_space_id = f.data_space_id
   where f.name = 'xyz'

Após mover os dados para uma nova tabela e soltar a tabela problemática, o grupo de arquivos foi removido com êxito. O processo de mover os dados foi: criar uma nova tabela com a mesma estrutura e índices, copiar dados via SELECT INTO, soltar tabela antiga, renomear nova tabela (e, claro, cuidar de chaves estrangeiras, se houver alguma em todo o processo) )

Martin Guth
fonte
Eu estava procurando essa resposta há anos. Muito obrigado!
Christian4145