Проверка на дубликаты уникальных идентификаторов
Материал из 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