Làm thế nào để tìm ra ai đã xóa một số dữ liệu SQL Server


29

Sếp của tôi đã có một truy vấn từ một khách hàng ngày hôm qua hỏi làm thế nào họ có thể tìm ra ai đã xóa một số dữ liệu trong cơ sở dữ liệu SQL Server của họ (đó là phiên bản express nếu có vấn đề).

Tôi nghĩ rằng điều này có thể được tìm thấy từ nhật ký giao dịch (miễn là nó không bị cắt ngắn) - điều này có đúng không? Và nếu vậy làm thế nào để bạn thực sự đi tìm thông tin này?

Câu trả lời:


35

Tôi chưa thử fn_dblog trên Express nhưng nếu có sẵn, phần sau sẽ cung cấp cho bạn các thao tác xóa:

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

Lấy ID giao dịch cho các giao dịch bạn quan tâm và xác định SID đã bắt đầu giao dịch với:

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

Sau đó xác định người dùng từ SID:

SELECT
    *
FROM 
    sysusers
WHERE
    [sid] = @SID

Chỉnh sửa: Kết hợp tất cả lại với nhau để tìm xóa trên một bảng được chỉ định:

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]

Điều này thực sự hoạt động với SQL express nhưng trên hệ thống của tôi, nó chỉ hiển thị các giao dịch xảy ra ngày hôm nay. Tôi không nghĩ rằng SQL Express đã bị cắt bớt nhật ký giao dịch?
Matt Wilko

5
Nếu cơ sở dữ liệu của bạn ở trong mô hình khôi phục đơn giản, bạn không thể đưa ra bất kỳ giả định nào về thời gian các giao dịch không hoạt động sẽ tồn tại trong nhật ký.
Aaron Bertrand

3
Nhật ký giao dịch là cơ bản, chứ không phải là tùy chọn. Mô hình phục hồi cho cơ sở dữ liệu (đơn giản hay đầy đủ) là gì và các bản sao lưu được cấu hình (chỉ toàn bộ hoặc sao lưu nhật ký + đầy đủ)?
Mark Storey-Smith

Tôi đã đánh cắp điều này cho câu trả lời của tôi ở đây mặc dù được tái cấu trúc một chút để tránh việc tự tham gia fn_dblog. Một nhược điểm là nó trả về cơ sở dữ liệu USERNAME()chứ không phải tên đăng nhập hữu ích hơn nhiều.
Martin Smith

3

Nếu cơ sở dữ liệu ở chế độ khôi phục hoàn toàn hoặc nếu bạn có bản sao lưu nhật ký giao dịch, bạn có thể thử đọc chúng bằng trình đọc nhật ký của bên thứ ba.

Bạn có thể dùng thử ApexSQL Log (cao cấp nhưng có bản dùng thử miễn phí) hoặc SQL Log Rescue (chỉ miễn phí nhưng chỉ có sql 2000).


3

làm thế nào họ có thể tìm ra ai đã xóa một số dữ liệu trong cơ sở dữ liệu SQL Server của họ

Mặc dù điều này đã được trả lời, muốn thêm rằng SQL Server có bật theo dõi mặc định và nó có thể được sử dụng để tìm ra ai đã đánh rơi / thay đổi các đối tượng.

Sự kiện đối tượng

Các sự kiện đối tượng bao gồm: Đối tượng đã thay đổi, Đối tượng đã tạo và Đối tượng đã xóa

chú thích: SQL Server theo mặc định có 5 tệp theo dõi, mỗi tệp 20 MB và không có phương pháp được hỗ trợ nào được biết để thay đổi điều này. Nếu bạn có một hệ thống bận rộn, các tệp theo dõi có thể cuộn quá nhanh (thậm chí trong vài giờ) và bạn có thể không bắt được một số thay đổi.

Có thể tìm thấy ví dụ tuyệt vời: Theo dõi mặc định trong SQL Server - sức mạnh của kiểm toán hiệu suất và bảo mật


