热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:(转)SQLServer查询数据库各种历史记录

篇首语:本文由编程笔记#小编为大家整理,主要介绍了(转)SQLServer查询数据库各种历史记录相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了(转)SQLServer查询数据库各种历史记录相关的知识,希望对你有一定的参考价值。



数据库启动记录

1. 最近一次启动SQL Server的时间




select sqlserver_start_time from sys.dm_os_sys_info;
--也可参考系统进程创建的时间,比服务启动时间略晚(秒级)
select login_time from sysprocesses where spid = 1
select login_time from sys.dm_exec_sessions where session_id = 1
--也可参考tempdb数据库创建的时间,比服务启动时间略晚(秒级)
select create_date from sys.databases
where database_id=2



 

2. 最近几次启动SQL Server的时间




--参考error log,系统默认保留6个归档,共7个文件
exec xp_readerrorlog 0,1, N\'SQL Server is starting\'
exec xp_readerrorlog 1,1, N\'SQL Server is starting\'
exec xp_readerrorlog 2,1, N\'SQL Server is starting\'
exec xp_readerrorlog 3,1, N\'SQL Server is starting\'
exec xp_readerrorlog 4,1, N\'SQL Server is starting\'
exec xp_readerrorlog 5,1, N\'SQL Server is starting\'
exec xp_readerrorlog 6,1, N\'SQL Server is starting\'
--之前关键字用N\'Server process ID is\'并不严谨,改为N\'SQL Server is starting\'



 

3. 历史上更多次启动SQL Server的时间

查看windows event log,SQL语句无法直接读取event log,如果想用命令行,可以试试VBS,Powershell。

Event Viewer/Windows logs下Application 或者 System 事件里都有服务启动的记录。

 

二. 登录数据库记录

1. 查看error log

默认情况下,只有失败的登录会被记录在error log里,如果想登录失败/成功都被记录到error log,需要开启如图选项:

 

用SQL语句修改注册表,也同样可以开启,键值对应关系如下:

0, None

1, Failed

2, Successful

3, Both failed and successful


USE [master]
GO
EXEC xp_instance_regwrite N\'HKEY_LOCAL_MACHINE\', N\'Software\\Microsoft\\MSSQLServer\\MSSQLServer\', N\'AuditLevel\', REG_DWORD, 3
GO

在error log里查看登录记录:


exec xp_readerrorlog 0,1, N\'Login\', N\'for user\', null, null, N\'DESC\'

 

2. 利用LOGON 触发器进行记录

从SQL Server 2005 SP2开始引入了LOGON Trigger,可以用它在登录时做个记录,实现如下:




