原文:SQL Mon 介绍

这是一个相当高级的SQL Server监控工具,全面监控SQL Server的活动与性能,分析性能瓶颈,给出优化建议。

red-gate有一个在线的数据库监控工具,不过那个商业的东西价钱不便宜。我写的这个平民版,开源,功能上有颇多的差异(各有长短)。

项目在Codeplex上开源:http://sqlmon.codeplex.com/

在Codeproject上有英文介绍:http://www.codeproject.com/KB/database/sqlmonitor.aspx

介绍

是否想过:“SQL Server为什么那么慢?”,“为什么CPU占用那么高?”,“到底哪里死锁了?”,“为什么数据库那么大?”,“怎样才可以查看我的存储过程和函数的历史版本?”,“可以让我的SQL Server跑得更快吗?”。

你的答案就在这里;-)

到底能干嘛

  • 监控SQL Server的活动:进程、任务,详细查看当前执行的语句与实际变量值,终止进程
  • IO/CPU/网络等性能趋势图
  • 函数/存储过程等的版本控制,这在商业软件中也没有(如果你知道,告诉我)
  • 对象浏览器:服务器、数据库、表、视图、函数、存储过程等
  • 数据库管理:收缩、日志清除、备份、恢复等
  • 在整个数据库中搜索对象/脚本内容,这在SQL Server 2012中也无法做到
  • 自动显示所有对象的脚本,如表、视图、函数、存储过程等

概览

在上图中,我们可以看见表的create脚本。如果你选择其它对象,如函数、存储过程等,一样会显示相应的脚本。

在对象列表中,如果是数据表,显示表的占用空间(包括索引)、记录数等。

这些在SQL Server 2012中都没有。

获取数据库信息

SELECT DB_NAME(database_id) AS DatabaseName, Name AS Logical_Name, Physical_Name, CAST(size AS decimal(30,0))*8 AS Size, state FROM sys.master_files WHERE DB_NAME(database_id) = 'YOUR_DATABASE_NAME' 

对象/脚本搜索

--search in script
Select s.name, s.create_date AS CreateDate, s.modify_date AS ModifyDate, s.type, c.text from syscomments c left join sys.objects s on c.id = s.object_id where [Text] like '%YOUR_QUERY_HERE%'

--search in jobs
SELECT job_id, name, date_created AS CreateDate, date_modified AS ModifyDate, 'Job' AS type FROM msdb.dbo.sysjobs

获取表结构

--To get table names and records
SELECT 
    [TableName] = so.name, 
    [RowCount] = MAX(si.rows) 
FROM 
    sysobjects so, 
    sysindexes si 
WHERE 
    so.xtype = 'U' 
    AND 
    si.id = OBJECT_ID(so.name) 
GROUP BY 
    so.name