1

Bạn có thể thử quy trình này để truy vấn các tệp sao lưu nhật ký và tìm trong đó (các) tệp sao lưu nhật ký một giá trị cụ thể của một cột của bảng vẫn còn / hiện tại.

Để tìm người dùng, sau khi bạn tìm thấy bản sao lưu nhật ký giá trị tồn tại lần cuối, bạn có thể khôi phục cơ sở dữ liệu cho đến khi sao lưu nhật ký đó và sau đó làm theo câu trả lời của Mark Storey-Smith .

Một số điều kiện tiên quyết

  • biết những giá trị nào từ các cột đã bị xóa
  • Đang trong mô hình khôi phục đầy đủ và đang thực hiện sao lưu nhật ký
  • bạn có ngày hoặc số nhận dạng trong bản sao lưu nhật ký của mình, chẳng hạn như khi sử dụng giải pháp của Ola Hallengren

Khước từ

Giải pháp này là không thấm nước, và nhiều công việc cần phải đi vào nó.

Nó chưa được thử nghiệm trên các môi trường quy mô lớn, hoặc thậm chí bất kỳ môi trường nào ngoài một vài thử nghiệm nhỏ. Chạy hiện tại là trên SQL Server 2017.

Bạn có thể sử dụng quy trình dưới đây từ Muhammad Imran mà tôi đã sửa đổi để làm việc với nội dung của bản sao lưu nhật ký thay vì nội dung của nhật ký cơ sở dữ liệu trực tiếp.

Bằng cách này, về mặt kỹ thuật, bạn không thực hiện khôi phục mà thay vào đó bỏ nội dung nhật ký vào một bảng tạm thời. Nó có thể vẫn sẽ chậm, và rất cởi mở với các lỗi và vấn đề. Nhưng nó có thể hoạt động, theo lý thuyết ™.

Thủ tục được lưu trữ sử dụng fn_dump_dblogchức năng không có giấy tờ để đọc các tệp nhật ký.


Môi trường thử nghiệm

Hãy xem xét cơ sở dữ liệu này, nơi chúng tôi chèn một số hàng, lấy 2 bản sao lưu nhật ký và trên bản sao lưu nhật ký thứ ba, chúng tôi xóa tất cả các hàng.

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

Thủ tục

Bạn có thể tìm và tải về các thủ tục được lưu trữ ở đây .

Tôi không thể thêm nó ở đây vì nó lớn hơn giới hạn ký tự và sẽ làm cho câu trả lời này thậm chí còn ít rõ ràng hơn nó.

Ngoài ra, bạn sẽ có thể chạy các thủ tục.

Chạy thủ tục

Một ví dụ về điều này, khi tôi thêm tất cả các tệp nhật ký của mình ( 4) vào thủ tục được lưu trữ và chạy thủ tục tìm giá trị1

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

Điều này giúp tôi:

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

Nơi chúng ta có thể tìm thấy khi lần cuối hoạt động value1xảy ra, xóa tronglog3.trn .

Một số dữ liệu thử nghiệm khác, thêm một bảng với các cột khác nhau

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

Thay đổi tên tệp nhật ký và thực hiện lại quy trình

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

Kết quả

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

Một lần chạy mới, tìm kiếm số nguyên ( 2) trong val3cột củadbo.WrongDeletes2

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

Kết quả

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

Áp dụng Mark Storey-Smith câu trả lời của

Bây giờ chúng ta biết rằng nó đã xảy ra trong tệp nhật ký thứ ba, hãy khôi phục cho đến thời điểm đó:

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

Chạy truy vấn cuối cùng trong câu trả lời của anh ấy

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]

Kết quả cho tôi (sysadmin)

UserName    TransactionStartTime
dbo 2019/08/09 17:14:10:450
dbo 2019/08/09 17:14:10:450
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.