• 开源项目:高级SQL Server监控、性能图、分析与优化、版本控制


    这是一个相当高级的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中都没有。

    获取数据库信息

    View Code
    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' 

    对象/脚本搜索

    View Code
    --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(maxnot 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 end0
            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, nullnull10
                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'), ''), @i2null20
                from sys.indexes i
                where i.object_id=@Id and i.index_id=@i2
            else if @T = 'uq'
                insert into @Data values(@f4 + 'unique'@i2null30)
            else if @T = 'f'
                begin
                insert into @Data 
                select @f4 + 'foreign key'-1@i40
                from sys.foreign_keys f
                where f.object_id=@i
                
                insert into @Data 
                select ' references ' + quotename(s.name) + '.' + quotename(o.name), -2@i41
                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@i42
                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 + ']'nullnull99)
            end

        insert into @Data values(@f1+')'nullnull100)
        
        -- 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, null1000
        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(4142)
        
        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,1NOT NULL,
        [databasename] [varchar](256NULL,
        [eventtype] [varchar](50NULL,
        [objectname] [varchar](256NULL,
        [objecttype] [varchar](25NULL,
        [sqlcommand] [nvarchar](maxNULL,
        [loginname] [varchar](256NULL,
        [hostname] [varchar](256NULL,
        [PostTime] [datetime] NULL,
        [Version] [int] NOT NULL,
     CONSTRAINT [PK_VERSION_CONTROL_TABLE] PRIMARY KEY CLUSTERED 
    (
        [ID] ASC
    )WITH (IGNORE_DUP_KEY = OFFON [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(@CurrentVersion0+ 1 WHERE ID = @CurrentID
    END
    GO
    ENABLE TRIGGER [TRG_VERSION_CONTROL_TABLE] ON DATABASE

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

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

    活动监控

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

    获取进程列表

    View Code
    --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

    获取任务列表

    View Code
    --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(01);
                    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.Log0 } };
                    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

  • 相关阅读:
    iOS 数字滚动 类似于老
    iOS 实现转盘的效果
    iOS 摇一摇的功能
    APP上架证书无效:解决
    iOS--UIAlertView与UIAlertController和UIAlertAction之间的事儿
    ios 获取字符串所需要占用的label的高度
    适配----Autolayout
    OC中 block 的用法
    微信小程序如何播放腾讯视频?
    IOS-UICollectionView
  • 原文地址:https://www.cnblogs.com/unruledboy/p/SQLMon2.html
Copyright © 2020-2023  润新知