--创建LOGON触发器
CREATE database DBA
GO
USE DBA
GO
IF OBJECT_ID(\'login_history\',\'U\') is not null
DROP TABLE login_history
GO
CREATE TABLE login_history
(
FACT_ID bigint IDENTITY(1,1) primary key,
LOGIN_NAME nvarchar(1024),
LOGIN_TIME datetime
)
GO
IF EXISTS(select 1 from sys.server_triggers where name = \'login_history_trigger\')
DROP TRIGGER login_history_trigger ON ALL SERVER
GO
CREATE TRIGGER login_history_trigger
ON ALL SERVER
FOR LOGON
AS
BEGIN
--IF SUSER_NAME() NOT LIKE \'NT AUTHORITY\\%\' AND
-- SUSER_NAME() NOT LIKE \'NT SERVICE\\%\'
IF ORIGINAL_LOGIN() NOT LIKE \'NT AUTHORITY\\%\' AND
ORIGINAL_LOGIN() NOT LIKE \'NT SERVICE\\%\'
BEGIN
INSERT INTO DBA..login_history
VALUES(ORIGINAL_LOGIN(),GETDATE());
END;
END;
GO
--登录后查看记录
SELECT * FROM login_history



 

3. 实例:查询某login的最后一次登录

系统表/试图里,并没有这样的字段记录,syslogins里accdate也是不对的,如果要查可以通过上面2个方法里的一种:

(1) ERROR LOG,得设置记录Login Auditing 的“Both failed and successful” 选项,默认为”Failed”;

(2) Logon Trigger;

 

创建,修改,删除记录 (DDL)

1. 服务器对象的创建,修改




--创建数据库
select name, create_date from sys.databases
--创建,修改登录
select name, createdate, updatedate from syslogins
select name, create_date, modify_date from sys.server_principals
--创建,修改LOGON触发器
select name, create_date, modify_date from sys.server_triggers



 

2. 数据库对象创建,修改


--创建,修改数据库对象
select name, create_date, modify_date from sys.objects
--创建,修改触发器,DDL触发器不在sys.objects里
select name, create_date, modify_date from sys.triggers

注意:

(1) 索引的创建,修改并没有记录




sys.objects --里面没有0,1 之外的索引
sys.indexes --里面没有日期
objectproperty() --没有日期属性
indexproperty() --没有日期属性
sys.dm_db_index_operational_stats
sys.dm_db_index_usage_stats
sys.dm_db_index_physical_stats --也都没有
STATS_DATE (table_id, index_id) --是索引的统计信息最后更新时间



(2) 关于creator和owner

SQL Server里只有owner,数据库里对象的owner必须是一个有效的database principal (user或者role),没有creator,很难知道是谁创建了这个对象,因为owner并不准确:

首先,数据库对象的owner可以被修改,ALTER AUTHORIZATION或者sp_changeobjectowner都行;

其次,就算owner没被修改过,默认情况下数据库对象的owner沿用schema的owner,除非在创建schema时特意指定了某个owner;

最后,系统表并没有记录creator,如果想要查询,也许得利用DDL 触发器来记录。

关于owner简单举例如下: 


 object owner

 

3. 默认跟踪里的创建,修改,删除对象 (create, alter, drop)

从sql server 2005开始引入了默认跟踪,这是sql server默认开启的跟踪,并定义了事件、文件大小,个数,查看定义如下:




--系统定义好的默认跟踪事件
select t.eventid, te.name
from (select distinct eventid from sys.fn_trace_geteventinfo(1)) t
inner join sys.trace_events te
on t.eventid = te.trace_event_id
--最多5个文件,每个文件20MB,依次滚动覆盖
select * from sys.traces
where id = 1



示例,利用默认跟踪查看删除数据库记录如下:




DECLARE @path varchar(1024)
SELECT @path = path
FROM sys.traces
WHERE id = 1
SELECT *
FROM fn_trace_gettable(@path, default) --default读取当前所有trace文件,包括正在用的
WHERE DatabaseName = \'DBA\'
and EventClass = 47 --46表示Create对象,47表示Drop对象,164表示修改对象
and ObjectType = 16964 --16964表示数据库



注意:

(1) 其他对象比如表的删除等也都可以查到;

(2) 默认跟踪返回的列值有很多定义,没有系统表记载,需要去翻帮助,比如ObjectType列值参考这个列表:

https://msdn.microsoft.com/en-us/library/ms180953.aspx

(3) 注意默认跟踪的时效性,5个20MB的文件,也许想要看的信息很快就被覆盖了;

(4)  truncate table并没有被默认跟踪记录。

 

数据库表的各种记录

汇总一下对表的各种历史操作记录的查看:

(1) create table, alter table记录,查看sys.objects 或者默认跟踪;

(2) drop table记录,查看默认跟踪;

(3) truncate table 也许只有去打开数据库log文件查看了,最后会简单介绍下;

(4) DML操作表中数据的记录,查看sys.dm_db_index_usage_stats,如下:




SELECT o.name as table_name,
s.last_user_seek,
s.last_user_scan,
s.last_user_lookup,
s.last_user_update
from sys.indexes i
left join sys.dm_db_index_usage_stats s
on s.object_id = i.object_id and
s.index_id = i.index_id
inner join sys.objects o
on i.object_id = o.object_id
where i.index_id <= 1
and o.is_ms_shipped = 0
order by o.name



注意:动态管理视图(DMV) 中采集来的信息都是从sql server启动后开始的,也就是说重启后就没了。

 

历史SQL语句记录

有些数据库本身,会记录所有历史的SQL命令。比如:mysql和pgsql都有专门的log文本文件来存放所有历史的SQL命令;

也有些数据库在保存log文本的同时,还保留最近的N条SQL命令在数据库里,以方便查询。

 

SQL Server并没有这样的实现,只有sys.dm_exec_query_stats缓存了一部分 (sql server服务开启后执行的语句,某些不被缓存执行计划的语句并不记录)。

这个视图主要是对执行计划的统计,包含消耗成本,运行次数等等,并没有session,user,每次被执行的时间等信息:




SELECT st.text as sql_statement,
qs.creation_time as plan_last_compiled,
qs.last_execution_time as plan_last_executed,
qs.execution_count as plan_executed_count,
qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.plan_handle) st
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
order by total_elapsed_time/execution_count desc



当然,开启跟踪,审计之类的方法,是可以记录所有操作的,但是这个开销有可能会影响系统性能,所以一般并不在生产环境启用。

 

数据库备份还原历史记录

备份还原的记录都在msdb里。

1. 备份记录




SELECT
bs.backup_set_id,
bs.database_name,
bs.backup_start_date,
bs.backup_finish_date,
CAST(CAST(bs.backup_size/1000000 AS INT) AS VARCHAR(14)) + \' \' + \'MB\' AS [Size],
CAST(DATEDIFF(second, bs.backup_start_date,
bs.backup_finish_date) AS VARCHAR(4)) + \' \' + \'Seconds\' [TimeTaken],
CASE bs.[type]
WHEN \'D\' THEN \'Full Backup\'
WHEN \'I\' THEN \'Differential Backup\'
WHEN \'L\' THEN \'TLog Backup\'
WHEN \'F\' THEN \'File or filegroup\'
WHEN \'G\' THEN \'Differential file\'
WHEN \'P\' THEN \'Partial\'
WHEN \'Q\' THEN \'Differential Partial\'
END AS BackupType,
bmf.physical_device_name,
CAST(bs.first_lsn AS VARCHAR(50)) AS first_lsn,
CAST(bs.last_lsn AS VARCHAR(50)) AS last_lsn,
bs.server_name,
bs.recovery_model
FROM msdb.dbo.backupset bs
INNER JOIN msdb.dbo.backupmediafamily bmf
ON bs.media_set_id = bmf.media_set_id
ORDER BY bs.server_name,bs.database_name,bs.backup_start_date;
GO



如果server_name是本机,那么备份是在本机生成的;

如果server_name是别的主机名,那么备份是被拿到本机做过数据库还原;

 

2. 还原纪录




SELECT
rs.[restore_history_id],
rs.[restore_date],
rs.[destination_database_name],
bmf.physical_device_name,
rs.[user_name],
rs.[backup_set_id],
CASE rs.[restore_type]
WHEN \'D\' THEN \'Database\'
WHEN \'I\' THEN \'Differential\'
WHEN \'L\' THEN \'Log\'
WHEN \'F\' THEN \'File\'
WHEN \'G\' THEN \'Filegroup\'
WHEN \'V\' THEN \'Verifyonly\'
END AS RestoreType,
rs.[replace],
rs.[recovery],
rs.[restart],
rs.[stop_at],
rs.[device_count],
rs.[stop_at_mark_name],
rs.[stop_before]
FROM [msdb].[dbo].[restorehistory] rs
INNER JOIN [msdb].[dbo].[backupset] bs
--on rs.backup_set_id = bs.media_set_id
ON rs.backup_set_id = bs.backup_set_id
INNER JOIN msdb.dbo.backupmediafamily bmf
ON bs.media_set_id = bmf.media_set_id
GO



还原数据库的时候是会写backupset和backupmediafamily系统表的,用来记录还原所用到的备份文件信息。

 

作业,维护计划,数据库邮件历史记录

作业,维护计划,数据库邮件的历史记录,也都在msdb里。

1. 作业历史记录




if OBJECT_ID(\'tempdb..#tmp_job\') is not null
drop table #tmp_job
--只取最后一次结果
select job_id,
run_status,
CONVERT(varchar(20),run_date) run_date,
CONVERT(varchar(20),run_time) run_time,
CONVERT(varchar(20),run_duration) run_duration
into #tmp_job
from msdb.dbo.sysjobhistory jh1
where jh1.step_id = 0
and (select COUNT(1) from msdb.dbo.sysjobhistory jh2
where jh2.step_id = 0
and (jh1.job_id = jh2.job_id)
and (jh1.instance_id <= jh2.instance_id))=1
--排除syspolicy_purge_history这个系统作业
select a.name job_name,
case b.run_status when 0 then \'Failed\'
when 1 then \'Succeeded\'
when 2 then \'Retry\'
when 3 then \'Canceled\'
else \'Unknown\'
end as job_status,
LEFT(run_date,4)+\'-\'+SUBSTRING(run_date,5,2)+\'-\'+RIGHT(run_date,2)
+SPACE(1)
+LEFT(RIGHT(1000000+run_time,6),2)+\':\'
+SUBSTRING(RIGHT(1000000+run_time,6),3,2)+\':\'
+RIGHT(RIGHT(1000000+run_time,6),2) as job_started_time,
+LEFT(RIGHT(1000000+run_duration,6),2)+\':\'
+SUBSTRING(RIGHT(1000000+run_duration,6),3,2)+\':\'
+RIGHT(RIGHT(1000000+run_duration,6),2) as job_duration
from msdb.dbo.sysjobs a
left join #tmp_job b
on a.job_id=b.job_id
where a.name not in (\'syspolicy_purge_history\')
and a.enabled = 1
order by b.run_status asc,a.name,b.run_duration desc



 

2. 维护计划历史记录


select * from msdb..sysdbmaintplan_history
--新的系统表也可以
select * from msdb..sysmaintplan_log
select * from msdb..sysmaintplan_logdetail

维护计划最终是作为作业在运行的,也可以直接查看同名作业的历史记录。

 

3. 数据库邮件历史记录




--直接查系统表
select * from msdb..sysmail_mailitems
select * from msdb..sysmail_log
--也可查看基于这2个系统表的系统视图
select * from msdb..sysmail_allitems
select * from msdb..sysmail_sentitems
select * from msdb..sysmail_unsentitems
select * from msdb..sysmail_faileditems
select * from msdb..sysmail_event_log
--更多系统表和视图
use msdb
GO
select * from sys.objects
where name like \'%sysmail%\'
and type in(\'U\',\'V\')
order by type,name



 

查看数据库日志文件

数据库日志文件里对于DDL,DML操作肯定是有记录的,有2个内置函数可以用来解析,但是并不那么轻松,简单介绍如下:

1. fn_dblog 读取当前在线的日志


select * from fn_dblog(null,null) --2个null代表起始的日志LSN

返回的结果集中字段定义:

(1) AllocUnitName: 对象名

(2) Operation: 操作类型,常见的有 \'LOP_INSERT_ROWS\', \'LOP_DELETE_ROWS\', \'LOP_MODIFY_ROW\'

(3) [RowLog Contents 0], [RowLog Contents 1], 2,3,4,5: 字段内容,但是是二进制的,和dbcc page看到的类似

 

试着查看truncate table记录如下:




IF OBJECT_ID(\'test_truncate\',\'U\') is not null
DROP TABLE test_truncate
GO
CREATE TABLE test_truncate(ID int)
INSERT INTO test_truncate values(1)
TRUNCATE TABLE test_truncate
--查看truncate table记录
select * from fn_dblog(null,null)
where AllocUnitName like \'%test_truncate%\'
and Description like \'Deallocated%\'



 

2. fn_dump_dblog 读取数据库备份里的日志

参数介绍:前面两2个NULL和fn_dblog一样代表起始的日志LSN,DISK表示设备类型,1表示备份文件个数,最多64个,这里以1个文件为例:




backup database DBA to disk = \'C:\\backup\\dba.bak\'
SELECT *
FROM
fn_dump_dblog (
NULL, NULL, N\'DISK\', 1, N\'C:\\backup\\dba.bak\',
DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT,
DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT,
DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT,
DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT,
DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT,
DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT,
DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT,
DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT,
DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT);
GO



推荐阅读
  • Oracle10g备份导入的方法及注意事项
    本文介绍了使用Oracle10g进行备份导入的方法及相关注意事项,同时还介绍了2019年独角兽企业重金招聘Python工程师的标准。内容包括导出exp命令、删用户、创建数据库、授权等操作,以及导入imp命令的使用。详细介绍了导入时的参数设置,如full、ignore、buffer、commit、feedback等。转载来源于https://my.oschina.net/u/1767754/blog/377593。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • Oracle Database 10g许可授予信息及高级功能详解
    本文介绍了Oracle Database 10g许可授予信息及其中的高级功能,包括数据库优化数据包、SQL访问指导、SQL优化指导、SQL优化集和重组对象。同时提供了详细说明,指导用户在Oracle Database 10g中如何使用这些功能。 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 解决VS写C#项目导入MySQL数据源报错“You have a usable connection already”问题的正确方法
    本文介绍了在VS写C#项目导入MySQL数据源时出现报错“You have a usable connection already”的问题,并给出了正确的解决方法。详细描述了问题的出现情况和报错信息,并提供了解决该问题的步骤和注意事项。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • 本文详细介绍了MySQL表分区的创建、增加和删除方法,包括查看分区数据量和全库数据量的方法。欢迎大家阅读并给予点评。 ... [详细]
  • 如何在php中将mysql查询结果赋值给变量
    本文介绍了在php中将mysql查询结果赋值给变量的方法,包括从mysql表中查询count(学号)并赋值给一个变量,以及如何将sql中查询单条结果赋值给php页面的一个变量。同时还讨论了php调用mysql查询结果到变量的方法,并提供了示例代码。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • 本文讨论了在使用sp_msforeachdb执行动态SQL命令时,当发生错误时如何捕获数据库名称。提供了两种解决方案,并介绍了如何正确使用'?'来显示数据库名称。 ... [详细]
  • 本文介绍了将mysql从5.6.15升级到5.7.15的详细步骤,包括关闭访问、备份旧库、备份权限、配置文件备份、关闭旧数据库、安装二进制、替换配置文件以及启动新数据库等操作。 ... [详细]
  • 本文介绍了在MySQL8.0中如何查看性能并解析SQL执行顺序。首先介绍了查询性能工具的开启方法,然后详细解析了SQL执行顺序中的每个步骤,包括from、on、join、where、group by、having、select distinct、union、order by和limit。同时还介绍了虚拟表的概念和生成过程。通过本文的解析,读者可以更好地理解MySQL8.0中的性能查看和SQL执行顺序。 ... [详细]
author-avatar
到地球来旅行
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有