Актуальный список ссылок на удаляемую запись

Все Вы видели при удалении записей некий список таблиц, ссылающихся на таблицу, в которой Вы удаляете записи. И, как Вы понимаете, это список не тех фактических таблиц, в которых есть ссылка на удаляемую запись, а список возможных таблиц, в которых, может быть, находится ссылка. Так вот, для актуализации этого списка была разработана хранимая процедура и доработана выдача сообщения при удалении, в котором выводится именно АКТУАЛЬНЫЙ список таблиц. Небольшое уточнение: это решение работает под MS SQL 2005.

Код хранимой процедуры:

create procedure [dbo].[tsp_GetLinkedTablesByRecordID] (
        @TableName nvarchar(250),
        @RecordID uniqueidentifier,
        @Result nvarchar(4000) output) WITH exec AS owner AS
begin
        declare @ID uniqueidentifier
        declare @fTableName nvarchar(250)
        declare @fFieldName nvarchar(250)
        declare @sql nvarchar(250)

        CREATE TABLE #fkeysnames(
                                pktable_qualifier sysname collate database_default NULL,
                                pktable_owner sysname collate database_default NULL,
                                pktable_name sysname collate database_default NOT NULL,
                                pkcolumn_name sysname collate database_default NOT NULL,
                                fktable_qualifier sysname collate database_default NULL,
                                fktable_owner sysname collate database_default NULL,
                                fktable_name sysname collate database_default NOT NULL,
                                fkcolumn_name sysname collate database_default NOT NULL,
                                key_seq smallint NOT NULL,
                                update_rule smallint NULL,
                                delete_rule smallint NULL,
                                fk_name sysname collate database_default NULL,
                                pk_name sysname collate database_default NULL,
                                deferrability smallint NULL)

        CREATE TABLE #result(
                        [ID] uniqueidentifier,
                        [Name] nvarchar(250)
        )

        INSERT INTO #fkeysnames exec [dbo].[tsp_fkeys] @pktable_name = @Tablename

        SET @Result = ''
        SET @fTableName = ''
        SET @fFieldName = ''
        SET @sql = ''

        declare fk_cursor cursor  FOR
        SELECT fktable_name, fkcolumn_name FROM #fkeysnames

        open fk_cursor

        fetch next FROM fk_cursor INTO @fTableName, @fFieldName

        while @@fetch_status = 0
        begin
                SET @ID = NULL

                SET @sql = 'select top 1 @ID = [ID] from [' + @fTableName + '] where [' + @fFieldName + '] = ''' + cast(@RecordID AS nvarchar(250)) + ''''
                exec sp_executesql @sql, N'@ID uniqueidentifier output', @ID output
                IF (NOT @ID IS NULL)
                begin
                        IF (NOT EXISTS(SELECT [ID] FROM #RESULT where [Name] = @fTableName))
                        begin
                                INSERT INTO #RESULT([ID], [Name]) values(newid(), @fTableName)
                                SET @Result = @Result + @fTableName + char(13) + char(10)
                        end
                end
                fetch next FROM fk_cursor INTO @fTableName, @fFieldName
        end
        close fk_cursor
        deallocate fk_cursor
end

А вот сама функция, получающая результат этой хранимой процедуры:

function GetLinkedTables(TableName, RecordID) {
    if ((IsEmptyValue(TableName)) || (IsEmptyValue(RecordID))) {
        return '';
    }
    var Parameters = System.CreateObject('TSObjectLibrary.Parameters');
    var ReturnParameter = Parameters.CreateItem();
    ReturnParameter.Name = 'Result';
    ReturnParameter.ParamType = 2;
    ReturnParameter.DataType = pdtGUID;
    Parameters.Add(ReturnParameter);
        
    var sql = "exec tsp_GetLinkedTablesByRecordID '" + TableName + "', '" +
        RecordID + "', :Result output";
    Connector.DBEngine.ExecuteCustomSQL(sql, Parameters);
    
    var Result = Parameters.ItemsByName('Result').ValAsStr;
    if (!IsEmptyValue(Result)) {
        return Result;
    }
    return '';
}

Теперь можно смело изменить получение списка ссылок в функции DeleteDataGridRecords (скрипт scr_WindowUtils), а именно строчки

var DependedTables = Connector.DBEngine.GetDependedTables(Dataset.DeleteQuery.Table, true);
var DependedTablesText = DependedTables.Text;

заменяем на

var TableName = Dataset.DeleteQuery.Table.SQLName;
if (!Connector.CurrentUser.IsAdmin) {
  TableName = 'tbl_' + TableName.substr(3, TableName.length);
}
var DependedTablesText = GetLinkedTables(TableName, Dataset('ID'));

Use it!

Нравится

Поделиться

7 комментариев

Процедура нам вернет весь список таблиц на которые есть внешние ключи. Хотелось бы доработать её таким образом что бы возвращались только те таблицы по которым нет каскадных ключей + ID связанных записей. Тогда найти связанные записи, которые мешают удалению станет еще проще.

if (!Connector.CurrentUser.IsAdmin) {
TableName = 'tbl_' + TableName.substr(3, TableName.length);
}

А если таблица не администрируется по записям? Например справочник.
Мне кажется более коректно было бы

TableName = TableName.replace('vw_','tbl_');

>> Процедура нам вернет весь список таблиц на которые есть внешние ключи.

Немного не так. Процедура действительно вернет таблицы по foreign keys, но кроме того отбросит те, в которых нет реальных ссылок на удаляемую запись. По идее, это именно то, о чем ты говоришь "+ ID связанных записей."

См.

SET @sql = 'select top 1 @ID = [ID] from [' + @fTableName + '] where [' + @fFieldName + '] = ''' + cast(@RecordID AS nvarchar(250)) + ''''
                exec sp_executesql @sql, N'@ID uniqueidentifier output', @ID output
                IF (NOT @ID IS NULL)
                begin
                        IF (NOT EXISTS(SELECT [ID] FROM #RESULT where [Name] = @fTableName))
                        begin
                                INSERT INTO #RESULT([ID], [Name]) values(newid(), @fTableName)
                                SET @Result = @Result + @fTableName + char(13) + char(10)
                        end
                end

По поводу "TableName = TableName.replace('vw_','tbl_');"

Можно и так. Но, по сути, это не имеет особого значения, т.к. Dataset.DeleteQuery.Table.SQLName возвращает в любом случае tbl_TableName. (по крайней мере, на момент написания этой функциональности).

Для 3.3.0 под пользователем Dataset.DeleteQuery.Table.SQLName возвращает "vw_TableName"

Вот для этого и были добавлены эти строки

if (!Connector.CurrentUser.IsAdmin) {
  TableName = 'tbl_' + TableName.substr(3, TableName.length);
}

Т.е. из vw_TableName вытягиваем подстроку с 3-й позиции и добавляем префикс 'tbl_'.

Немного доработал хранимку. Возвращает Caption таблиц и кол-во записей тоже сейчас.

Показать все комментарии