web-dev-qa-db-fra.com

Impossible de supprimer le groupe de fichiers sans aucun fichier associé

Je rencontre des messages d'erreur étranges sur SQL Server 2017 CU3. Je migre des bases de données et réorganise des groupes de fichiers. Par "réorganiser", je veux dire que j'utilise un procédure stockée qui crée une fonction de partition et un schéma de partition sur le nouveau groupe de fichiers pour un objet, reconstruit les index pendant le partitionnement puis supprime le partitionnement.

À la fin, j'ai des groupes de fichiers vides. Leurs fichiers sont supprimés . Les groupes de fichiers eux-mêmes sont également supprimés. Cela fonctionne bien dans la plupart des cas. Cependant, pour deux bases de données, j'ai supprimé les fichiers ... il reste un groupe de fichiers sans fichier associé mais

ALTER DATABASE REMOVE FILEGROUP

renvoie une erreur 5042:

Le groupe de fichiers 'xyz' ne peut pas être supprimé car il n'est pas vide.

Question

Comment puis-je me débarrasser de ce groupe de fichiers vide ... quel pourrait être le problème?

J'ai déjà lu certains problèmes courants, mais ils ne sont pas présents dans mon système:

  • Vérifié:

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

    0 lignes ... aucun objet de partitionnement laissé dans la base de données

  • UPDATE STATISTICS pour tous les objets de la base de données

    aucun effet

  • Vérifie les index sur le groupe de fichiers:

    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 lignes

  • Recherche les objets dans le groupe de fichiers:

    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 lignes

J'ai aussi donné DBCC SHRINKFILE avec le paramètre EMPTYFILE à essayer avant de supprimer le fichier du groupe de fichiers. Cela n'a pas vraiment de sens pour moi, mais j'ai lu des solutions pour décrire cela comme un correctif. N'a eu aucun effet de toute façon.


J'ai eu un peu d'espoir en lisant ceci question sur la panne du serveur et j'ai essayé ce qui suit:

  • Mettre à jour toutes les statistiques
  • Supprimer toutes les statistiques qui ne sont pas liées aux index

Mais cela n'a eu aucun effet. J'ai toujours un groupe de fichiers sans fichier associé et le groupe de fichiers ne peut pas être supprimé. Je suis totalement perplexe car cela se produit dans certaines bases de données et pas dans d'autres (avec la même structure). Quand j'exécute DBCC CHECK FILEGROUP sur ce groupe de fichiers vide, je reçois un tas de messages d'erreur comme celui-ci:

Impossible de traiter l'ID de jeu de lignes 72057594712162304 de l'objet "STORY_TRANSLATIONSCCC" (ID 120387498), index "Ref90159CCC" (ID 2), car il réside dans le groupe de fichiers "CCC_APPLICATION_new" (ID 8), qui n'a pas été vérifié.

Résultats DBCC pour 'STORY_TRANSLATIONSCCC'. Il y a 0 lignes dans 0 pages pour l'objet "STORY_TRANSLATIONSCCC".

Est-ce normal ou indique-t-il quelque chose d'inhabituel?

Cette question peut être un doublon, mais je ne trouve pas de correctif pour moi dans d'autres questions sur dba.stackexchange. Veuillez consulter la liste de ce que j'ai déjà essayé. Ceci est identique aux solutions décrites dans Impossible de supprimer les groupes de fichiers inutilisés .

Plus de détails

Peut-être que cela aide à comprendre ce que je fais avant que l'erreur ne se produise. Je prévois une migration vers un nouveau serveur. Je teste actuellement cela sur une instance de test. Les bases de données sont restaurées à partir du serveur prod et le modèle de récupération est passé à simple. Mon objectif est de restructurer les groupes de fichiers et de passer d'un modèle avec un fichier par groupe de fichiers à un modèle avec deux fichiers par groupe de fichiers. Pour y parvenir, je crée de nouveaux groupes de fichiers vides avec deux fichiers chacun et je déplace les données. Malheureusement, la plupart des objets ont des données LOB (XML et binaires) ... donc j'utilise le partitionnement comme une aide pour déplacer également les données lob. À la fin, toutes les données résident dans les nouveaux groupes de fichiers et les anciens groupes de fichiers sont vides. Ensuite, je supprime tous les fichiers et supprime également le groupe de fichiers respectif. Le groupe de fichiers principal reste et obtient juste un autre fichier ajouté. Vous pouvez trouver un exemple de script dans un autre question à moi . Ce processus fonctionne bien mais dans deux bases de données, les fichiers peuvent être supprimés mais pas le groupe de fichiers. Étonnamment, la structure de ces bases de données devrait être la même que celle des autres bases de données où aucun problème n'a été rencontré lors du déplacement des données et de la suppression des anciens groupes de fichiers.

Voici donc une liste des groupes de fichiers et des fichiers des deux bases de données où le problème se produit:

  1. CCC_GENTE

avant

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

après

    +-----------------+--------------------------+--------------------+----------------------------------------------------+
| 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                   |
+-----------------+--------------------------+--------------------+----------------------------------------------------+

