20

SQL Server バージョン - 2008 R2

メンテナンスを引き継ぐことを目的として、DMS ソリューションの評価に取り組んでいます。元のソリューションには、製造業者に関するデータを含む 1 つの中央データベースがあります。また、ディーラーごとに 1 つのデータベースがあります。これは、データベース間の依存関係が多数あることを意味します。

問題点:

  • DB ドキュメントなし
  • コードのコメントはありません
  • たくさんのヒープ
  • 標準のオブジェクト命名規則なし
  • 中央の DB には、他のオブジェクトに加えて、460 以上のテーブルと 900 以上の SProcs があります
  • 各ディーラー DB には、他のオブジェクトに加えて、370 以上のテーブルと 2350 以上の SProcs があります。

最初のステップとして、DB の完全なクリーンアップをお勧めします。これには、データベース間の依存関係を含むオブジェクトの依存関係を理解することが重要です。Red Gate のソリューションを使用してみましたが、出力が大きすぎます。私が欲しいのは、依存関係を持たないデータベース内のオブジェクトのリストです-それらは他のオブジェクトに依存しておらず、それらに依存するオブジェクトもありません。

依存関係のリストを取得するために使用したスクリプトは次のとおりです。

SELECT
DB_NAME() referencing_database_name,
OBJECT_NAME (referencing_id) referencing_entity_name,
ISNULL(referenced_schema_name,'dbo') referenced_schema_name,
referenced_entity_name,
ao.type_desc referenced_entity_type,
ISNULL(referenced_database_name,DB_NAME()) referenced_database_name
FROM sys.sql_expression_dependencies sed
JOIN sys.all_objects ao
ON sed.referenced_entity_name = ao.name 

各 DB からこの結果セットを挿入するテーブル (依存関係) を作成します。次のステップとして、データベース内のすべてのオブジェクトのリストを含む別のテーブル (AllObjects) も作成します。これを行うスクリプトは次のとおりです。

SELECT
DB_NAME() DBName,
name,
type_desc
FROM sys.all_objects
WHERE type_desc IN
(
'VIEW',
'SQL_TABLE_VALUED_FUNCTION',
'SQL_STORED_PROCEDURE',
'SQL_INLINE_TABLE_VALUED_FUNCTION',
'USER_TABLE',
'SQL_SCALAR_FUNCTION'
)

ここで、依存関係テーブルの referenced_entity_name 列に表示されないこのテーブルの名前のリストから、探しているオブジェクトのリストが得られます。

SELECT
AO.DBName,
AO.name,
AO.type_desc
FROM AllObjects AO
LEFT OUTER JOIN Dependencies D ON
D.referenced_database_name = AO.DBName AND
D.referenced_entity_name = AO.name AND
D.referenced_entity_type = AO.type_desc
WHERE 
D.referenced_database_name IS NULL AND
D.referenced_entity_name IS NULL AND
D.referenced_entity_type IS NULL

今質問:

  1. 一部のオブジェクトの依存関係が出力に含まれていないようです。私は何が欠けていますか?
  2. 調査結果が正しいことを確認するにはどうすればよいですか?
  3. これを行うには別の方法があるので、結果を比較して再確認できますか?

前もって感謝します、

ラージ

4

3 に答える 3

6

ああ、MS は sys.sql_expression_dependencies を使用してデータベース間の依存関係を検出することにかなりの努力を払っていましたが、以前に見逃しているのを見たことがあります。あなたの場合、欠落している依存関係の例を見つけて、バックトラックを開始します。クエリから削除しましたか? その場合は、クエリを修正してください。sys.sql_expression_dependencies は特定のクラスの依存関係を省略していますか? どんな条件で?動的 SQL のせいですか? 等

またsp_refreshsqlmodule、sys.sql_modules 内のオブジェクトごとに実行してから、コードを再実行する必要があります。SQL Server に依存関係情報を強制的に更新させます (可能な限り)。

ここで、検証のために、トレースを設定し、イベント 114「Audit Schema Object Access Event」に加えて、ストアド プロシージャや RPC 呼び出しの開始イベントと完了イベントをリッスンします。列DatabaseNameParentNameObjectNameServerNameSPIDおよびRequestID(MARS 対応接続の場合) を含めます。多分他の人も。"Audit Schema Object Access Event" は、オブジェクトがアクセスされるたびに発生するため、このトレースの実行中にアプリを実行し、SPID + RequestId を使用してデータを照合し、sys.sql_expression_dependencies を使用して結果と比較します。依存関係データに表示されないものがトレース データにある場合は、何かを見落としています。

于 2012-12-10T03:36:41.457 に答える
1

リンクされたサーバーに対処する必要がある場合は、@ MilicaMedic の回答をサーバー間の依存関係で機能するように調整しました。依存関係で利用可能な列名も出力します。

次のように使用できます。

