web-dev-qa-db-fra.com

Comment savoir qui a supprimé certaines données SQL Server

Mon patron a demandé hier à un client comment il pouvait savoir qui avait supprimé certaines données de sa base de données SQL Server (c'est l'édition express si cela compte).

Je pensais que cela pouvait être trouvé dans le journal des transactions (à condition qu'il n'ait pas été tronqué) - est-ce correct? Et si oui, comment allez-vous réellement trouver ces informations?

29
Matt Wilko

Je n'ai pas essayé fn_dblog sur Express mais s'il est disponible, ce qui suit vous donnera des opérations de suppression:

SELECT 
    * 
FROM 
    fn_dblog(NULL, NULL) 
WHERE 
    Operation = 'LOP_DELETE_ROWS'

Prenez l'ID de transaction pour les transactions qui vous intéressent et identifiez le SID qui a initié la transaction avec:

SELECT
    [Transaction SID]
FROM
    fn_dblog(NULL, NULL)
WHERE
    [Transaction ID] = @TranID
AND
    [Operation] = 'LOP_BEGIN_XACT'

Identifiez ensuite l'utilisateur à partir du SID:

SELECT
    *
FROM 
    sysusers
WHERE
    [sid] = @SID

Edit: Rassembler tout cela pour trouver des suppressions sur une table spécifiée:

DECLARE @TableName sysname
SET @TableName = 'dbo.Table_1'

SELECT
    u.[name] AS UserName
    , l.[Begin Time] AS TransactionStartTime
FROM
    fn_dblog(NULL, NULL) l
INNER JOIN
    (
    SELECT
        [Transaction ID]
    FROM 
        fn_dblog(NULL, NULL) 
    WHERE
        AllocUnitName LIKE @TableName + '%'
    AND
        Operation = 'LOP_DELETE_ROWS'
    ) deletes
ON  deletes.[Transaction ID] = l.[Transaction ID]
INNER JOIN
    sysusers u
ON  u.[sid] = l.[Transaction SID]
35
Mark Storey-Smith

Si la base de données est en mode de récupération complète ou si vous disposez de sauvegardes du journal des transactions, vous pouvez essayer de les lire à l'aide de lecteurs de journaux tiers.

Vous pouvez essayer ApexSQL Log (premium mais a un essai gratuit) ou SQL Log Rescue (gratuit mais sql 2000 uniquement).

3
Tony Melanik

comment ils pourraient savoir qui a supprimé certaines données dans leur base de données SQL Server

Bien que cela soit répondu, je voulais ajouter que SQL Server a une trace par défaut activée et qu'elle peut être utilisée pour savoir qui a déposé/modifié les objets.

Événements d'objet

Les événements d'objet incluent: Objet modifié, Objet créé et Objet supprimé

note: SQL Server par défaut a 5 fichiers de trace, 20 Mo chacun et il n'y a aucune méthode connue prise en charge pour changer cela. Si vous avez un système occupé, les fichiers de trace peuvent rouler beaucoup trop rapidement (même en quelques heures) et vous ne pourrez peut-être pas détecter certaines des modifications.

Un excellent exemple peut être trouvé: La trace par défaut dans SQL Server - la puissance des performances et des audits de sécurité

3
Kin Shah

Vous pouvez essayer cette procédure pour interroger les fichiers de sauvegarde de journal et trouver dans quel (s) fichier (s) de sauvegarde de journal une valeur spécifique d'une colonne d'une table était encore/dernière présente.

Pour trouver l'utilisateur, après avoir trouvé dans quelle sauvegarde de journal la dernière valeur existait, vous pouvez restaurer une base de données jusqu'à cette sauvegarde de journal, puis suivre la réponse de Mark Storey-Smith .

Quelques prérequis

  • savoir quelles valeurs de quelles colonnes ont été supprimées
  • Sont sous le modèle de récupération complète et prennent des sauvegardes de journaux
  • vous avez des dates ou des identifiants dans vos sauvegardes de journaux, comme lors de l'utilisation de la solution d'Ola Hallengren

Clause de non-responsabilité

Cette solution est loin d'être étanche, et beaucoup de travail reste à faire.

Il n'a pas été testé sur des environnements à grande échelle, ni même sur des environnements autres que quelques petits tests. L'exécution actuelle était sur SQL Server 2017.

Vous pouvez utiliser ci-dessous procédure de Muhammad Imran que j'ai modifié pour travailler avec le contenu de les sauvegardes de journaux au lieu du contenu du journal d'une base de données active.

De cette façon, vous n'effectuez techniquement pas de restauration, mais plutôt le vidage du contenu du journal dans une table temporaire. Il sera probablement encore lent et très ouvert aux bugs et problèmes. Mais cela pourrait fonctionner, en théorie ™.

La procédure stockée utilise la fonction non documentée fn_dump_dblog Pour lire les fichiers journaux.


Environnement de test

Considérez cette base de données, où nous insérons quelques lignes, effectuons 2 sauvegardes de journal et lors de la troisième sauvegarde de journal, nous supprimons toutes les lignes.

CREATE DATABASE WrongDeletesDatabase
GO
USE WrongDeletesDatabase
GO
BACKUP DATABASE WrongDeletesDatabase TO DISK ='c:\temp\Full.bak'

ALTER DATABASE WrongDeletesDatabase SET RECOVERY FULL
GO

CREATE TABLE dbo.WrongDeletes(ID INT, val varchar(255))

INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (1,'value1')
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log1.trn'
GO
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (2,'value2')
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log2.trn'
GO
DELETE FROM dbo.WrongDeletes
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log3.trn'
GO
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (3,'value3')
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log4.trn'
GO

La procédure

Vous pouvez trouver et télécharger la procédure stockée ici .

Je ne pourrais pas l'ajouter ici car il est plus grand que la limite de caractères, et rendrait cette réponse encore moins claire qu'elle ne l'est.

En dehors de cela, vous devriez pouvoir exécuter la procédure.

Exécution de la procédure

Un exemple de cela, lorsque j'ajoute tous mes fichiers journaux (4) À la procédure stockée et exécute la procédure en recherchant value1

EXEC dbo.Recover_Deleted_Data_Proc  @Database_Name= 'WrongDeletesDatabase',
                                    @SchemaName_n_TableName= 'dbo.WrongDeletes', 
                                    @SearchString = 'value1', 
                                    @SearchColumn = 'val',
                                    @LogBackupFolder ='C:\temp\Logs\'

Cela me fait:

ID  val LogFileName
1   value1  c:\temp\Logs\log3.trn
1   value1  c:\temp\Logs\log1.trn

Où nous pouvons trouver la dernière fois qu'une opération sur value1 S'est produite, la suppression dans log3.trn.

Quelques données de test supplémentaires, en ajoutant un tableau avec différentes colonnes

CREATE TABLE dbo.WrongDeletes2(Wow varchar(255), Anotherval varchar(255),Val3 int)

INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (1,'value1')
INSERT INTO dbo.WrongDeletes2(wOw,Anotherval,Val3)
VALUES ('b','value1',1)
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log1_1.trn'
GO
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (2,'value2')
INSERT INTO dbo.WrongDeletes2(wOw,Anotherval,Val3)
VALUES ('c','value2',2)
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log2_1.trn'
GO
DELETE FROM dbo.WrongDeletes
DELETE FROM dbo.WrongDeletes2
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log3_1.trn'
GO
INSERT INTO dbo.WrongDeletes(ID,val)
VALUES (3,'value3')
INSERT INTO dbo.WrongDeletes2(wOw,Anotherval,Val3)
VALUES ('d','value3',3)
GO
BACKUP LOG WrongDeletesDatabase TO DISK = 'c:\temp\Logs\log4_1.trn'
GO

Modification des noms des fichiers journaux et réexécution de la procédure

EXEC dbo.Recover_Deleted_Data_Proc  @Database_Name= 'WrongDeletesDatabase',
                                    @SchemaName_n_TableName= 'dbo.WrongDeletes', 
                                    @SearchString = 'value1', 
                                    @SearchColumn = 'val',
                                    @LogBackupFolder ='C:\temp\Logs\'

Résultat

ID  val LogFileName
1   value1  c:\temp\Logs\log1_1.trn
1   value1  c:\temp\Logs\log3_1.trn
1   value1  c:\temp\Logs\log3_1.trn

Nouvelle exécution, recherchant l'entier (2) Dans la colonne val3 De dbo.WrongDeletes2

EXEC dbo.Recover_Deleted_Data_Proc  @Database_Name= 'WrongDeletesDatabase',
                                    @SchemaName_n_TableName= 'dbo.WrongDeletes2', 
                                    @SearchString = '2', 
                                    @SearchColumn = 'Val3',
                                    @LogBackupFolder ='C:\temp\Logs\'

Résultat

Anotherval  Val3    Wow LogFileName
value2  2   c   c:\temp\Logs\log2.trn
value2  2   c   c:\temp\Logs\log3.trn

Appliquer Mark Storey-Smith réponse

Nous savons maintenant que cela s'est produit dans le troisième fichier journal, restaurons jusqu'à ce point:

USE master
GO
ALTER DATABASE WrongDeletesDatabase SET OFFLINE WITH ROLLBACK IMMEDIATE
GO
ALTER DATABASE WrongDeletesDatabase SET ONLINE 
GO
RESTORE DATABASE WrongDeletesDatabase FROM DISK = 'c:\temp\Logs\Full.bak' WITH NORECOVERY,REPLACE
RESTORE LOG WrongDeletesDatabase FROM DISK = 'c:\temp\Logs\log1.trn' WITH NORECOVERY
RESTORE LOG WrongDeletesDatabase FROM DISK = 'c:\temp\Logs\log2.trn' WITH NORECOVERY
RESTORE LOG WrongDeletesDatabase FROM DISK = 'c:\temp\Logs\log3.trn' WITH RECOVERY
GO
USE WrongDeletesDatabase
GO

Exécution de la dernière requête dans sa réponse

SELECT
    u.[name] AS UserName
    , l.[Begin Time] AS TransactionStartTime
FROM
    fn_dblog(NULL, NULL) l
INNER JOIN
    (
    SELECT
        [Transaction ID]
    FROM 
        fn_dblog(NULL, NULL) 
    WHERE
        AllocUnitName LIKE @TableName + '%'
    AND
        Operation = 'LOP_DELETE_ROWS'
    ) deletes
ON  deletes.[Transaction ID] = l.[Transaction ID]
INNER JOIN
    sysusers u
ON  u.[sid] = l.[Transaction SID]

Résultat pour moi (sysadmin)

UserName    TransactionStartTime
dbo 2019/08/09 17:14:10:450
dbo 2019/08/09 17:14:10:450
1
Randi Vertongen