Проверка на дубликаты уникальных идентификаторов

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

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

EXECUTE BLOCK
  RETURNS(tbl VARCHAR(31), cnt INTEGER)
AS
  DECLARE VARIABLE rn VARCHAR(31);
  DECLARE VARIABLE fn VARCHAR(31);
  DECLARE VARIABLE id INTEGER;
  DECLARE VARIABLE s INTEGER;
  DECLARE VARIABLE c INTEGER;
BEGIN
  IF (g_his_create(0, 0) = 0) THEN
    EXCEPTION gd_e_exception 'Can''t create huge array';
 
  FOR
    SELECT
      rf.rdb$relation_name, LIST(TRIM(rf.rdb$field_name))
    FROM
      rdb$relation_fields rf
      JOIN rdb$relations r ON r.rdb$relation_name = rf.rdb$relation_name
      JOIN rdb$index_segments idxs ON idxs.rdb$field_name = rf.rdb$field_name
      JOIN rdb$indices idx ON idx.rdb$index_name = idxs.rdb$index_name
      JOIN rdb$relation_constraints rc ON rc.rdb$index_name = idx.rdb$index_name
        AND rc.rdb$relation_name = rf.rdb$relation_name
    WHERE
      rc.rdb$constraint_type = 'PRIMARY KEY'
      AND
      rf.rdb$relation_name <> 'GD_RUID'
      /*
 
      Тут должно быть условие на таблицы, в которых не соблюдается
      условие уникальности ИД.
 
      */
      AND
      r.rdb$system_flag = 0
      AND
      COALESCE(r.rdb$relation_type, 0) = 0
      AND
      NOT EXISTS(
        SELECT * 
        FROM 
          rdb$index_segments idxs_fk 
          JOIN rdb$indices idx_fk ON idxs_fk.rdb$index_name = idx_fk.rdb$index_name
        WHERE
          idxs_fk.rdb$field_name = rf.rdb$field_name
          AND        
          idx_fk.rdb$relation_name = rf.rdb$relation_name
          AND
          idx_fk.rdb$index_name <> idx.rdb$index_name          
      )
    GROUP BY
      1
    HAVING
      LIST(TRIM(rf.rdb$field_name)) = 'ID'
  INTO
    :rn, :fn
  DO BEGIN
    s = 0;
    c = 0;
    EXECUTE STATEMENT
      'SELECT SUM(g_his_include(0, id)), COUNT(*) FROM ' || :rn || ' WHERE id > 147000000'
    INTO :s, :c;
 
    s = COALESCE(:s, 0);
    c = COALESCE(:c, 0);
 
    IF (:c <> :s) THEN
    BEGIN
      tbl = :rn;
      cnt = :c - :s;
      SUSPEND;
    END  
  END
 
  g_his_destroy(0);
END

Процедура выводит все ИД из базы данных в привязке к ИД таблицы:

CREATE OR ALTER PROCEDURE gd_p_find_dupl_id
  RETURNS(id INTEGER, relationkey INTEGER)
AS
  DECLARE VARIABLE rn VARCHAR(31);
  DECLARE VARIABLE fn VARCHAR(31);
  DECLARE VARIABLE rid INTEGER;
BEGIN
  FOR
    SELECT
      rf.rdb$relation_name, atr.id, LIST(TRIM(rf.rdb$field_name))
    FROM
      rdb$relation_fields rf
      JOIN rdb$relations r ON r.rdb$relation_name = rf.rdb$relation_name
      JOIN rdb$index_segments idxs ON idxs.rdb$field_name = rf.rdb$field_name
      JOIN rdb$indices idx ON idx.rdb$index_name = idxs.rdb$index_name
      JOIN rdb$relation_constraints rc ON rc.rdb$index_name = idx.rdb$index_name
        AND rc.rdb$relation_name = rf.rdb$relation_name
      JOIN at_relations atr ON atr.relationname = r.rdb$relation_name
    WHERE
      rc.rdb$constraint_type = 'PRIMARY KEY'
      AND
      rf.rdb$relation_name <> 'GD_RUID'
      /*
 
      Тут должно быть условие на таблицы, в которых не соблюдается
      условие уникальности ИД.
 
      */
      AND
      r.rdb$system_flag = 0
      AND
      COALESCE(r.rdb$relation_type, 0) = 0
      AND
      NOT EXISTS(
        SELECT *
        FROM
          rdb$index_segments idxs_fk
          JOIN rdb$indices idx_fk ON idxs_fk.rdb$index_name = idx_fk.rdb$index_name
        WHERE
          idxs_fk.rdb$field_name = rf.rdb$field_name
          AND
          idx_fk.rdb$relation_name = rf.rdb$relation_name
          AND
          idx_fk.rdb$index_name <> idx.rdb$index_name
      )
    GROUP BY
      1, 2
    HAVING
      LIST(TRIM(rf.rdb$field_name)) = 'ID'
  INTO
    :rn, :rid, :fn
  DO BEGIN
    relationkey = :rid;
    FOR
      EXECUTE STATEMENT 'SELECT id FROM ' || :rn
      INTO :id
    DO
      SUSPEND;
  END
END

Используется для поиска дубликатов с помощью запроса:

SELECT 
  id, list(relationkey), COUNT(*) 
FROM 
  gd_p_find_dupl_id
GROUP BY 
  1
HAVING 
  COUNT(*) > 1
Персональные инструменты
Пространства имён

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