누가 일부 데이터 SQL Server를 삭제했는지 확인하는 방법


29

상사는 어제 고객으로부터 SQL Server 데이터베이스에서 누가 일부 데이터를 삭제했는지 알아내는 방법을 묻는 질문을 받았습니다.

나는 이것이 트랜잭션 로그에서 찾을 수 있다고 생각했습니다 (잘리지 않은 경우)-이것이 맞습니까? 그렇다면 실제로이 정보를 어떻게 찾을 수 있습니까?

답변:


35

Express에서 fn_dblog를 시도하지 않았지만 사용 가능한 경우 다음 작업을 수행하면 삭제 작업이 수행됩니다.

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

관심있는 거래의 거래 ID를 가져 와서 다음과 같이 거래를 시작한 SID를 식별하십시오.

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

그런 다음 SID에서 사용자를 식별하십시오.

SELECT
    *
FROM 
    sysusers
WHERE
    [sid] = @SID

편집 : 지정된 테이블에서 삭제를 찾기 위해 모두 모으십시오.

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]

이것은 실제로 SQL Express에서 작동하지만 내 시스템에서는 오늘 발생한 트랜잭션 만 보여줍니다. SQL Express에 즉시 트랜잭션 로그 잘림이 있다고 생각하지 않습니까?
Matt Wilko

5
데이터베이스가 단순 복구 모델 인 경우 비활성 트랜잭션이 로그에서 얼마나 오래 지속되는지에 대한 가정을 할 수 없습니다.
Aaron Bertrand

3
트랜잭션 로그는 옵션이 아니라 기본입니다. 데이터베이스의 복구 모델 (단순 또는 전체)은 무엇이며 백업 구성 방법 (전체 만 또는 로그 백업 + 전체)은 무엇입니까?
Mark Storey-Smith

나는 자기 조인을 피하기 위해 조금 리팩토링했지만 여기에 내 대답을 위해 이것을 훔쳤다 fn_dblog. 한 가지 단점은 USERNAME()훨씬 더 유용한 로그인 이름 대신 데이터베이스를 반환 한다는 것입니다.
Martin Smith

3

데이터베이스가 전체 복구 모드이거나 트랜잭션 로그 백업이있는 경우 타사 로그 리더를 사용하여 데이터베이스를 읽으려고 시도 할 수 있습니다.

ApexSQL Log (프리미엄이지만 무료 평가판 사용) 또는 SQL Log Rescue (무료이지만 SQL 2000 만 해당)를 사용해 볼 수 있습니다 .


3

SQL Server 데이터베이스에서 일부 데이터를 삭제 한 사람을 찾는 방법

이것이 대답되었지만 SQL Server에 기본 추적이 활성화되어 있으며 누가 개체를 삭제 / 변경했는지 확인할 수 있습니다.

객체 이벤트

오브젝트 이벤트에는 오브젝트 변경, 오브젝트 작성 및 오브젝트 삭제가 포함됩니다.

참고 : SQL Server에는 기본적으로 5 개의 추적 파일 (각 20MB)이 있으며이를 지원하는 알려진 방법은 없습니다. 사용중인 시스템이있는 경우 추적 파일이 너무 빨리 롤오버되어 (몇 시간 내에도) 일부 변경 사항을 포착하지 못할 수 있습니다.

탁월한 예를 찾을 수 있습니다. SQL Server의 기본 추적-성능 및 보안 감사의 힘


1

이 절차를 수행하여 로그 백업 파일을 쿼리하고 테이블 열의 특정 값이 아직 / 마지막으로 존재하는 로그 백업 파일을 찾을 수 있습니다.

사용자를 찾기 위해 마지막으로 존재했던 로그 백업 값을 찾은 후 해당 로그 백업까지 데이터베이스를 복원 한 다음 Mark Storey-Smith 의 답변 을 따를 수 있습니다.

일부 전제 조건

  • 어떤 열에서 어떤 값이 삭제되었는지 파악
  • 전체 복구 모델에 있고 로그 백업을 수행하고 있습니다
  • Ola Hallengren의 솔루션을 사용할 때와 같이 로그 백업에 날짜 또는 식별자가 있습니다.

기권

이 솔루션은 방수 기능과는 거리가 멀고 더 많은 작업이 필요합니다.

대규모 환경이나 몇 가지 작은 테스트 이외의 환경에서는 테스트되지 않았습니다. 현재 실행은 SQL Server 2017에서 수행되었습니다.

라이브 데이터베이스 로그의 내용 대신 로그 백업 의 내용으로 작업하도록 수정 한 Muhammad Imran의 아래 절차 를 사용할 수 있습니다 .

이렇게하면 기술적으로 복원을 수행하지 않고 임시 테이블에 로그 내용을 덤프합니다. 아마 여전히 느릴 것이며 버그와 이슈에 매우 개방적입니다. 그러나 이론 상으로는 효과가있을 수 있습니다.

저장 프로시 저는 문서화되지 않은 fn_dump_dblog기능을 사용 하여 로그 파일을 읽습니다.


테스트 환경

일부 데이터베이스를 삽입하고 2 개의 로그 백업을 수행하고 3 번째 로그 백업에서는 모든 행을 삭제하는이 데이터베이스를 고려하십시오.

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

절차

여기 에서 저장 프로 시저를 찾아 다운로드 할 수 있습니다 .

문자 제한보다 커서 여기에 추가 할 수 없으며이 대답을보다 명확하게 만들 수 없습니다.

이 외에도 절차를 실행할 수 있어야합니다.

절차 실행

예를 들어, 모든 로그 파일 ( 4)을 저장 프로 시저에 추가하고 value1을 찾는 프로 시저를 실행할 때

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

이것은 나를 얻는다 :

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

작업이 마지막으로 value1발생한 시간을 찾을 수있는 위치 는에서 삭제 log3.trn합니다.

다른 열이있는 테이블을 추가하여 테스트 데이터 추가

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

로그 파일 이름 변경 및 절차 다시 실행

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

결과

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

2에서 정수 ( )를 검색하는 새로운 실행val3dbo.WrongDeletes2

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

결과

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

적용 마크 스토리 - 스미스 의 대답을

이제 세 번째 로그 파일에서 발생했음을 알았습니다. 해당 시점까지 복원하겠습니다.

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

그의 답변에서 마지막 쿼리 실행

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]

나에 대한 결과 (sysadmin)

UserName    TransactionStartTime
dbo 2019/08/09 17:14:10:450
dbo 2019/08/09 17:14:10:450
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.