--To get table used space
EXEC sp_spaceused 'TABLE_NAME'
--To get table script
declare @Id int, @i int, @i2 int,@Sql varchar(max),@Sql2 varchar(max), @f1 varchar(5), @f2 varchar(5), @f3 varchar(5), @f4 varchar(5), @T varchar(5)
    select @Id=object_id('YOUR_TABLE_NAME_HERE'), @f1 = char(13) + char(10), @f2 = '    ', @f3=@f1+@f2, @f4=',' + @f3
    
    if not(@Id is null)
    BEGIN
    declare @Data table(Id int identity primary key, D varchar(max) not null, ic int null, re int null, o int not null);
    
    -- Columns
    with c as(
        select c.column_id, Nr = row_number() over(order by c.column_id), Clr=count(*) over(),
            D = quotename(c.name) + ' ' +
                case when s.name = 'sys' or c.is_computed=1 then '' else quotename(s.name) + '.' end +
                case when c.is_computed=1 then '' when s.name = 'sys' then t.Name else quotename(t.name) end +
                case when c.user_type_id!=c.system_type_id or c.is_computed=1 then ''
                    when t.Name in ('xml', 'uniqueidentifier', 'tinyint', 'timestamp', 'time', 'text', 'sysname', 'sql_variant', 'smallmoney', 'smallint', 'smalldatetime', 'ntext', 'money',
                                    'int', 'image', 'hierarchyid', 'geometry', 'geography', 'float', 'datetimeoffset', 'datetime2', 'datetime', 'date', 'bigint', 'bit') then ''
                    when t.Name in('varchar','varbinary', 'real', 'numeric', 'decimal', 'char', 'binary')
                        then '(' + isnull(convert(varchar,nullif(c.max_length,-1)), 'max') + isnull(','+convert(varchar,nullif(c.scale, 0)), '') + ')'
                    when t.Name in('nvarchar','nchar')
                        then '(' + isnull(convert(varchar,nullif(c.max_length,-1) / 2), 'max') + isnull(','+convert(varchar,nullif(c.scale, 0)), '') + ')'
                    else '??'
                    end + 
                case when ic.object_id is not null then ' identity(' + convert(varchar,ic.seed_value) + ',' + convert(varchar,ic.increment_value) + ')' else '' end +
                case when c.is_computed=1 then 'as' + cc.definition when c.is_nullable = 1 then ' null' else ' not null' end +
                case c.is_rowguidcol when 1 then ' rowguidcol' else '' end +
                case when d.object_id is not null then ' default ' + d.definition else  '' end
        from sys.columns c
        inner join sys.types t
        on t.user_type_id = c.user_type_id
        inner join sys.schemas s
        on s.schema_id=t.schema_id
        left outer join sys.computed_columns cc
        on cc.object_id=c.object_id and cc.column_id=c.column_id
        left outer join sys.default_constraints d
        on d.parent_object_id=@id and d.parent_column_id=c.column_id
        left outer join sys.identity_columns ic
        on ic.object_id=c.object_id and ic.column_id=c.column_id
        where c.object_id=@Id
        
    )
        insert into @Data(D, o)
        select '    ' + D + case Nr when Clr then '' else ',' + @f1 end, 0
        from c where NOT D IS NULL 
        order by column_id
    
    -- SubObjects
    set @i=0
    while 1=1
        begin
        select top 1 @i=c.object_id, @T = c.type, @i2=i.index_id
        from sys.objects c 
        left outer join sys.indexes i
        on i.object_id=@Id and i.name=c.name
        where parent_object_id=@Id and c.object_id>@i and c.type not in('D')
        order by c.object_id
        if @@rowcount=0 break
        if @T = 'C' 
            insert into @Data 
            select @f4 + 'check ' + case is_not_for_replication when 1 then 'not for replication ' else '' end + definition, null, null, 10
            from sys.check_constraints where object_id=@i
        else if @T = 'Pk'
            insert into @Data 
            select @f4 + 'primary key' + isnull(' ' + nullif(lower(i.type_desc),'clustered'), ''), @i2, null, 20
            from sys.indexes i
            where i.object_id=@Id and i.index_id=@i2
        else if @T = 'uq'
            insert into @Data values(@f4 + 'unique', @i2, null, 30)
        else if @T = 'f'
            begin
            insert into @Data 
            select @f4 + 'foreign key', -1, @i, 40
            from sys.foreign_keys f
            where f.object_id=@i
            
            insert into @Data 
            select ' references ' + quotename(s.name) + '.' + quotename(o.name), -2, @i, 41
            from sys.foreign_keys f
            inner join sys.objects o
            on o.object_id=f.referenced_object_id
            inner join sys.schemas s
            on s.schema_id=o.schema_id
            where f.object_id=@i
            
            insert into @Data 
            select ' not for replication', -3, @i, 42
            from sys.foreign_keys f
            inner join sys.objects o
            on o.object_id=f.referenced_object_id
            inner join sys.schemas s
            on s.schema_id=o.schema_id
            where f.object_id=@i and f.is_not_for_replication=1
            end
        else
            insert into @Data values(@f4 + 'Unknow SubObject [' + @T + ']', null, null, 99)
        end

    insert into @Data values(@f1+')', null, null, 100)
    
    -- Indexes
    insert into @Data
    select @f1 + 'create ' + case is_unique when 1 then 'unique ' else '' end + lower(s.type_desc) + ' index ' + 'i' + convert(varchar, row_number() over(order by index_id)) + ' on ' + quotename(sc.Name) + '.' + quotename(o.name), index_id, null, 1000
    from sys.indexes s
    inner join sys.objects o
    on o.object_id=s.object_id
    inner join sys.schemas sc
    on sc.schema_id=o.schema_id
    where s.object_id=@Id and is_unique_constraint=0 and is_primary_key=0 and s.type_desc != 'heap'
    
    -- columns
    set @i=0
    while 1=1
        begin
        select top 1 @i=ic from @Data where ic>@i order by ic 
        if @@rowcount=0 break
        select @i2=0, @Sql=null, @Sql2=null
        while 1=1
            begin
            select @i2=index_column_id, 
                @Sql = case c.is_included_column when 1 then @Sql else isnull(@Sql + ', ', '(') + cc.Name + case c.is_descending_key when 1  then ' desc' else '' end end,
                @Sql2 = case c.is_included_column when 0 then @Sql2 else isnull(@Sql2 + ', ', '(') + cc.Name + case c.is_descending_key when 1  then ' desc' else '' end end
            from sys.index_columns c
            inner join sys.columns cc
            on c.column_id=cc.column_id and cc.object_id=c.object_id
            where c.object_id=@Id and index_id=@i and index_column_id>@i2
            order by index_column_id
            if @@rowcount=0 break
            end
        update @Data set D=D+@Sql +')' + isnull(' include' + @Sql2 + ')', '') where ic=@i
        end
        
    -- references
    set @i=0
    while 1=1
        begin
        select top 1 @i=re from @Data where re>@i order by re
        if @@rowcount=0 break
        
        select @i2=0, @Sql=null, @Sql2=null
        while 1=1
            begin
            select @i2=f.constraint_column_id, 
                @Sql = isnull(@Sql + ', ', '(') + c1.Name,
                @Sql2 = isnull(@Sql2 + ', ', '(') + c2.Name
            from sys.foreign_key_columns f
            inner join sys.columns c1
            on c1.column_id=f.parent_column_id and c1.object_id=f.parent_object_id
            inner join sys.columns c2
            on c2.column_id=f.referenced_column_id and c2.object_id=f.referenced_object_id
            where f.constraint_object_id=@i and f.constraint_column_id>@i2
            order by f.constraint_column_id
            if @@rowcount=0 break
            end
        update @Data set D = D + @Sql + ')'  where re=@i and ic=-1
        update @Data set D = D + @Sql2 + ')'  where re=@i and ic=-2
        end;
    
    -- Render
    with x as(
        select id=d.id-1, D=d.D + isnull(d2.D,'')
        from @Data d
        left outer join @Data d2
        on d.re=d2.re and d2.o=42
        where d.o=41
        
    )
    update @Data
        set D=d.D+x.D
    from @Data d
    inner join x
    on x.id=d.id
    
    delete @Data where o in(41, 42)
    
    select @Sql = 'create table ' + quotename(s.name) + '.' + quotename(o.name) + '(' + @f1
    from sys.objects o
    inner join sys.schemas s
    on o.schema_id = s.schema_id
    where o.object_id=@Id
    
    set @i=0
    while 1=1
        begin
        select top 1 @I=Id, @Sql = @Sql + D from @Data order by o, case when o=0 then right('0000' + convert(varchar,id),5)  else D end, id
        if @@rowcount=0 break
        delete @Data where id=@i
        end
    END
    SELECT @Sql

