29 November 2016

Адреса ФИАС в среде PostgreSQL. Часть 2

PostgreSQL
Это вторая часть статьи, в которой изложен опыт работы со списком адресообразующих элементов ФИАС, загруженным в базу данных под управлением PostgreSQL. С первой частью статьи можно ознакомиться здесь.



Полный текст статьи состоит состоит из 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');


Спасибо за внимание!
Tags:#ФИАС#PostgreSQL#Stored Procedure
Hubs: PostgreSQL
+6
9.2k 54
Comments 8
Popular right now