Поиск повторяющихся идентификаторов

Материал из GedeminWiki
Перейти к: навигация, поиск

Идеология платформы Гедымин требует уникальности идентификаторов объектов в пределах файла базы данных. Дублирующиеся идентификаторы являются следствием физического повреждения базы данных или логических сбоев в программном обеспечении.

Поиск дубликатов с использованием временной таблицы

Для поиска дубликатов создадим временную таблицу TEMP:

CREATE TABLE temp (
  id dintkey, 
  relid dintkey
)

Заполним ее:

EXECUTE BLOCK
AS
  DECLARE VARIABLE RN CHAR(31);
  DECLARE VARIABLE RID INTEGER;
BEGIN
  FOR
    SELECT DISTINCT
      r.rdb$relation_name, r.rdb$relation_id
    FROM
      rdb$relation_fields rf JOIN rdb$relations r
        ON r.rdb$relation_name = rf.rdb$relation_name
      LEFT JOIN
        (
          SELECT relc.rdb$relation_name
          FROM
            rdb$relation_constraints relc JOIN rdb$ref_constraints refc
              ON refc.rdb$constraint_name = relc.rdb$constraint_name
            JOIN rdb$index_segments iseg
              ON iseg.rdb$index_name = relc.rdb$index_name
                AND iseg.rdb$field_name = 'ID'
        ) constr ON constr.rdb$relation_name = r.rdb$relation_name
    WHERE
      rf.rdb$field_name = 'ID' AND r.rdb$relation_name <> 'TEMP'
        AND r.rdb$relation_name <> 'GD_RUID'
        AND r.rdb$view_source IS NULL
        AND constr.rdb$relation_name IS NULL
    INTO :RN, :RID
  DO BEGIN
    EXECUTE STATEMENT
      'INSERT INTO TEMP (id, relid) ' ||
      '  SELECT id, ' || :RID || ' FROM ' || :RN ||
      '  WHERE id > 32 '
      WITH AUTONOMOUS TRANSACTION;
  END
END

Теперь, получить список ИД, встречающихся более одного раза можно так:

SELECT
  t.id, COUNT(t.id), list(TRIM(r.rdb$relation_name))
FROM
  temp t JOIN rdb$relations r
    ON t.relid = r.rdb$relation_id
GROUP BY
  t.id
HAVING
  COUNT(t.id) > 1

Поиск дубликатов с использованием битовых массивов

На сервере должна быть установлена библиотека GUDF.DLL. Используются два битовых массива. База данных сканируется дважды. На первом проходе, первый массив заполняется всеми ИД из БД, второй -- только повторяющимися. На втором проходе выводится список повторяющихся ИД с именами таблиц.

EXECUTE BLOCK
  RETURNS(ID INTEGER, RN CHAR(31))
AS
  DECLARE VARIABLE DUMB INTEGER;
BEGIN
  g_his_create(0, 0);
  g_his_create(1, 0);
 
  FOR
    SELECT DISTINCT
      r.rdb$relation_name
    FROM
      rdb$relation_fields rf JOIN rdb$relations r
        ON r.rdb$relation_name = rf.rdb$relation_name
      LEFT JOIN
        (
          SELECT relc.rdb$relation_name
          FROM
            rdb$relation_constraints relc JOIN rdb$ref_constraints refc
              ON refc.rdb$constraint_name = relc.rdb$constraint_name
            JOIN rdb$index_segments iseg
              ON iseg.rdb$index_name = relc.rdb$index_name
                AND iseg.rdb$field_name = 'ID'
        ) constr ON constr.rdb$relation_name = r.rdb$relation_name
    WHERE
      rf.rdb$field_name = 'ID' AND r.rdb$relation_name <> 'TEMP'
        AND r.rdb$relation_name <> 'GD_RUID'
        AND r.rdb$view_source IS NULL
        AND constr.rdb$relation_name IS NULL
    INTO :RN
  DO BEGIN
    EXECUTE STATEMENT
      'SELECT SUM(g_his_include(1, id)) ' ||
      '  FROM ' || :RN ||
      '  WHERE g_his_has(0, id) <> 0 '
    INTO :DUMB;
 
    EXECUTE STATEMENT
      'SELECT SUM(g_his_include(0, id)) ' ||
      '  FROM ' || :RN
    INTO :DUMB;
  END
 
  FOR
    SELECT DISTINCT
      r.rdb$relation_name
    FROM
      rdb$relation_fields rf JOIN rdb$relations r
        ON r.rdb$relation_name = rf.rdb$relation_name
      LEFT JOIN
        (
          SELECT relc.rdb$relation_name
          FROM
            rdb$relation_constraints relc JOIN rdb$ref_constraints refc
              ON refc.rdb$constraint_name = relc.rdb$constraint_name
            JOIN rdb$index_segments iseg
              ON iseg.rdb$index_name = relc.rdb$index_name
                AND iseg.rdb$field_name = 'ID'
        ) constr ON constr.rdb$relation_name = r.rdb$relation_name
    WHERE
      rf.rdb$field_name = 'ID' AND r.rdb$relation_name <> 'TEMP'
        AND r.rdb$relation_name <> 'GD_RUID'
        AND r.rdb$view_source IS NULL
        AND constr.rdb$relation_name IS NULL
    INTO :RN
  DO BEGIN
    FOR
      EXECUTE STATEMENT
        'SELECT id ' ||
        '  FROM ' || :RN ||
        '  WHERE g_his_has(1, id) <> 0 '
      INTO :id
    DO BEGIN
      SUSPEND;
    END
  END
 
  g_his_destroy(0);
  g_his_destroy(1);
END

См. также

Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
Инструменты