性能趋势图

在上图中,我们可以看见SQL Server历史/当前的IO/CPU/网络信息都在趋势图中显示。

这些数据来自几个系统变量:

  • @@cpu_busy
  • @@io_busy
  • @@idle
  • @@pack_received
  • @@pack_sent
  • @@connections
  • @@packet_errors
  • @@total_read
  • @@total_write
  • @@total_errors

以下是相应的SQL:

declare @now         datetime
declare @cpu_busy     int
declare @io_busy    int
declare @idle        int
declare @pack_received    int
declare @pack_sent    int
declare @pack_errors    int
declare @connections    int
declare @total_read    int
declare @total_write    int
declare @total_errors    int

declare @oldcpu_busy     int    /* used to see if DataServer has been rebooted */
declare @interval    int
declare @mspertick    int    /* milliseconds per tick */

/*
**  Set @mspertick.  This is just used to make the numbers easier to handle
**  and avoid overflow.
*/
select @mspertick = convert(int, @@timeticks / 1000.0)

/*
**  Get current monitor values.
*/
select
    @now = getdate(),
    @cpu_busy = @@cpu_busy,
    @io_busy = @@io_busy,
    @idle = @@idle,
    @pack_received = @@pack_received,
    @pack_sent = @@pack_sent,
    @connections = @@connections,
    @pack_errors = @@packet_errors,
    @total_read = @@total_read,
    @total_write = @@total_write,
    @total_errors = @@total_errors

