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.
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:
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:
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é.
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) )
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
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
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
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.
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.
Si tout le reste échoue, vous voudrez peut-être envisager d'ouvrir un appel avec Microsoft.
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.
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)
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)