DirectumRX. Отчет для поиска связей при ошибке удаления "Запись уже используется".

39 8

Добрый день, коллеги!

Неоднократно наблюдал на ресурсе вопросы о том, что делать, если запись не удаляется, потому что "уже используется". А функционала по поиску связанных записей в системе нет. Мы также сталкивались с этой проблемой. И до недавнего времени, полагаясь на удачу, удавалось её решить. Пока эта самая удача не закончилась. В итоге было найдено другое решение (правда, пока только для Microsoft SQL Server).

Что вам понадобится:

  1. Доступ к SQL-серверу
  2. Имя таблицы для вашего типа сущности (можно посмотреть в SDS)
  3. ИД записи, которая не удаляется
--Заполнить имя БД
use DirectumRX
GO
--Указать таблицу типа сущности, который не удаляется
DECLARE @tableName nvarchar(max) = 'Sungero_Parties_Counterparty'
DECLARE @CursorQuery nvarchar(max);
--Указать ИД сущности, которая не удаляется
DECLARE @taregtId nvarchar(max) = '32'

DECLARE @tmp_Accounts TABLE (
PKTABLE_QUALIFIER sysname
,PKTABLE_OWNER sysname
,PKTABLE_NAME sysname
,PKCOLUMN_NAME sysname
,FKTABLE_QUALIFIER sysname
,FKTABLE_OWNER sysname
,FKTABLE_NAME sysname
,FKCOLUMN_NAME sysname
,KEY_SEQ smallint
,UPDATE_RULE smallint
,DELETE_RULE smallint
,FK_NAME nvarchar(max)
,PK_NAME nvarchar(max)
,deferrability smallint
)

insert into @tmp_Accounts exec sp_fkeys @tableName

DECLARE CUR_FK CURSOR FAST_FORWARD FOR
    select 'select Id, \"' + FKTABLE_NAME + '" from ' + FKTABLE_NAME + ' where ' + FKCOLUMN_NAME + ' = ' + @taregtId  from @tmp_Accounts
 
OPEN CUR_FK
FETCH NEXT FROM CUR_FK INTO @CursorQuery
 
WHILE @@FETCH_STATUS = 0
BEGIN
   exec sp_executesql @CursorQuery

   FETCH NEXT FROM CUR_FK INTO @CursorQuery
END
CLOSE CUR_FK
DEALLOCATE CUR_FK
GO

Заполнив параметры, выполните скрипт, который вернет результат в следующем виде:

Если есть связь - вы увидите строку с ИД и наименованием таблицы связанной сущности, ну а найти её в системе уже не составит труда.

Илья Лёвин

Добрый день. Простите, а как посмотреть имя таблицы для нашего типа сущности?
Вот, например, Подразделения - где в SDS это можно увидеть? И что такое SDS?

Илья Лёвин: обновлено 08.06.2021 в 16:05
Антон Максунов

Илья, SDS (DDS) это Sungero Development Studio (Directum Development Studio) - среда разработки. Справа есть список объектов, в ветке Базовые решения можно найти стандартные справочники. Например для Подразделений

Свои справочники смотрите в своих решениях (отдельные ветки выше Базовых).

Антон Максунов: обновлено 08.06.2021 в 19:19
Михаил Кожевников

Добрый день. Спасибо что поделились скриптом. Недавно он очень пригодился для анализа. Доработанная версия скрипта:

--Заполнить имя БД
use DirectumRX
GO
--Указать таблицу типа сущности, который не удаляется
DECLARE @tableName nvarchar(max) = 'Sungero_Core_Recipient'
--Указать один или несколько ИД сущностей, которые не удаляются
DECLARE @taregtId nvarchar(max) = '12,61,132'

DECLARE @CursorQuery nvarchar(max);

DECLARE @tmp_Accounts TABLE (
PKTABLE_QUALIFIER sysname
,PKTABLE_OWNER sysname
,PKTABLE_NAME sysname
,PKCOLUMN_NAME sysname
,FKTABLE_QUALIFIER sysname
,FKTABLE_OWNER sysname
,FKTABLE_NAME sysname
,FKCOLUMN_NAME sysname
,KEY_SEQ smallint
,UPDATE_RULE smallint
,DELETE_RULE smallint
,FK_NAME nvarchar(max)
,PK_NAME nvarchar(max)
,deferrability smallint
)

insert into @tmp_Accounts exec sp_fkeys @tableName

DECLARE @tmp_ResultTable Table (
  TableRowId nvarchar(max),
  TableName nvarchar(max),
  FieldName nvarchar(max),
  FieldValue nvarchar(max)
);