/*
**  Check to see if DataServer has been rebooted.  If it has then the
**  value of @@cpu_busy will be less than the value of spt_monitor.cpu_busy.
**  If it has update spt_monitor.
*/
select @oldcpu_busy = cpu_busy
    from master.dbo.spt_monitor
if @oldcpu_busy > @cpu_busy
begin
    update master.dbo.spt_monitor
        set
            lastrun = @now,
            cpu_busy = @cpu_busy,
            io_busy = @io_busy,
            idle = @idle,
            pack_received = @pack_received,
            pack_sent = @pack_sent,
            connections = @connections,
            pack_errors = @pack_errors,
            total_read = @total_read,
            total_write = @total_write,
            total_errors = @total_errors
end

/*
**  Now print out old and new monitor values.
*/
set nocount on
select @interval = datediff(ss, lastrun, @now)
    from master.dbo.spt_monitor
/* To prevent a divide by zero error when run for the first
** time after boot up
*/
if @interval = 0
    select @interval = 1
select last_run = lastrun, current_run = @now, seconds = @interval,
    cpu_busy_total = convert(int, ((@cpu_busy * @mspertick) / 1000)),
    cpu_busy_current = convert(int, (((@cpu_busy - cpu_busy)
        * @mspertick) / 1000)),
    cpu_busy_percentage = convert(int, ((((@cpu_busy - cpu_busy)
        * @mspertick) / 1000) * 100) / @interval),
    io_busy_total = convert(int, ((@io_busy * @mspertick) / 1000)),
    io_busy_current = convert(int, (((@io_busy - io_busy)
        * @mspertick) / 1000)),
    io_busy_percentage = convert(int, ((((@io_busy - io_busy)
        * @mspertick) / 1000) * 100) / @interval),
    idle_total = convert(int, ((convert(bigint,@idle) * @mspertick) / 1000)),
    idle_current = convert(int, (((@idle - idle)
        * @mspertick) / 1000)),
    idle_percentage = convert(int, ((((@idle - idle)
        * @mspertick) / 1000) * 100) / @interval),
    packets_received_total = @pack_received,
    packets_received_current = @pack_received - pack_received,
    packets_sent_total = @pack_sent,
    packets_sent_current = @pack_sent - pack_sent,
    packet_errors_total = @pack_errors,
    packet_errors_current = @pack_errors - pack_errors,
    total_read = @total_read,
    current_read = @total_read - total_read,
    total_write = @total_write,
    current_write =    @total_write - total_write,
    total_errors = @total_errors,
    current_errors = @total_errors - total_errors,
    connections_total = @connections,
    connections_current = @connections - connections
from master.dbo.spt_monitor

/*
**  Now update spt_monitor
*/
update master.dbo.spt_monitor
    set
        lastrun = @now,
        cpu_busy = @cpu_busy,
        io_busy = @io_busy,
        idle = @idle,
        pack_received = @pack_received,
        pack_sent = @pack_sent,
        connections = @connections,
        pack_errors = @pack_errors,
        total_read = @total_read,
        total_write = @total_write,
        total_errors = @total_errors

版本控制

数据库开发人员总在想,每次修改了函数/存储过程,我们都得自己做备份,用以历史参考,当发现错误的时候,可以回滚。在SQL Monitor里面,这个是全自动的。

版本控制的思想来自这里:http://www.sqlteam.com/article/using-ddl-triggers-in-sql-server-2005-to-capture-schema-changes

原理就是用数据库DDL触发器记录每个DDL操作,自增版本,并存储到一个表中。

关键代码

SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[VERSION_CONTROL_TABLE]') AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[{0}](
    [ID] [bigint] IDENTITY(1,1) NOT NULL,
    [databasename] [varchar](256) NULL,
    [eventtype] [varchar](50) NULL,
    [objectname] [varchar](256) NULL,
    [objecttype] [varchar](25) NULL,
    [sqlcommand] [nvarchar](max) NULL,
    [loginname] [varchar](256) NULL,
    [hostname] [varchar](256) NULL,
    [PostTime] [datetime] NULL,
    [Version] [int] NOT NULL,
 CONSTRAINT [PK_VERSION_CONTROL_TABLE] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END

GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE TRIGGER [TRG_VERSION_CONTROL_TABLE}]
ON DATABASE
FOR CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
CREATE_TABLE, ALTER_TABLE, DROP_TABLE,
CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION,
CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER,
CREATE_VIEW, ALTER_VIEW, DROP_VIEW,
CREATE_INDEX, ALTER_INDEX, DROP_INDEX