J'espère que ça aide un peu. Il y a aussi une deuxième base de données où les noms de groupes de fichiers sont différents, mais je laisse cela pour plus de brièveté.

8
Martin Guth

Après quatre mois, le support Microsoft a trouvé une solution. Il y avait en effet un tableau faisant référence à ce groupe de fichiers vraisemblablement vide.

Le tableau a été identifié par la déclaration suivante:

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'

Après avoir déplacé les données vers une nouvelle table et supprimé la table problématique, le groupe de fichiers a été supprimé avec succès. Le processus de déplacement des données était le suivant: créer une nouvelle table avec la même structure et les mêmes index, copier les données via SELECT INTO, supprimer l'ancienne table, renommer la nouvelle table (et bien sûr, prendre soin des clés étrangères s'il y en a dans tout le processus) )

2
Martin Guth

Vérification des groupes de fichiers dans la base de données

Vérifiez que le groupe de fichiers ne contient aucun fichier en pièce jointe en exécutant la commande suivante:

use [DB]
go
sp_helpfilegroup 

Cela produira une liste de groupes de fichiers:

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

... puis pour chaque groupe de fichiers répertorié, exécutez

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

La sortie pourrait ressembler à ceci:

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

.... et la deuxième sortie pourrait être:

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

Suppression du groupe de fichiers

Si vous avez toujours un fichier associé à l'un de vos groupes de fichiers, la commande complète pour supprimer le fichier logique du groupe de fichiers et le groupe de fichiers lui-même serait:

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

Le groupe de fichiers 'xyz' est par défaut

Si vous recevez un message d'erreur lorsque vous essayez de supprimer le fichier logique du groupe de fichiers qui ressemble à ceci:

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.

... alors vous devrez définir le groupe de fichiers PRIMARY comme groupe de fichiers DEFAULT:

ALTER DATABASE [DB] MODIFY FILEGROUP [PRIMARY] DEFAULT

Le groupe de fichiers 'xyz' est en lecture seule

Cependant, si le message d'erreur est le suivant:

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

... alors vous devrez supprimer la propriété READ_ONLY du groupe de fichiers xyz:

ALTER DATABASE [DB] MODIFY FILEGROUP [xyz] READWRITE

Vous devriez maintenant pouvoir supprimer le fichier logique du groupe de fichiers et le groupe de fichiers lui-même.

Transactions ouvertes

Si vous n'avez vraiment pas de fichier (nom_logique/nom_fichier_ pyhsical) associé au groupe de fichiers xyz que vous essayez de supprimer, alors effectuer une sauvegarde du journal des transactions peut libérer toutes les transactions qui entravent la suppression du groupe de fichiers.

Composez le 911

Si tout le reste échoue, vous voudrez peut-être envisager d'ouvrir un appel avec Microsoft.


Inadéquation des métadonnées

Ajouté après de nouvelles recherches

Apparemment, il existe des cas où les métadonnées de la base de données ne reflètent pas l'emplacement réel des objets.

Référence:
- CORRECTIF: erreur d'incohérence des métadonnées après avoir changé de partition de table et supprimé les fichiers et groupes de fichiers correspondants (Support Microsoft)
- CORRECTIF: une erreur se produit lorsque vous essayez de supprimer ou de supprimer des groupes de fichiers ou des schémas de partition et des fonctions dans SQL Server (Support Microsoft)

Ces deux cas semblent avoir été résolus avec Cumulative Update 3 pour SQL Server 2014 SP1 et Cumulative Update 1 pour SQL Server 2016 respectivement. Ils ne s'appliquent pas à votre situation, mais ils montrent que parfois les métadonnées peuvent être erronées.

L'élément qui semble bloquer la suppression de votre groupe de fichiers est l'index, qui peut être stocké avec des métadonnées incorrectes.

Solution possible

Envisagez de reconstruire l'index Ref90159CCC qui est référencé dans le message d'erreur.

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.

L'article suivant décrit une situation similaire et montre comment l'auteur a détecté le coupable et a résolu la situation.

Référence:SQL Server: problème de cohérence des partitions et des métadonnées ( Blog dbi-services.com)


Rechercher des objets liés à un groupe de fichiers obsolètes

J'ai truqué ce script pour vérifier autant de cachettes possibles pour les tables/index/partitions/etc. qui pourrait toujours être lié au fichier de groupe de fichiers supprimé:

Veuillez remplacer DEFAULTRO par le nom de votre groupe de fichiers obsolète (par exemple 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
                  )
           )

Référence: Mon script personnel

Exécutez-le et voyez si des objets sont affichés contenant votre groupe de fichiers obsolète. Allez avec le data_space_id plutôt qu'avec le nom. Les jointures sont intentionnellement FULL pour intercepter toutes les références "orphelines".

Vous pouvez également utiliser ce petit script pour vérifier rapidement les éléments du groupe de fichiers obsolètes:

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

Référence:SERVEUR SQL - Liste tous les objets créés sur tous les groupes de fichiers de la base de données (SQLAuthority.com)

4
John aka hot2use