create table #dependencies (
    referencing_server nvarchar(128),
    referencing_database nvarchar(128),
    referencing_schema nvarchar(128),
    referencing_object_name nvarchar(128),
    referencing_column nvarchar(128),
    referenced_server nvarchar(128),
    referenced_database nvarchar(128),
    referenced_schema nvarchar(128),
    referenced_object_name nvarchar(128),
    referenced_column nvarchar(128)
);

insert @dependencies
exec crossServerDependencies 
    'ThisServerName, LinkedServerName, LinkedServerName2, etc'

そこから、回答で説明したように、それを AllObjects テーブルに結合します。

私のコードには、「splitString」と「AddBracketsWhenNecessary」という 2 つの外部関数が必要です。必要に応じて、前者を単純化し、後者を完全に削除できます。しかし、私はそれらを他のことに使用しているので、実装に取り​​入れています。両方のコードは下部にあります。

主な手順は次のとおりです。

create procedure crossServerDependencies
    @server_names_csv nvarchar(500) = null -- csv list of server names you want to pull dependencies for
as

-- Create output table

    if object_id('tempdb..#dependencies') is not null 
        drop table #dependencies;

    create table #dependencies (
        referencing_server nvarchar(128),
        referencing_database nvarchar(128),
        referencing_schema nvarchar(128),
        referencing_object_name nvarchar(128),
        referencing_column nvarchar(128),
        referenced_server nvarchar(128),
        referenced_database nvarchar(128),
        referenced_schema nvarchar(128),
        referenced_object_name nvarchar(128),
        referenced_column nvarchar(128)
    );

-- Split server csv into table

    set @server_names_csv = isnull(@server_names_csv, @@servername);

    declare @server_names table (
        server_row int,
        server_name nvarchar(128),
        actuallyExists bit
    );

    insert      @server_names
    select      server_row = id, 
                server_name,
                actuallyExists = case when sv.name is not null then 1 else 0 end
    from        dbo.splitString(@server_names_csv, ',') sp
    cross apply (select server_name = dbo.AddBracketsWhenNecessary(val)) ap
    left join   sys.servers sv on sp.val = dbo.AddBracketsWhenNecessary(sv.name); 