AS

SET NOCOUNT ON

DECLARE @CurrentVersion int
DECLARE @CurrentID int
DECLARE @DatabaseName varchar(256)
DECLARE @ObjectName varchar(256)
DECLARE @data XML

SET @data = EVENTDATA()

INSERT INTO dbo.VERSION_CONTROL_TABLE(databasename, eventtype,objectname, objecttype, sqlcommand, loginname,Hostname,PostTime, Version)
VALUES(
@data.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'varchar(256)'),
@data.value('(/EVENT_INSTANCE/EventType)[1]', 'varchar(50)'),  -- value is case-sensitive
@data.value('(/EVENT_INSTANCE/ObjectName)[1]', 'varchar(256)'), 
@data.value('(/EVENT_INSTANCE/ObjectType)[1]', 'varchar(25)'), 
@data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'varchar(max)'), 
@data.value('(/EVENT_INSTANCE/LoginName)[1]', 'varchar(256)'),
HOST_NAME(),
GETDATE(),
0
)

SET @CurrentID = IDENT_CURRENT('VERSION_CONTROL_TABLE')
SELECT @DatabaseName = databasename, @ObjectName = objectname FROM VERSION_CONTROL_TABLE WHERE ID = @CurrentID
IF (@DatabaseName IS NOT NULL AND @ObjectName IS NOT NULL)
BEGIN
    SELECT @CurrentVersion = MAX(Version) FROM VERSION_CONTROL_TABLE WHERE databasename = @DatabaseName AND objectname = @ObjectName
    UPDATE VERSION_CONTROL_TABLE SET Version = ISNULL(@CurrentVersion, 0) + 1 WHERE ID = @CurrentID
END
GO
ENABLE TRIGGER [TRG_VERSION_CONTROL_TABLE] ON DATABASE

SQL Monitor会全自动给你的所有脚本修改做版本记录。你可以随时查看在什么时候哪个机器用什么身份修改了哪个对象的脚本。

SQL Monitor内置版本比较,你可以清楚知道不同的版本的差异。

活动监控

上图清晰显示所有系统的活动进程,每个进程当前执行什么语句。

获取进程列表

--To get processes
SELECT s.session_id AS spid, s.login_time, s.host_name AS hostname, s.host_process_id AS hostprocess, s.login_name AS loginname, s.logical_reads AS physical_io, s.cpu_time AS cpu, s.program_name, 0 AS dbid, s.last_request_start_time AS last_batch_begin, CASE WHEN status = 'running' THEN GETDATE() ELSE dateadd(ms, s.cpu_time, s.last_request_end_time) END AS last_batch_end, s.status FROM sys.dm_exec_sessions s JOIN sys.dm_exec_connections c ON s.session_id = c.session_id

获取任务列表

--To get jobs
SELECT job_id AS spid, name AS program_name, 0 AS dbid, 0 AS cpu, 0 AS physical_io, NULL AS login_time, NULL AS last_batch_begin, NULL AS last_batch_end, NULL AS status, NULL AS hostname, NULL AS hostprocess, NULL AS cmd, NULL AS loginname FROM msdb.dbo.sysjobs

分析

这是商业级的数据与性能分析,SQL Monitor自动给你的系统、数据库、数据表、索引等进行分析。

基本原理是首先利用master.sys.xp_fixeddrives获取磁盘的剩余空间,然后:

