Это вторая часть статьи, в которой изложен опыт работы со списком адресообразующих элементов ФИАС, загруженным в базу данных под управлением PostgreSQL. С первой частью статьи можно ознакоматься здесь: habrahabr.ru/post/316314
Полный текст статьи состоит состоит из 4 частей.
В первой половине этой части статьи изложены комментарии к реализации функции. Во второй- исходный текст функции.
Тем из читателей, кого интересуют только исходные тексты, предлагаем сразу перейти к Приложению.
Полное наименование адресообразующего элемента
Основная идея функции fsfn_AddressObjects_TreeActualName в том, чтобы возвратить соединенные в одну строку название элемента вместе с названиями всех его предков.
Например, пусть функции поиска родословной элемента (fstf_AddressObjects_AddressObjectTree) возвращает следующий список значений.
Таблица 5. результат выполнения функции fstf_AddressObjects_AddressObjectTree(‘bfc1236d-b5d2-4734-a238-3b1e4830e963’)
AOGUID | CurrStatus | ActStatus | AOLevel | ShortName | FormalName | ObjectGroup |
---|---|---|---|---|---|---|
db9c4f8b-b706-40e2-b2b4-d31b98dcd3d1 | 0 | 1 | 1 | край | Красноярский | Region |
625497d3-22de-4390-b4b4-2febfbfc15ce | 0 | 1 | 3 | р-н | Балахтинский | Territory |
39da6405-b3e6-4baf-b332-d47b73b4d5fb | 0 | 1 | 6 | п | Могучий | Locality |
bfc1236d-b5d2-4734-a238-3b1e4830e963 | 0 | 1 | 7 | ул | Новая | Street |
Тогда fsfn_AddressObjects_TreeActualName(‘bfc1236d-b5d2-4734-a238-3b1e4830e963’) должна возвратить:
«Красноярский край, Балахтинский р-н, п Могучий, ул Новая»
У функции есть еще одни необязательный параметр массив масок (a_MaskArray), с помощью которого можно включать в результат не все названия элементов, а только те, которые нужны.
Текст функции приведен в разделе Приложения «Создание функции fsfn_AddressObjects_TreeActualName».
Как это работает
Основу реализации функции составляет вызов fstf_AddressObjects_AddressObjectTree (описанная в первой части статьи) и цикл по возвращаемым ей записям, в теле которой формируется полное наименование адресообразующего элемента путем сцепления (конкатенации) всех наименований в одну строку. Эта строка в конце концов будет возвращения функцией fsfn_AddressObjects_TreeActualName.
Далее будут поясняться детали.
Во-первых, порой нет необходимости в том, чтобы результат функции обязательно включал наименования всех предков текущего элемента. Например, в пределах Красноярского края вместо «Красноярский край, Балахтинский р-н, п Могучий, ул Новая», чаще используют укороченную форму «Балахтинский р-н, п Могучий, ул Новая». А внутри города Красноярска вместо адреса «Красноярский край, г Красноярск, д Песчанка, ул Сергея Лазо» чаще используют «д Песчанка, ул Сергея Лазо».
Для того чтобы иметь возможность управлять разными формами написания полного наименования адресообразующего элемента введен параметр массив масок (a_MaskArray), который содержит последовательность указателей (масок) на группы элементов,
Таблица 6. Список масок функции
Значение | Примечание |
---|---|
{ST} | Маска — улица |
{ZC} | Маска — почтовый индекс |
{DT} | Маска — городской район |
{LP} | Маска — подчиненный населенный пункт |
{LM} | Маска — основной населенный пункт |
{TP} | Маска — района субъекта федерации |
{TM} | Маска — субъект федерации (регион) |
{CY} | Маска — страна |
Во-вторых, для того чтобы реализовать построение полного наименования в соответствии с массивом масок создана вспомогательная функция fsfn_AddressObjects_ObjectGroup, которая относит каждый адресообразующий элемент к определенной группе.
Таблица 7. Значения, возвращаемые функцией fsfn_AddressObjects_ObjectGroup
Значение | Примечание |
---|---|
Country | Признак группы — Страна |
Region | Признак группы — Регион |
City | Признак группы — Основной населенный пункт |
Territory | Признак группы — район |
Locality | Признак группы — населенный пункт подчиненный основному |
MotorRoad | Признак группы — автомобильная дорога |
RailWayObject | Признак группы — железная дорога |
VillageCouncil | Признак группы — сельсовет |
Street | Признак группы — улица в населенном пункте |
AddlTerritory | Признак группы — дополнительная территория |
PartAddlTerritory | Признак группы — часть дополнительной территории |
Список значений, возвращаемых функцией fsfn_AddressObjects_ObjectGroup, приведен в Таблица 5.
Цель создания этой функции в том чтобы собрать в одном месте все особенности (если хотите, то «костыли») определения группы элемента. С детальной реализацией этой функции можно ознакомиться в разделе Приложения «Создание функции fsfn_AddressObjects_ObjectGroup».
Комбинация значений функции и поля AOLevel (уровень адресообразующего элемента) вместе с проверкой на присутствие маски группы массиве масок позволяет определять должно ли название текущего элемента включаться в строку результата.
Например, признаком того, что название основного населенного пункта должно включаться в полное наименование элемента, является истинность следующего выражения:
v_ObjectGroup='City' AND '{LM}' <@ a_MaskArray AND v_AOLevel =4
.
ПРИЛОЖЕНИЕ
Создание функции fsfn_AddressObjects_ObjectGroup
BEGIN TRANSACTION; DROP FUNCTION IF EXISTS fsfn_AddressObjects_ObjectGroup(a_AOGUID VARCHAR(36),a_CurrStatus INTEGER); /*****************************************************************************/ /* Возвращает признак группы адресообразующего элемента по его идентификатору */ /* fias_AddressObjects */ /*****************************************************************************/ CREATE OR REPLACE FUNCTION fsfn_AddressObjects_ObjectGroup( a_AOGUID VARCHAR(36), /* Глобальный уникальный идентификатор */ /* адресообразующего элемента*/ a_CurrStatus INTEGER default NULL /* Статус актуальности КЛАДР 4: */ /* 0 - актуальный, */ /* 1-50 - исторический, */ /* т.е. элемент был переименован, */ /* в данной записи приведено одно */ /* из прежних его наименований, */ /* 51 - переподчиненный */ ) RETURNS VARCHAR(50) /* Группа адресообразующего элемента */ AS $BODY$ DECLARE c_CountryGroupValue CONSTANT VARCHAR(50):='Country'; c_RegionGroupValue CONSTANT VARCHAR(50):='Region'; c_CityGroupValue CONSTANT VARCHAR(50):='City'; c_TerritoryGroupValue CONSTANT VARCHAR(50):='Territory'; c_LocalityGroupValue CONSTANT VARCHAR(50):='Locality'; c_MotorRoadValue CONSTANT VARCHAR(50):='MotorRoad'; c_RailWayObjectValue CONSTANT VARCHAR(50):='RailWayObject'; c_VillageCouncilValue CONSTANT VARCHAR(50):='VillageCouncil'; c_StreetGroupValue CONSTANT VARCHAR(50):='Street'; c_AddlTerritoryValue CONSTANT VARCHAR(50):='AddlTerritory'; c_PartAddlTerritoryValue CONSTANT VARCHAR(50):='PartAddlTerritory'; v_ShortTypeName VARCHAR(10); /* Тип адресообразующего элемента */ v_AddressObjectName VARCHAR(100); /* Название адресообразующего элемента */ v_AOLevel INTEGER; /* Уровень адресообразующего элемента*/ v_CurrStatus INTEGER; /* Текущий статус адресообразующего элемента*/ v_ObjectGroup VARCHAR(50); /* Группа адресообразующего элемента */ v_Return_Error Integer :=0; /* Код возврата */ --************************************************************************** --************************************************************************** BEGIN SELECT INTO v_CurrStatus COALESCE(a_CurrStatus,MIN(addrobj.currstatus)) FROM fias_AddressObjects addrobj WHERE addrobj.AOGUID=a_AOGUID; SELECT INTO v_ShortTypeName,v_AddressObjectName,v_AOLevel ShortName,FormalName,AOLevel FROM fias_AddressObjects addrobj WHERE addrobj.AOGUID=a_AOGUID AND addrobj.currstatus = v_CurrStatus LIMIT 1; IF v_AOLevel = 1 AND UPPER(v_ShortTypeName) <> 'Г' THEN /* уровень региона */ v_ObjectGroup:=c_RegionGroupValue; ELSIF v_AOLevel = 1 AND UPPER(v_ShortTypeName) = 'Г' THEN /* уровень города */ /* как региона */ v_ObjectGroup:=c_CityGroupValue; ELSIF v_AOLevel = 3 THEN /* уровень района */ v_ObjectGroup:=c_TerritoryGroupValue; ELSIF (v_AOLevel = 4 AND UPPER(v_ShortTypeName) NOT IN ('С/С','С/А','С/О','С/МО')) OR (v_AOLevel = 1 AND UPPER(v_ShortTypeName) <> 'Г') THEN /* уровень города */ v_ObjectGroup:=c_CityGroupValue; ELSIF v_AOLevel IN (4,6) AND UPPER(v_ShortTypeName) IN ('С/С','С/А','С/О','С/МО') AND UPPER(v_ShortTypeName) NOT LIKE ('Ж/Д%') THEN /* уровень сельсовета */ v_ObjectGroup:=c_VillageCouncilValue; ELSIF v_AOLevel = 6 AND UPPER(v_ShortTypeName) NOT IN ('С/С','С/А','С/О','С/МО', 'САД','СНТ','ТЕР', 'АВТОДОРОГА', 'ПРОМЗОНА', 'ДП','МКР') AND UPPER(v_ShortTypeName) NOT LIKE ('Ж/Д%') THEN /* уровень населенного */ /* пункта */ v_ObjectGroup:=c_LocalityGroupValue; ELSIF UPPER(v_ShortTypeName) IN ('АВТОДОРОГА') THEN /* уровень */ /* автомобильной дороги */ v_ObjectGroup:=c_MotorRoadValue; ELSIF v_AOLevel IN (6,7) AND UPPER(v_ShortTypeName) LIKE ('Ж/Д%') THEN /* уровень элемент */ /* на железной дороге */ v_ObjectGroup:=c_RailWayObjectValue; ELSIF v_AOLevel = 7 AND UPPER(v_ShortTypeName) NOT LIKE ('Ж/Д%') AND UPPER(v_ShortTypeName) NOT IN ('УЧ-К','ГСК','ПЛ-КА','СНТ','ТЕР') OR (v_AOLevel = 6 AND UPPER(v_ShortTypeName) IN ('МКР') ) THEN /* уровень улицы */ v_ObjectGroup:=c_StreetGroupValue; ELSIF v_AOLevel = 90 OR v_AOLevel = 6 AND UPPER(v_ShortTypeName) IN ('САД', 'СНТ','ТЕР','ПРОМЗОНА','ДП') OR v_AOLevel = 7 AND UPPER(v_ShortTypeName) IN ('УЧ-К','ГСК','ПЛ-КА','СНТ','ТЕР') THEN /* уровень дополнительных */ /* территорий */ v_ObjectGroup:=c_AddlTerritoryValue; ELSIF v_AOLevel = 91 THEN /* уровень подчиненных дополнительным территориям */ /* объектов */ v_ObjectGroup:=c_PartAddlTerritoryValue; END IF; RETURN v_ObjectGroup; END; $BODY$ LANGUAGE plpgsql; COMMENT ON FUNCTION fsfn_AddressObjects_ObjectGroup(a_AOGUID VARCHAR(36), a_CurrStatus INTEGER) IS 'Возвращает признак группы адресного объекта по его идентификатору в таблице fias_AddressObjects'; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECT fsfn_AddressObjects_ObjectGroup('719b789d-2476-430a-89cd-3fedc643d821',51); SELECT fsfn_AddressObjects_ObjectGroup('db9c4f8b-b706-40e2-b2b4-d31b98dcd3d1'); SELECT fsfn_AddressObjects_ObjectGroup('625497d3-22de-4390-b4b4-2febfbfc15ce'); SELECT fsfn_AddressObjects_ObjectGroup('39da6405-b3e6-4baf-b332-d47b73b4d5fb'); SELECT fsfn_AddressObjects_ObjectGroup('bfc1236d-b5d2-4734-a238-3b1e4830e963');
Создание функции fsfn_AddressObjects_TreeActualName
BEGIN TRANSACTION; DROP FUNCTION IF EXISTS fsfn_AddressObjects_TreeActualName(a_AOGUID VARCHAR(36),a_MaskArray VARCHAR(2)[10]) CASCADE; /*****************************************************************************/ /* Возвращает строку с полным названием адресообразующего элемента */ /*****************************************************************************/ CREATE OR REPLACE FUNCTION fsfn_AddressObjects_TreeActualName( a_AOGUID VARCHAR(36) DEFAULT NULL, /* Идентификтор */ /* адресообразующего элемента */ a_MaskArray VARCHAR(2)[10] default '{TP,LM,LP,ST}' /* Массив масок, */ /* управляющий содержанием строки */ /* с адресом дома*/ ) RETURNS VARCHAR(1000) AS $BODY$ DECLARE c_CountryGroupValue CONSTANT VARCHAR(50):='Country'; /* Признак группы - Страна*/ c_RegionGroupValue CONSTANT VARCHAR(50):='Region'; /* Признак группы - Регион*/ c_CityGroupValue CONSTANT VARCHAR(50):='City'; /* Признак группы - Основной */ /* населенный пункт*/ c_TerritoryGroupValue CONSTANT VARCHAR(50):='Territory';/* Признак группы - район */ c_LocalityGroupValue CONSTANT VARCHAR(50):='Locality';/* Признак группы - */ /* населенный пункт, */ /* подчиненный основному */ c_MotorRoadValue CONSTANT VARCHAR(50):='MotorRoad';/* Признак группы - */ /* автомобильная дорога */ c_RailWayObjectValue CONSTANT VARCHAR(50):='RailWayObject';/* Признак группы - */ /* железная дорога */ c_VillageCouncilValue CONSTANT VARCHAR(50):='VillageCouncil'; /* Признак группы - сельсовет */ c_StreetGroupValue CONSTANT VARCHAR(50):='Street'; /* Признак группы - */ /* улица в населенном пункте */ c_AddlTerritoryValue CONSTANT VARCHAR(50):='AddlTerritory';/* Признак группы - */ /* дополнительная территория*/ c_PartAddlTerritoryValue CONSTANT VARCHAR(50):='PartAddlTerritory';/* Признак группы */ /* - часть дополнительной территории*/ c_StreetMask CONSTANT VARCHAR(2)[1] :='{ST}';/* Маска улица */ c_PostIndexMask CONSTANT VARCHAR(2)[1] :='{ZC}';/* Маска почтовый индекс */ c_DistrictMask CONSTANT VARCHAR(2)[1] :='{DT}';/* Маска городской район*/ c_PartLocalityMask CONSTANT VARCHAR(2)[1] :='{LP}';/* Маска подчиненный */ /* населенный пункт*/ c_MainLocalityMask CONSTANT VARCHAR(2)[1] :='{LM}';/* Маска основной */ /* населенный пункт*/ c_PartTerritoryMask CONSTANT VARCHAR(2)[1] :='{TP}';/* Маска района */ /* субъекта федерации*/ c_MainTerritoryMask CONSTANT VARCHAR(2)[1] :='{TM}';/* Маска субъект федерации */ /* (регион)*/ c_CountryMask CONSTANT VARCHAR(2)[1] :='{CY}';/* Маска страна*/ v_ShortTypeName VARCHAR(10); /* Тип адресообразующего элемента */ v_AddressObjectName VARCHAR(100); /* Название адресообразующего элемента */ v_AOLevel INTEGER; /* Уровень адресообразующего элемента*/ v_MinCurrStatus INTEGER; /* Минимальное значение текущего статуса */ /* адресообразующего элемента*/ v_TreeAddressObjectName VARCHAR(1000); /* Полное в иерархии название элемента*/ v_ObjectGroup VARCHAR(50); /* Группа адресообразующего элемента */ v_TreeLeverCount INTEGER; /* Счетчик цикла*/ v_Return_Error_i Integer := 0; /* Код возврата*/ cursor_AddressObjectTree RefCURSOR; /* курсор по иерархии адреса*/ v_Return_Error Integer :=0; /* Код возврата */ --****************************************************************************** --****************************************************************************** BEGIN SELECT INTO v_MinCurrStatus MIN(addrobj.currstatus) FROM fias_AddressObjects addrobj WHERE aoguid=a_AOGUID; OPEN cursor_AddressObjectTree FOR SELECT rtf_ShortTypeName, REPLACE(rtf_AddressObjectName,' ',' '), rtf_AOLevel,fsfn_AddressObjects_ObjectGroup(rtf_AOGUID ) FROM fstf_AddressObjects_AddressObjectTree(a_AOGUID) ORDER BY rtf_AOLevel; v_TreeLeverCount:=0; v_TreeAddressObjectName:=''; FETCH FIRST FROM cursor_AddressObjectTree INTO v_ShortTypeName,v_AddressObjectName, v_AOLevel,v_ObjectGroup; WHILE FOUND LOOP v_TreeLeverCount:=v_TreeLeverCount+1; IF v_ObjectGroup=c_CountryGroupValue AND c_CountryMask <@ a_MaskArray AND v_AOLevel =0 THEN v_TreeAddressObjectName:=v_TreeAddressObjectName|| CASE WHEN v_TreeAddressObjectName='' THEN '' ELSE ', ' END || v_AddressObjectName||' '||v_ShortTypeName; ELSIF v_ObjectGroup=c_RegionGroupValue AND c_MainTerritoryMask <@ a_MaskArray AND v_AOLevel <=2 THEN v_TreeAddressObjectName:=v_TreeAddressObjectName|| CASE WHEN v_TreeAddressObjectName='' THEN '' ELSE ', ' END || CASE WHEN UPPER(v_ShortTypeName) LIKE UPPER('%Респ%') THEN 'Республика ' || v_AddressObjectName ELSE v_AddressObjectName|| ' '||v_ShortTypeName END; ELSIF v_ObjectGroup=c_TerritoryGroupValue AND c_PartTerritoryMask <@ a_MaskArray AND v_AOLevel =3 THEN v_TreeAddressObjectName:=v_TreeAddressObjectName|| CASE WHEN v_TreeAddressObjectName='' THEN '' ELSE ', ' END || v_AddressObjectName||' '||v_ShortTypeName; ELSIF v_ObjectGroup=c_CityGroupValue AND c_MainLocalityMask <@ a_MaskArray AND v_AOLevel =4 THEN v_TreeAddressObjectName:=v_TreeAddressObjectName|| CASE WHEN v_TreeAddressObjectName='' THEN '' ELSE ', ' END || CASE WHEN UPPER(LEFT(v_AddressObjectName,6+ LENGTH(v_ShortTypeName)))='ЗАТО '|| UPPER(TRIM(v_ShortTypeName))||'.' THEN v_AddressObjectName ELSE v_ShortTypeName ||' '|| v_AddressObjectName END; ELSIF v_ObjectGroup=c_LocalityGroupValue AND c_DistrictMask <@ a_MaskArray AND v_AOLevel =5 THEN v_TreeAddressObjectName:=v_TreeAddressObjectName|| CASE WHEN v_TreeAddressObjectName='' THEN '' ELSE ', ' END || v_AddressObjectName||' '||v_ShortTypeName ; ELSIF v_ObjectGroup=c_LocalityGroupValue AND c_PartLocalityMask <@ a_MaskArray AND v_AOLevel =6 THEN v_TreeAddressObjectName:=v_TreeAddressObjectName|| CASE WHEN v_TreeAddressObjectName='' THEN '' ELSE ', ' END || v_ShortTypeName ||' '|| v_AddressObjectName; ELSIF v_ObjectGroup=c_StreetGroupValue AND c_StreetMask <@ a_MaskArray AND v_AOLevel =7 THEN v_TreeAddressObjectName:=v_TreeAddressObjectName|| CASE WHEN v_TreeAddressObjectName='' THEN '' ELSE ', ' END || v_ShortTypeName ||' '|| v_AddressObjectName; END IF; FETCH NEXT FROM cursor_AddressObjectTree INTO v_ShortTypeName, v_AddressObjectName, v_AOLevel,v_ObjectGroup; END LOOP; CLOSE cursor_AddressObjectTree; RETURN v_TreeAddressObjectName; END; $BODY$ LANGUAGE plpgsql ; COMMENT ON FUNCTION fsfn_AddressObjects_TreeActualName(a_AOGUID VARCHAR(36), a_MaskArray VARCHAR(2)[10]) IS 'Возвращает строку с полным названием адресообразующего элемента'; --ROLLBACK TRANSACTION; COMMIT TRANSACTION; SELECT fsfn_AddressObjects_TreeActualName('bfc1236d-b5d2-4734-a238-3b1e4830e963','{TM,TP,LM,LP,ST}'); SELECT fsfn_AddressObjects_TreeActualName('bfc1236d-b5d2-4734-a238-3b1e4830e963');
ссылка на оригинал статьи https://habrahabr.ru/post/316380/
Добавить комментарий