DECLARE CUR_FK CURSOR FAST_FORWARD FOR
    select 'select Id, ''' + FKTABLE_NAME + ''', ''' + FKCOLUMN_NAME+ ''', ' + FKCOLUMN_NAME+ ' from ' + FKTABLE_NAME + ' where [' + FKCOLUMN_NAME + '] in (' + @taregtId +')' from @tmp_Accounts
 
OPEN CUR_FK
FETCH NEXT FROM CUR_FK INTO @CursorQuery
 
WHILE @@FETCH_STATUS = 0
BEGIN
   insert into @tmp_ResultTable
   exec sp_executesql @CursorQuery
   FETCH NEXT FROM CUR_FK INTO @CursorQuery
END
CLOSE CUR_FK
DEALLOCATE CUR_FK

SELECT * FROM @tmp_ResultTable

GO

Список изменений:
1. Добавлено экранирование имени поля в запросе квадратными скобочками - "...where [' + FKCOLUMN_NAME + ']..."
Без этой доработки возникала ошибка: "Msg 245, Level 16, State 1, Line 1 Conversion failed when converting the nvarchar value 'dbo' to data type int."
Дело в том что у одной из таблиц есть поле с именем "User", которое при выполнении запроса воспринималось как встроенная функция USER (в моем случае она возвращает "dbo") что и приводило к ошибке.

2. Изменен формат вывода - результат в виде одной таблицы, вместо десятка отдельных таблиц. Дальнейшая группировка и фильтрация выполняется в Excel.

3. Добавлена возможность задавать список ИД записей по которым ищутся связи. Пригождается когда надо анализировать несколько десятков записей, а доступа к БД для выполнения скриптов нет.

4. В результирующей таблице добавлены поля FieldName - имя поле в котором используется искомая запись и FieldValue - значение в поле (нужно для фильтрации когда выполняется поиск по списку ИД)

Анатолий Придыбайло

Может кто-то может поделиться таким же запросом для СУБД PostgreSQL?

Павел Таут
Милана Михеева

Анатолий, добрый день! Удалось запрос для PostgreSQL составить?

Анатолий Придыбайло

Милана, нет

Тимур Саяпов

Запрос для  PostgreSQL 

DO $$
DECLARE
  -- Таблица, на которую ссылаются.
  v_pk_table_name text := 'public.sungero_core_recipient';

  -- Идентификаторы, которые “не удаляются”.
  v_target_ids bigint[] := ARRAY[200,201];

  r record;
  v_sql text;
  v_rowid_expr text;
  v_pk_json_parts text;
BEGIN
  CREATE TEMP TABLE tmp_result (
    table_row_id text,   -- текст, т.к. это либо json с PK, либо ctid.
    table_name   text,
    field_name   text,
    field_value  text
  ) ON COMMIT DROP;

  FOR r IN
    SELECT
      fk_ns.nspname    AS fk_schema_name,
      fk_table.relname AS fk_table_name,
      fk_col.attname   AS fk_column_name
    FROM pg_constraint c
    JOIN pg_class pk_table   ON pk_table.oid = c.confrelid
    JOIN pg_namespace pk_ns  ON pk_ns.oid = pk_table.relnamespace
    JOIN pg_class fk_table   ON fk_table.oid = c.conrelid
    JOIN pg_namespace fk_ns  ON fk_ns.oid = fk_table.relnamespace
    JOIN LATERAL unnest(c.conkey)  WITH ORDINALITY AS fk_k(attnum, ord) ON true
    JOIN LATERAL unnest(c.confkey) WITH ORDINALITY AS pk_k(attnum, ord) ON pk_k.ord = fk_k.ord
    JOIN pg_attribute fk_col ON fk_col.attrelid = fk_table.oid AND fk_col.attnum = fk_k.attnum
    JOIN pg_attribute pk_col ON pk_col.attrelid = pk_table.oid AND pk_col.attnum = pk_k.attnum
    WHERE c.contype = 'f'
      AND (pk_ns.nspname || '.' || pk_table.relname) = v_pk_table_name
  LOOP
    -- Собираем выражение для "идентификатора строки" FK-таблицы:
    -- либо JSON из PK-колонок, либо ctid.
    SELECT string_agg(format('%L, t.%I::text', a.attname, a.attname), ', ')
      INTO v_pk_json_parts
    FROM pg_index i
    JOIN pg_attribute a
      ON a.attrelid = i.indrelid
     AND a.attnum = ANY(i.indkey)
    JOIN pg_class c2 ON c2.oid = i.indrelid
    JOIN pg_namespace n2 ON n2.oid = c2.relnamespace
    WHERE i.indisprimary
      AND n2.nspname = r.fk_schema_name
      AND c2.relname = r.fk_table_name;

    IF v_pk_json_parts IS NULL THEN
      v_rowid_expr := 't.ctid::text';
    ELSE
      v_rowid_expr := 'jsonb_build_object(' || v_pk_json_parts || ')::text';
    END IF;

    v_sql := format(
      'INSERT INTO tmp_result (table_row_id, table_name, field_name, field_value)
       SELECT %s,
              %L,
              %L,
              t.%I::text
       FROM %I.%I t
       WHERE t.%I = ANY($1)',
      v_rowid_expr,
      r.fk_schema_name || '.' || r.fk_table_name,
      r.fk_column_name,
      r.fk_column_name,
      r.fk_schema_name,
      r.fk_table_name,
      r.fk_column_name
    );

    EXECUTE v_sql USING v_target_ids;
  END LOOP;

END $$;

SELECT *
FROM tmp_result
ORDER BY table_name, field_name, table_row_id;

 

Авторизуйтесь, чтобы написать комментарий