//database & disk free space
        var databases = GetDatabasesInfo();
        var files = new List<tuple<bool, />>();
        databases.AsEnumerable().ForEach(d =>
        {
            var database = GetDatabaseInfo(d["name"].ToString());
            database.AsEnumerable().ForEach(f =>
            {
                files.Add(new Tuple<bool, />(Convert.ToInt32(f["type"]) == 1, f["physical_name"].ToString(), Convert.ToInt64(Convert.ToDecimal(f["Size"]) / Size1K)));
            }
            );
        });
        var spaces = new Dictionary<string, />>();
        //MB free
        var driveSpaces = Query("EXEC master.sys.xp_fixeddrives");
        driveSpaces.AsEnumerable().ForEach(s =>
        {
            //could not use name but rather index, because the column name will change according to locale
            spaces.Add(s[0].ToString(), new KeyValue<long, />(Convert.ToInt64(s[1]), 0));
        });
        files.ForEach(f =>
        {
            //maybe some access issues
            try
            {
                var drive = f.Item2.Substring(0, 1);
                if (spaces.ContainsKey(drive))
                {
                    spaces[drive].Value += f.Item3;
                }
            }
            catch (Exception)
            {
                //mmmm.....what can we do, mate?
            }
        });
        spaces.ForEach(s =>
        {
            if (s.Value.Key < s.Value.Value / 100 * Settings.Instance.DatabaseDiskFreeSpaceRatio)
            {
                analysisResult.Add(new AnalysisResult { ResultType = AnalysisResultTypes.DiskFreeSpace, ObjectName = s.Key, ReferenceValue = s.Value.Key, CurrentValue = s.Value.Value, Factor = Settings.Instance.DatabaseDiskFreeSpaceRatio + SizePercentage });
            }
        });

        //database data file & log file space
        databases.AsEnumerable().ForEach(d =>
        {
            var name = d["name"].ToString();
            if (!systemDatabases.Contains(name))
            {
                var database = GetDatabaseInfo(name);
                var databaseSpace = new Dictionary<databasefiletypes, /> { { DatabaseFileTypes.Data, 0 }, { DatabaseFileTypes.Log, 0 } };
                database.AsEnumerable().ForEach(f =>
                {
                    var key = (DatabaseFileTypes)Convert.ToInt32(f["type"]);
                    databaseSpace[key] += Convert.ToInt64(Convert.ToDecimal(f["Size"]) / Size1K);
                }
                );
                bool? shrink = null;
                if (databaseSpace[DatabaseFileTypes.Log] > databaseSpace[DatabaseFileTypes.Data] / 100 * Settings.Instance.DatabaseDataLogSpaceRatio)
                    shrink = false;
                else
                {
                    var logSpaces = SQLHelper.Query("DBCC SQLPERF(LOGSPACE)", GetServerInfo(name));
                    var logSpace = logSpaces.Select(string.Format("[Database Name] = '{0}'", name));
                    if (logSpace.Length > 0)
                    {
                        var logSpacedUsed = Convert.ToDouble(logSpace[0]["Log Space Used (%)"]);
                        if (logSpacedUsed < Settings.Instance.DatabaseDataLogSpaceRatio)
                            shrink = true;
                    }
                }
                if (shrink != null)
                    analysisResult.Add(new AnalysisResult { ResultType = AnalysisResultTypes.DatabaseLogSpace, ObjectName = name, ReferenceValue = databaseSpace[DatabaseFileTypes.Log], CurrentValue = databaseSpace[DatabaseFileTypes.Data], Factor = Settings.Instance.DatabaseDataLogSpaceRatio + SizePercentage, Key = (bool)shrink ? 1 : 0 });
            }
        });

对于表空间,使用了sp_spaceused,关键代码:

var tables = GetObjects(KeyTables);
tables.AsEnumerable().ForEach(t =>
    {
        var name = t[KeyName].ToString();
        var space = Query(string.Format("EXEC sp_spaceused '{0}'", name), CurrentServerInfo);
        if (space.Rows.Count > 0)
        {
            var row = space.Rows[0];
            var dataSize = ToKB(row["data"]) / Size1K;
            var indexSize = ToKB(row["index_size"]) / Size1K;
            if (indexSize > dataSize / 100 * Settings.Instance.TableDataIndexSpaceRatio)
                analysisResult.Add(new AnalysisResult { ResultType = AnalysisResultTypes.TableIndexSpace, ObjectName = name, ReferenceValue = dataSize, CurrentValue = indexSize, Factor = Settings.Instance.DatabaseDataLogSpaceRatio + SizePercentage, Key = (int)TableIndexSpaceRules.DataIndexSpaceRatio });
        }
    });

最新版本

http://sqlmon.codeplex.com/releases/view/77943

转载:http://www.cnblogs.com/unruledboy