-- Loop servers

    declare 
        @server_row int = 0,
        @server_name nvarchar(50),
        @server_exists bit = 0,
        @server_is_local bit = 0,
        @server_had_some_inserts bit = 0;

    while @server_row <= (select max(server_row) from @server_names)
    begin

        -- Server loop initializations

            set @server_row += 1;
            set @server_had_some_inserts = 0;

            select      @server_name = server_name,
                        @server_exists = actuallyExists
            from        @server_names 
            where       server_row = @server_row;

            set @server_is_local = 
                case when @server_name = dbo.AddBracketsWhenNecessary(@@servername) then 1 else 0 end;

        -- Handle non-existent server (and prevent sql injection)

            if @server_exists = 0
            begin
                print 
                    '"' + @server_name + '" does not exist.  ' + 
                    'Please check your spelling and/or access to view the linked server ' +
                    '(running under ' + user_name() + ').';
                continue;
            end

        -- Get database list

            if object_id('tempdb..#databases') is not null 
                drop table #databases;

            create table #databases (
                rownum int identity(1,1),
                database_id int, 
                database_name nvarchar(128)
            );

            declare @sql nvarchar(max) = '

                select      database_id, [name]
                from        master.sys.databases
                where       state <> 6 -- ignore offline dbs 
                and         database_id > 4 -- ignore system dbs
                and         has_dbaccess([name]) = 1
                and         [name] not in (''ReportServer'', ''ReportServerTempDB'')

            ';

            if @server_is_local = 0
            begin
                set @sql = replace(@sql, '''', '''''');
                set @sql = 'select * from openquery( @server_name, ''' + @sql + ''')';
            end 

            set @sql = 'insert #databases (database_id, database_name)' + @sql; 
            set @sql = replace(@sql, '@server_name', @server_name);
            exec (@sql);

            delete #databases
            where database_name = 'ReportServer';

        -- Loop databases 

            declare @rowNum int = 0;

            while @rowNum <= (select max(rownum) from #databases)
            begin

                -- Database loop initializations

                    set @rowNum += 1;

                    declare 
                        @database_id nvarchar(max), 
                        @database_name nvarchar(max);

                    select      @database_id = database_id, 
                                @database_name = dbo.AddBracketsWhenNecessary(database_name)
                    from        #databases
                    where       rownum = @rowNum;

                -- Get object dependency info

                    set @sql = '

                        with

                            getTableColumnIds as (

                                select      table_id = o.object_id,
                                            table_name = o.name, 
                                            column_id = c.column_id,
                                            column_name = c.name
                                from        @database_name.sys.objects o
                                join        @database_name.sys.all_columns c on o.object_id = c.object_id

                            )

                            @insertStatement
                            select      ''@server_name'',
                                        db_name(@database_id), 
                                        object_schema_name(referencing_id, @database_id), 
                                        object_name(referencing_id, @database_id), 
                                        referencing_column = ringTCs.column_name,
                                        isnull(referenced_server_name, ''@server_name''),
                                        isnull(referenced_database_name, db_name(@database_id)),
                                        isnull(referenced_schema_name, ''dbo''),
                                        referenced_entity_name,
                                        referenced_column = redTCs.column_name
                            from        @database_name.sys.sql_expression_dependencies d
                            left join   getTableColumnIds ringTCs 
                                            on d.referencing_id = ringTCs.table_id 
                                            and d.referencing_minor_id = ringTCs.column_id
                            left join   getTableColumnIds redTCs 
                                            on d.referenced_id = redTCs.table_id 
                                            and d.referenced_minor_id = redTCs.column_id

                    ';

                    set @sql = replace(@sql, '@database_id', @database_id);
                    set @sql = replace(@sql, '@database_name', @database_name);

                    if @server_is_local = 0
                    begin
                        set @sql = replace(@sql, '''', '''''');
                        set @sql = replace(@sql, '@insertStatement', '');
                        set @sql = 'select * from openquery(@server_name, ''' + @sql + ''')';
                    end

                    set @sql = replace(@sql, '@insertStatement', 'insert #dependencies '); 
                    set @sql = replace(@sql, '@server_name', @server_name);
                    exec (@sql);

                -- Database loop terminations

                    if @@rowcount > 0
                        set @server_had_some_inserts = 1;

            end -- database loop 

        -- server loop terminations

            if @server_had_some_inserts = 0
            begin

                declare @remote_user_name nvarchar(255);

                select  @remote_user_name = remote_name
                from    sys.linked_logins li
                join    sys.servers s on li.server_id = s.server_id
                where   remote_name is not null
                and     s.name = 'sisag'

                print (
                    'No dependencies found for ' + @server_name + '.  ' + 
                    'If this is unexpected, you may need to run "grant view any definition to ' + 
                    '[' + isnull(@remote_user_name, '?') + ']" ' + 
                    'on the remote server.'
                );

            end

    end -- server loop 

-- Terminate

    select * from #dependencies

AddBracketsWhenNecessary のコード:

create function AddBracketsWhenNecessary (
    @objectName nvarchar(250)
)
returns nvarchar(250) as
begin


    if left(@objectName, 1) = '[' and right(@objectName, 1) = ']'
        return @objectName;

    declare @hasInvalidCharacter bit;

    select      @hasInvalidCharacter = max(isInvalid)
    from        dbo.splitString(@objectName, null) chars
    cross apply (select 
                    isLetter = patindex('[a-z,_]', val),
                    isNumber = PATINDEX('[0-9]', val)
                ) getCharType
    cross apply (select 
                    isInvalid =
                        case 
                        when isLetter = 1 then 0
                        when isNumber = 1 and not chars.id = 1 then 0
                        else 1
                        end
                ) getValidity

    return 
            case when @hasInvalidCharacter = 1 then '[' else '' end 
        +   @objectName
        +   case when @hasInvalidCharacter = 1 then ']' else '' end;

end

最後に、私のスプリッター関数 (ただし、より単純なバージョンが必要な場合は Arnold Fribble を参照てください。SqlServer 2016 以降の場合は組み込み関数を使用してください):

create function splitString ( 
    @stringToSplit nvarchar(max), 
    @delimiter nvarchar(50)
)
returns table as
return 

    with

        split_by_delimiter as ( 

            select      id      = 1, 
                        start   = 1, 
                        stop    = convert(int, 
                                    charindex(@delimiter, @stringToSplit)
                                  )

            union all
            select      id      = id + 1, 
                        start   = newStart, 
                        stop    = convert(int, 
                                    charindex(@delimiter, @stringToSplit, newStart)
                                  )
            from        split_by_delimiter
            cross apply (select newStart = stop + len(@delimiter)) ap
            where       Stop > 0

        ),

        split_into_characters as (  

            select      id      = 1,    
                        chr     = left(@stringToSplit,1)
            union all
            select      id      = id + 1,       
                        chr     = substring(@stringToSplit, ID + 1, 1) 
            from        split_into_characters 
            where       id < len(@stringToSplit)

        )

        select      id, 
                    val = 
                        ltrim(rtrim(substring(
                            @stringToSplit, 
                            start, 
                            case 
                                when stop > 0 then stop - start 
                                else len(@stringtosplit) 
                                end
                        ))) 
        from        split_by_delimiter
        where       len(@delimiter) > 0

        union all
        select      id, 
                    val = chr
        from        split_into_characters
        where       @delimiter = ''
        or          @delimiter is null

私が使用している実際のコードからいくつかの小さな変更を加える必要があったため、参照エラーがある場合は、コメントでお知らせください。編集します.

于 2018-05-30T15:28:47.553 に答える