SQL Mon 介绍的更多相关文章

  1. 初学者SQL语句介绍

    初学者SQL语句介绍      1.用 Select 子句检索记录    Select 子句是每一个检索数据的查询核心.它告诉数据库引擎返回什么字段.    Select 子句的常见形式是:    S ...

  2. pl/sql的介绍

    为什么需要pl/sql编程? 因为使用纯的sql语句来操作数据库,有先天性的技术缺陷: 1.不能模块编程: 2.执行速度慢: 3.安全性有问题: 4.浪费带宽. pl/sql是什么? pl/sql(p ...

  3. SQL的介绍及MySQL的安装

    基础篇 - SQL 介绍及 MySQL 安装               SQL的介绍及MySQL的安装 课程介绍 本课程为实验楼提供的 MySQL 实验教程,所有的步骤都在实验楼在线实验环境中完成, ...

  4. oracle PL/SQL的介绍

    转自:http://blog.sina.com.cn/s/blog_4c302f060101i4o1.html 一 PL/SQL的介绍 1 PL/SQL是什么? PL/SQL(procedural l ...

  5. 四:SQL语句介绍

    前言:介绍SQL语句及其大致的分类 一:SQL语句介绍(Structured SQL Lanage) 结构化的查询语言 是一种特殊的编程语言 是一种数据库查询和程序设计语言 用于存取数据及查询.更新和 ...

  6. SQL数据类型介绍

    在计算机中数据有两种特征:类型和长度.所谓数据类型就是以数据的表现方式和存储方式来划分的数据的种类.    在SQL Server 中每个变量.参数.表达式等都有数据类型.系统提供的数据类型分为几大类 ...

  7. SQL函数介绍

    http://www.cnblogs.com/moss_tan_jun/archive/2010/08/23/1806861.html 一旦成功地从表中检索出数据,就需要进一步操纵这些数据,以获得有用 ...

  8. pl/sql基础知识—pl/sql块介绍

    n  介绍 块(block)是pl/sql的基本成型单元,编写pl/sql程序实际上就是编写pl/sql块.要完成相对简单的应用功能,可能只需要编写一个pl/sql块:但是如果要想实现复杂的功能,可能 ...

  9. SQL 提示介绍 hash/merge/concat union

    查询提示一直是个很有争议的东西,因为他影响了sql server 自己选择执行计划.很多人在问是否应该使用查询提示的时候一般会被告知慎用或不要使用...但是个人认为善用提示在不修改语句的条件下,是常用 ...

随机推荐

  1. 64位sql server 如何使用链接服务器连接Access

    原文:64位sql server 如何使用链接服务器连接Access 测试环境 操作系统版本:Windows Server 2008 r2 64位 数据库版本:Sql Server 2005 64位 ...

  2. myeclipse解决JSP文件script调整背景颜色

    1进口MyEclipse主题后,打开jsp要么html文件,jsvascript部分原因遭遇了一层白色的.闪避这个时候.症状,如下面: watermark/2/text/aHR0cDovL2Jsb2c ...

  3. MTK6572横屏的调试过程

    电视剧集:系统MTK缺省的系统源代码,Phone模式.底部有三个虚拟按键.需求为,设置成默认横屏,设定一个合理的虚拟按键方案. ------------------------------------ ...

  4. HDU 4916 Count on the path

    意甲冠军: 考虑到一棵树,m询价  不要求回答每一次询价u和v通过在两个节点形成的最低等级点路径 思路: 一開始以为是LCA-  只是T了好几次-  后来发现不用LCA也可做 考虑每一个询问u和v   ...

  5. sql小总结

    ---------------------------------------------------------------------------------------------------- ...

  6. 银联+移动+三星PK微信、余额宝

    在不远的将来,你可以扔掉钱包,扔掉信用卡,揣着手机买东西. 银联.移动.三星这些不同的行业大佬,因为这个目标,被一款名叫NFC的支付技术连接在了一起. 这场游戏的参与者众多,一个合纵连横的时代正在到来 ...

  7. SharePoint 如何使自己的网页自动跳转

    SharePoint 如何使自己的网页自动跳转         SharePoint自动制作自己的网页跳的很easy,只有在页面上要添加一个Web部分--内容编辑器,对应的js代码就可以.       ...

  8. .NET系统开发过程中积累的扩展方法

    分享.NET系统开发过程中积累的扩展方法   .NET 3.5提供的扩展方法特性,可以在不修改原类型代码的情况下扩展它的功能.下面分享的这些扩展方法大部分来自于Code Project或是Stacko ...

  9. 大数据系列修炼-Scala课程01

    简介 由于本人刚毕业,也是从事软件开发相关的工作.想再学习一下关于大数据.移动互联网.云计算相关的技术.为我的未来打好基础.并且从零开始学习大数据相关的知识,脚踏实地的走好每一步,听行业前辈说毕业生刚 ...

  10. Matlab splinetx

    function v = splinetx(x,y,u) %SPLINETX Textbook spline function. % v = splinetx(x,y,u) finds the pie ...