PostgreSQL_intoDOC

Статья продолжает знакомить с функциями для документирования баз данных PostgreSQL. Но на этот раз речь пойдет о специальных функциях, подготавливающих описания диаграмм классов на языке PlantUML. В качестве основного средства документирования выбрана система управления проектами TRAC с подключенным плагином plantuml.

Это вторая часть статьи, в которой обсуждаются вспомогательные функции, использующиеся функциями предназначенными для непосредственного формирования PlantUML-скриптов. Основные процедур и функции обсуждались в первой части статьи. Тем из читателей, кого интересуют только исходные тексты или описание контрольного примера предлагаем сразу перейти к Приложению.

Содержание

  1. Вспомогательные функции

    1. Функция admtf_Table_Indexes список индексов таблицы базы данных и их характеристик

    2. Функция admfn_Table_isAttributeExists признак наличия в таблице атрибута (колонки) с заданным именем

    3. Функция admfn_Table_TableNameArray — Массив названий таблиц по условию

    4. Функция admfn_Table_LinkSign — Признак связи таблицы в ограничении

    5. Функция admfn_Table_ForeignKeyRole — Роль таблицы в ограничении

    6. Функция admfn_Table_ConstrainedLinkSign — Мощность таблицы в ограничении

    7. Функция admfn_ForeignKey_ConstrainedTableName — название таблицы с ограничением

    8. Функция admfn_Table_TablePK — Массив атрибутов первичного ключа

    9. Функция admfn_Table_TableFK — Массив атрибутов внешнего ключа

    10. Функция admfn_Table_haveUniqueIndexes— признак наличия уникального индекса

    11. Функция admtf_Table_UniqueIndexes— список уникальных индексов

    12. Функция cnfn_Array_LowerCase— массив в нижний регистр

    13. Функция cnfn_Array_QuotesWrapperWithout— удаление обрамляющих кавычек

  2. ПРИЛОЖЕНИЕ 1.

    1. Создание функции admtf_Table_Indexes

    2. Создание функции admfn_Table_isAttributeExists

    3. Создание вспомогательной функции admfn_Table_TableNameArray

    4. Создание вспомогательной функции admfn_Table_LinkSign

    5. Создание вспомогательной функции admfn_Table_ForeignKeyRole

    6. Создание вспомогательной функции admfn_Table_ConstrainedLinkSign

    7. Создание вспомогательной функции admfn_ForeignKey_ConstrainedTableName

    8. Создание вспомогательной функции admfn_Table_TablePK

    9. Создание вспомогательной функции admfn_Table_TableFK

    10. Создание вспомогательной функции admfn_Table_haveUniqueIndexes

    11. Создание вспомогательной функции admtf_Table_UniqueIndexes

    12. Создание вспомогательной функции cnfn_Array_LowerCase

  3. ПРИЛОЖЕНИЕ 2. Дополнительные материалы

    1. Краткий комментарий к загрузке контрольного примера

      1. Состав таблиц загрузки

      2. Состав таблиц контрольного примера

      3. Создание таблиц контрольного примера

Функция admtf_Table_Indexes список индексов таблицы базы данных и их характеристик

 Замечание

Скрытый текст

Эта функция подробно рассматривалась во второй части статьи "Функции для документирования баз данных PostgreSQL".

Со времени публикации той статьи прошло 6 лет. В ней приведены функции актуальные для PostgreSQL версий 9.3, 9.4. В последующих версиях были внесены изменения в структуру каталога pg_am. В результате нерабочими оказались функции, возвращающие характеристики индексов. Причина в том, что из каталога pg_am удалён признак amcanorder, определяющий возможность упорядочения данных атрибутов индекса. Поэтому в следующих версиях PostgreSQL порядок расположения атрибутов индекса (ASC, DESC) определяется без учета значения признака amcanorder. И как следствие возникает ошибка выполнения функции.

Здесь рассматривается измененная версия функции admtf_Table_Indexes

Функция admtf_Table_Indexes возвращает список индексов (INDEX) таблицы базы данных и их характеристик.Исходный код можно посмотреть и скачать здесь.

В качестве параметров функция принимает название исходной таблицы (a_TableName) и название схемы, в пределах которой создана таблица (a_SchemaName).

SELECT_Table_I_woc_inside
Скрытый текст
SELECT inxcls.relname AS r_IndexName ,inxam.amname AS r_IndexType,inx.indisunique AS r_isUnique,inx.indisprimary AS r_isPrimary,
			STRING_AGG(attr.attname|| CASE inx.indoption[inx.No] & 1 WHEN 1 THEN ' DESC' ELSE ' ASC'	END,
				c_Delimiter ORDER BY inx.No)
		FROM (SELECT i.indrelid, i.indexrelid,i.indisunique,i.indisprimary,i.indkey::SMALLINT[], 
				i.indoption::SMALLINT[],generate_subscripts(i.indkey, 1) as No FROM pg_index i) inx	
			                            INNER JOIN pg_class inxcls ON inx.indexrelid=inxcls.oid 
					INNER JOIN pg_am inxam ON inxcls.relam=inxam.oid
					INNER JOIN  pg_class tbl ON inx.indrelid=tbl.oid
					INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid 
					INNER JOIN pg_attribute attr ON attr.attrelid=tbl.OID AND attr.attnum=inx.indkey[inx.No]
				WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName)
				GROUP BY inxcls.relname,inxam.amname,inx.indisunique,inx.indisprimary
				ORDER BY inxcls.relname;

Описание отдельного индекса представляет собой совокупность записи в pg_class, описывающей его как физическое отношение, и записи в pg_index, содержащей данные о специфических характеристиках индекса. Дополнительно информация о методах доступа индекса хранится в системном каталоге pg_am.

Из записи каталога pg_index извлекаются признак уникальности индекса (indisunique), признак того, что индекс построен в соответствии с описанием первичного ключа (indisprimary), а также массивы порядковых номеров атрибутов таблицы, по значениям которых строится индекс (indkey) и признаков порядка сортировки значений атрибутов в индексе (indoption).

Смысл значений массива indoption показан на следующем рисунке — если правый бит двоичной формы значения содержит 1B, то значение соответствующего атрибута сортируются в убывающем порядке, в противном случае — в возрастающем порядке.

SELECT_Table_I_ASC_DESC
Скрытый текст
CASE inx.indoption[inx.No] & 1 
                                        WHEN 1 THEN ' DESC' 
                                        ELSE ' ASC'  END;

Для получения всех индексов таблицы применяются следующие методы::

  • размножение записей с помощью generate_subscripts;

  • последующая группировка записей;

  • создание списка атрибутов индексов при помощи функции STRING_AGG с опцией ORDER BY.

В приложении приведена универсальная версия функции, которая может выполняться как в POSTGRESQL 9.3, так и в следующих версиях. Проверка наличия признака amcanorder выполняется с помощью функции admfn_Table_isAttributeExists.

Таблица 10.1. демонстрирует результат выполнения функции admtf_Table_Indexes с параметрами 'public','Street'.

Table_Indexes_Result
Таблица 10.1. Результат выполнения функции admtf_Table_Indexes ('public','Street').

Исходный код функции можно посмотреть здесь.

admfn_Table_isAttributeExists - признак наличия в таблице атрибута (колонки) с заданным именем

Функция admfn_Table_isAttributeExists возвращает логическое значение (TRUE,FALSE) признака наличия в таблице атрибута (колонки) с заданным именем.

В качестве параметров функция принимает название атрибута таблицы (a_AttributeName),название исходной таблицы (a_TableName) и название схемы, в пределах которой создана таблица (a_SchemaName).

Основные данные функция извлекает из записи каталогов pg_attribute, pg_class и pg_namespace. Первый содержит записи с данными об атрибутах таблиц, представлений, материализованных представлений, составных типов и даже функций. Второй – о характеристиках таблиц (отношений). Третий – о характеристиках схем базы данных.

Ниже приведены 3 варианта нахождения атрибута в таблице базы данных. Один классический, который использует соединение всех каталогов. В двух других используется системный тип regclass, с помощью которого квалифицированное имя таблицы преодразуется в системный идентификатор (OIDs) этой таблицы. В результате чего оператор SELECT выглядит нагляднее.

SELECT_Table_A_if_exists
Скрытый текст
-- Первый вариант - классический
        SELECT  attr.*,tbl.*
                FROM pg_class tbl
                    INNER JOIN pg_attribute attr ON attr.attrelid=tbl.oid
					INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid 
                WHERE tbl.relname==a_SchemaName AND nsp.nspname=a_SchemaName
                        AND attr.attname=a_AttributeName
                ORDER BY tbl.relname,attr.attname;
-- Второй вариант - без каталога pg_namespace с использованием типа regclass
        SELECT  attr.*,tbl.*
                FROM pg_class tbl
                    INNER JOIN pg_attribute attr ON attr.attrelid=tbl.oid
                WHERE tbl.oid=(a_SchemaName||'.'||a_TableName)::regclass
                        AND attr.attname=a_AttributeName
                ORDER BY tbl.relname,attr.attname;
-- Третий вариант - только с каталогом pg_attribute с использованием типа regclass
        SELECT  attr.*
                FROM pg_attribute attr 
                WHERE attr.attrelid=(a_SchemaName||'.'||a_TableName)::regclass
                        AND attr.attname=a_AttributeName
                ORDER BY attr.attname;

SELECT_Table_A_if_exists_result
Таблица 10.2. Результат выполнения функции admfn_Table_isAttributeExists ('public','Street', 'streetname').

Исходный код функции можно посмотреть здесь.

Функция admfn_Table_TableNameArray

Вспомогательная функция admfn_Table_TableNameArray возвращает массив названий таблиц, полеченный из записей каталога pg_class по условию WHERE, переданному через параметры.

В качестве основных параметров функция принимает условие поиска таблиц (a_WHEREoption), которое не содержит ключевое слово WHERE, и название схемы, в пределах которой будет вестись поиск (a_SchemaName).

SELECT_adm_Table_TNA_inside
Скрытый текст
SELECT ARRAY_AGG(tbl.relname) 
FROM pg_class tbl INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid 
WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND tbl.relkind=LOWER('r') AND (a_WHEREoption);

При описании условия выбора таблиц следует учесть названия псевдонимов (aliases) каталогов, которые используются в функции. Так для каталога pg_class используется псевдоним (alias) «tbl», а для каталога pg_namespace«nsp».

Кроме, тего в теле функции предусмотрна проверка коректности оператора SELECT после включения в него значений параметров. Если переданное условие некорректно, вместо массива возвращается NULL и выдается сообщение об ошибке подобное следующему.

ERROR_adm_Table_TNA_inside

Таблица 11 показывает пример результата, возвращаемого функцией при корректном значении параметра a_WHEREoption.

adm_Table_TNA_Tbl011
Таблица 11. Результат выполнения функции admfn_Table_TableNameArray('public', 'tbl.relname ~* E''^Street''')

сходный код функции можно посмотреть здесь.

Функция admfn_Table_LinkSign

Вспомогательная функция admfn_Table_LinkSign возвращает признак связи таблиц ('N:1','1:N','1:1'), соответствующей ограничению Foreign Key.

Порядок формирования признака зависит от роли (места) исходной таблицы в заданном внешнем ключе, которая определяется функцией admfn_Table_ForeignKeyRole, в зависимости от того ссылается ли внешний ключ на таблицу ('Referenced') или является частью таблицы('Constrained').

При этом значение признака связи главным образом зависит от результата выполнения функции admfn_Table_ConstrainedLinkSign, которая возвращает мощность таблицы, признак количества записей, участвующих в связи, образованной ограничением. Особенностью этой функции является то, что она возвращает значения ('N', '1') только для таблицы, содержащей внешний ключ, т.к. мощность таблицы на которую ссылается внешний ключ всегда равна '1'.

SELECT_adm_Table_LS_inside
Скрытый текст
v_ForeignKeyRole:=admfn_Table_ForeignKeyRole(a_SchemaName,a_TableName,a_FKConstrantName);
IF v_ForeignKeyRole='Constrained' THEN	v_CurrSign:=admfn_Table_ConstrainedLinkSign(a_SchemaName,a_TableName,a_FKConstrantName);	v_LinkSign:=v_CurrSign||':'||'1';
ELSIF v_ForeignKeyRole='Referenced'THEN	v_ConstrainedTableName:=admfn_ForeignKey_ConstrainedTableName(a_SchemaName,a_FKConstrantName);	v_CurrSign:=admfn_Table_ConstrainedLinkSign(a_SchemaName,v_ConstrainedTableName,a_FKConstrantName);	v_LinkSign:= '1'||':'||v_CurrSign;
ELSE	v_CurrSign:=NULL;
END IF;

Возможен случай, когда значением параметра a_TableName оказалась таблица, на которую ссылается внешний ключ (a_FKConstrantName), тогда вызывается функция admfn_ForeignKey_ConstrainedTableName, возвращающая по внешнему ключу, название таблицы, в которой он объявлен. В этом случае функция admfn_Table_ConstrainedLinkSign возвращает мощность таблицы, полученной с помощью admfn_ForeignKey_ConstrainedTableName.

adm_Table_LS_Tbl012
Таблица 12 показывает пример результатов, возвращаемых функцией в зависимости от таблицы и её роли во внешнем ключе.

Исходный код функции можно посмотреть здесь.

Функция admfn_Table_ForeignKeyRole

Вспомогательная функция admfn_Table_ForeignKeyRole возвращает признак роли (места) таблицы в ограничении(constraint) внешний ключ (Foreign Key): 'Referenced' — таблица, на которую ссылается внешний ключ; 'Constrained' - таблица, которая ссылается на другую таблицу через внешний ключ.

В качестве основных параметров функция принимает названиие таблицы (a_TableName), название схемы, в пределах которой они созданы (a_SchemaName), а также название ограничения внешнего ключа (a_FKConstrantName).

Данные функция извлекает из записей каталога pg_constraint. Каталог pg_constraint при условии contype='f' содержит записи с характеристиками внешних ключей таблиц.

Явное обращение к данным каталогов pg_namespace и pg_class отсутствует благодаря использованию псевдонима regclass, преобразующего квалифицированное имя таблицы в её системный ИД (OID).

SELECT_adm_Table_FKR_inside
Скрытый текст
SELECT CASE (a_SchemaName ||'.'||a_TableName)::regclass WHEN con.conrelid THEN 'Constrained'								WHEN con.confrelid THEN 'Referenced' END
FROM pg_constraint con
WHERE LOWER(con.conname)=LOWER(a_FKConstrantName) AND con.contype='f';

Если системный ИД (OID) таблицы (a_TableName) совпадает со значением атрибута conrelid записи об ограничении внешнего ключа (a_FKConstrantName), то ограничение является частью описания таблицы. Если же OID таблицы равен confrelid, то внешний ключ ссылается на эту таблицу.

Таблица 13 показывает примеры вызова функции и результат. Для каждого параметра явно указывается его тип, потому что создано две версии функции admfn_Table_ForeignKeyRole. У одной параметры имеют тип NAME, у другой — VARCHAR.

adm_Table_FKR_Tbl013
Таблица 13. Результат выполнения функции SELECT admfn_Table_ForeignKeyRole

Исходный код функции можно посмотреть здесь.

Функция admfn_Table_ConstrainedLinkSign

Вспомогательная функция admfn_Table_ConstrainedLinkSign возвращает признак количества записей, участвующих в связи, соответствующей ограничению Foreign Key, со стороны таблицы: 'N' - много, '1'- одна.

В качестве основных параметров функция принимает названиие таблицы (a_TableName), название схемы, в пределах которой они созданы (a_SchemaName), а также название ограничения внешнего ключа (a_FKConstrantName).

В теле функции сравнивается список атрибутов внешнего ключа (a_FKConstrantName) таблицы со следующими списками атрибутов исходной таблицы (a_TableName):

  • Первичного ключа (PRIMARY KEY);

  • Уникального индекса (UNIQUE INDEX);

  • Ограничения уникальности (UNIQUE CONSTRAINT).

Если атрибуты внешного ключа совпадают с одним из вышеперечисленных списков атрибутов, то функция возвращает значение '1', во всех остальных случаях — 'N'. При этом, есть одно исключение — если описание таблицы не содержит внешний ключ, а является таблицей, на которую этот ключ ссылается, то в этом случае функция возвращает значение '1'.

Массивы сравниваемых атрибутов извлекаются из записей каталогов при помощи функций, названия которых содержит Таблица 14.

adm_Table_FKP_Tbl014
Таблица 14. функции, вызываемые в теле admfn_Table_ConstrainedLinkSign

Для получения атрибутов ограничений уникальности (r_ConstraintType='u') таблицы вызывается функция admtf_Table_Constraintes, с полным описанием которой можно ознакомиться в статье «Функции для документирования баз данных PostgreSQL».

ALTER_TABLE_adm_Table_FKP_inside
Скрытый текст
ALTER TABLE IF EXISTS public.streetsynonym
		ADD CONSTRAINT fk_streetsynonym_street FOREIGN KEY (wcrccode, streetid, localityid)
			REFERENCES public.street (wcrccode, streetid, localityid) MATCH FULL
			ON UPDATE NO ACTION
			ON DELETE NO ACTION;

Таблица 15 содержит примеры результатов выполнения функции для ограничения внешнего ключа, приведенного на рисунке.

adm_Table_FKP_Tbl015
Таблица 15. Результат выполнения функции SELECT admfn_Table_ConstrainedLinkSign

Исходный код функции можно посмотреть здесь.

Функция admfn_ForeignKey_ConstrainedTableName

Вспомогательная функция admfn_ForeignKey_ConstrainedTableName возвращает название таблицы, в которой объявлен внешний ключ.

В качестве основных параметров функция принимает название схемы (a_SchemaName) и название ограничения внешнего ключа (a_FKConstrantName).

Основные данные функция извлекает из записей каталога pg_constraint. Каталог pg_constraint при условии contype='f', а также из записей каталогов pg_class и pg_namespace.

SELECT_adm_FK_CTN_inside
Скрытый текст
SELECT bl.relname
FROM pg_constraint con 	
  INNER JOIN pg_namespace nspc ON con.connamespace = nspc.oid
  INNER JOIN pg_class tbl ON con.conrelid=tbl.oid
WHERE nspc.nspname=LOWER(a_SchemaName) AND con.contype ='f'	
   AND con.conname =LOWER(a_FKConstrantName);

Таблица 16 содержит примеры результатов выполнения функции для двух внешних ключе, наименования которых могут иметь тип NAME или VARCHAR.

adm_FK_CTN_Tbl016
Таблица 16. Результат выполнения функции SELECT admfn_ForeignKey_ConstrainedTableName

Исходный код функции можно посмотреть здесь.

Функция admfn_Table_TablePK

Вспомогательная функция admfn_Table_TablePK возвращает массив атрибутов, составляющих первичный ключ (Primary Key) таблицы.

В качестве основных параметров функция принимает названиие таблицы (a_TableName) и название схемы, в пределах которой они созданы (a_SchemaName).

SELECT_adm_Table_PK_inside
Скрытый текст
SELECT ARRAY_AGG(att.attname)
FROM (SELECT c.conrelid,c.conkey, GENERATE_SUBSCRIPTS(c.conkey, 1) PKAttributeID 
		FROM pg_constraint c 
		WHERE c.contype='p' AND c.conrelid=(LOWER(a_SchemaName)||'.'||LOWER(a_TableName))::regclass
		ORDER BY PKAttributeID) patt
	INNER JOIN pg_attribute att ON att.attrelid=patt.TableOID AND att.attnum=patt.PKAttributeID;

Основные данные функция извлекает из записей каталогов pg_constraint, pg_attribute. Каталог pg_constraint при условии contype='p' содержит записи с характеристиками первичных ключей таблиц, а pg_attribute содержит записи с данными об атрибутах таблиц, представлений, материализованных представлений, составных типов и даже функций.

Явное обращение к данным каталогов pg_namespace и pg_class отсутствует благодаря использованию псевдонима regclass, преобразующего квалифицированное имя таблицы в её системный ИД (OID).

Массив названий атрибутов создаётся функцией ARRAY_AGG.

Таблица 17 показывает пример вызова функции и результат. Для каждого параметра явно указывается его тип, потому что создано две версии функции admfn_Table_TablePK. У одной параметры имеют тип NAME, у другой — VARCHAR.

adm_Table_PK_Tbl017
Таблица 17. Результат выполнения функции SELECT admfn_Table_TablePK ('public'::VARCHAR, 'Street'::VARCHAR);

Исходный код функции можно посмотреть здесь.

Функция admfn_Table_TableFK

Вспомогательная функция admfn_Table_TableFK возвращает массив атрибутов, составляющих внешний ключ (Foreign Key) таблицы.

В качестве основных параметров функция принимает названиие таблицы (a_TableName), название схемы, в пределах которой они созданы (a_SchemaName), а также название ограничения внешнего ключа (a_FKConstrantName).

SELECT_adm_Table_FK_inside
Скрытый текст
SELECT ARRAY_AGG(att.attname)
FROM (SELECT c.conrelid,c.conkey, GENERATE_SUBSCRIPTS(c.conkey, 1) PKAttributeID 
		FROM pg_constraint c 
		WHERE c.contype='f' AND c.conrelid=(LOWER(a_SchemaName)||'.'||LOWER(a_TableName))::regclass 
			AND c.conname=LOWER(TRIM(a_FKConstrantName))
		ORDER BY PKAttributeID) patt
	INNER JOIN pg_attribute att ON att.attrelid=patt.TableOID AND att.attnum=patt.PKAttributeID;

Основные данные функция извлекает из записей каталогов pg_constraint, pg_attribute. Каталог pg_constraint при условии contype='f' содержит записи с характеристиками внешних ключей таблиц, а pg_attribute содержит записи с данными об атрибутах таблиц, представлений, материализованных представлений, составных типов и даже функций.

Явное обращение к данным каталогов pg_namespace и pg_class отсутствует благодаря использованию псевдонима regclass, преобразующего квалифицированное имя таблицы в её системный ИД (OID).

Массив названий атрибутов создаётся функцией ARRAY_AGG.

Таблица 18 показывает пример вызова функции и результат. Для каждого параметра явно указывается его тип, потому что создано две версии функции admfn_Table_TableFK. У одной параметры имеют тип NAME, у другой — VARCHAR.

adm_Table_FK_Tbl018
Таблица 18. Результат выполнения функции admfn_Table_TableFK ('public'::name, 'StreetSynonym'::name, 'fk_streetsynonym_street'::name);

Исходный код функции можно посмотреть здесь.

Функция admfn_Table_haveUniqueIndexes

Вспомогательная функция admfn_Table_haveUniqueIndexes возвращает значение ИСТИНА (TRUE), если описание таблицы содержит уникальный индекс.

В качестве основных параметров функция принимает названиие таблицы (a_TableName), название схемы, в пределах которой они созданы (a_SchemaName). Дополнительным параметром является признак необходимости учёта уникального индекса, соответствующего первичному ключу PRIMARY KEY (a_isPrimaryNeed).

SELECT_adm_Table_HUI_inside
Скрытый текст
SELECT inx.indisunique
		FROM pg_index inx 
		WHERE inx.indrelid=(TRIM(LOWER(a_SchemaName))||'.'||TRIM(LOWER(a_TableName)))::regclass AND inx.indisunique
			AND (inx.indisprimary=FALSE OR inx.indisprimary = a_isPrimaryNeed)

Основные данные функция извлекает из записей каталогов pg_index, содержащий специальные данные об интексе таблицы, которые отсутствуют в каталоге pg_class. Уникальность возвращаемого индекса гарантируется истинностью значения атрибута indisunique.

Явное обращение к данным каталогов pg_namespace и pg_class отсутствует благодаря использованию псевдонима regclass, преобразующего квалифицированное имя таблицы в её системный ИД (OID).

Таблица 19 показывает пример вызова функции и результат.

Для каждого параметра явно указывается его тип, потому что создано две версии функции admfn_Table_haveUniqueIndexes. У одной параметры имеют тип NAME, у другой — VARCHAR.

adm_Table_HUI_Tbl019
Таблица 19. Результат выполнения функции admfn_Table_haveUniqueIndexes

Для оценки приведенных примеров необходимы данные индексах таблицы Street. Как показывает Таблица 20, у этой таблицы только один уникальный индекс, созданный на основе первичного ключа. Поэтому функция в режиме a_isPrimaryNeed = TRUE возвращает истинное значение, а в режиме умолчания — ложное.

adm_Table_HUI_Tbl020
Таблица 20. Список индексов таблицы 'Street'

Типы индексов, указанные в таблице подробно рассмотрены в статьях Егора Рогова на ХАБРЕ «Индексы в PostgreSQL — 1, 2, 3, 4, 5, 6».

Исходный код функции можно посмотреть здесь.

Функция admtf_Table_UniqueIndexes

Вспомогательная функция admfn_Table_haveUniqueIndexes возвращает список уникальных индексов таблицы, как часть полного списка индексов полученного путём вызова. Условием выбора является истинность атрибута r_isUnique. С полным описанием функции admtf_Table_Indexes можно ознакомиться в статье «Функции для документирования баз данных PostgreSQL».

В качестве основных параметров функция принимает названиие таблицы (a_TableName), название схемы, в пределах которой они созданы (a_SchemaName). Дополнительным параметром является признак необходимости учёта уникального индекса, соответствующего первичному ключу PRIMARY KEY (a_isPrimaryNeed).

Для каждого параметра явно указывается его тип, потому что создано две версии функции admtf_Table_UniqueIndexes. У одной параметры имеют тип NAME, у другой — VARCHAR.

SELECT_adm_Table_UI_inside
Скрытый текст
SELECT ti.r_IndexName,ti.r_IndexType,ti.r_isUnique,ti.r_isPrimary,ti.r_IndexKeyNames
		FROM admtf_Table_Indexes(a_SchemaName, a_TableName, a_isPrimaryNeed) ti 
		WHERE ti.r_isUnique;

Таблица 21 демонстрирует список уникальных индексов для таблицы Street, т.к. у этой таблицы только один уникальный индекс, созданный на основе первичного ключа, . поэтому функция вызвана в режиме a_isPrimaryNeed = TRUE.

adm_Table_UI_Tbl021
Таблица 21. Результат выполнения функции admtf_Table_UniqueIndexes ('public'::name,'Street'::name,TRUE)

Типы индексов, указанные в таблице подробно рассмотрены в статьях Егора Рогова на ХАБРЕ «Индексы в PostgreSQL — 1, 2, 3, 4, 5, 6».

Исходный код функции можно посмотреть здесь.

Функция cnfn_Array_LowerCase

Вспомогательная функция cnfn_Array_LowerCase возвращает массив, каждый элемент которого преобразован в нажний регистр с помощью функции LOWER.

В качестве основного параметра функция принимает массив произвольных строк (a_SourceArray).

SELECT_adm_Array_LWC_inside
Скрытый текст
v_CurrentElement:=SUBSTRING(v_CurrentElement FROM 2 FOR LENGTH(v_CurrentElement)-2)

Таблица 22 показывает результаты, возвращаемые функцией для нескольких входных значений.

adm_Array_LWC_Tbl022
Таблица 22. Результат выполнения функции cnfn_Array_LowerCase

Исходный код функции можно посмотреть здесь.

Функция cnfn_Array_QuotesWrapperWithout

Вспомогательная функция cnfn_Array_QuotesWrapperWithout возвращает массив, из элементов которого удалены начальная и конечная одиночные кавычки, если они присутствуют.

В качестве основных параметров функция принимает массив произвольных строк (a_SourceArray).

В теле функции массив анализируется каждый элемент массива, если в значении найдены окружающие кавычки, то из такого значения функцией SUBSTRING извлекается подстрока.

SELECT_adm_Array_WOQ_inside
Скрытый текст
v_CurrentElement:=SUBSTRING(v_CurrentElement FROM 2 FOR LENGTH(v_CurrentElement)-2)

Таблица 23 показывает результаты, возвращаемые функцией для нескольких входных значений.

adm_Array_WOQ_Tbl023
Таблица 23. Результат выполнения функции cnfn_Array_QuotesWrapperWithout

Исходный код функции можно посмотреть здесь.

ПРИЛОЖЕНИЕ

Создание функции admtf_Table_Indexes

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст
BEGIN TRANSACTION; 
DROP FUNCTION IF EXISTS admtf_Table_Indexes (a_SchemaName NAME, a_TableName NAME);
DROP FUNCTION IF EXISTS admtf_Table_Indexes (a_SchemaName NAME, a_TableName NAME,a_isOrderNeed BOOLEAN);
/*******************************************************************************************************/
/*  Функция возвращает список индексов таблицы								*/
/*------------------------------------------------------------------------------------------------------*/
/*  Это универсальная версия функции, которая может выполняться как в POSTGRESQL 9.3., 			*/
/*  так и в следующих версиях	                                						*/
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admtf_Table_Indexes
			(a_SchemaName	NAME default 'public',/* название схемы базы данных		*/				
			a_TableName	NAME default NULL, 	/* Название таблицы */
			a_isOrderNeed	BOOLEAN default TRUE	/* Нужно ли добавлять признак порядка сортировки для каждого атрибута индекса? */
 )

RETURNS  TABLE (r_IndexName NAME,r_IndexType NAME,r_isUnique BOOLEAN,r_isPrimary BOOLEAN, r_IndexKeyNames Text) AS
$BODY$
DECLARE
	c_IndexKind             CONSTANT CHAR:='i';
	c_Delimiter             CONSTANT VARCHAR(2):=',';
	c_AmCanOrderAttrName    CONSTANT VARCHAR(256):='amcanorder';
	c_AdmnSchemaName        CONSTANT VARCHAR(256):='pg_catalog';
	c_withOrder             CONSTANT BOOLEAN:=TRUE; /* Обязательно добавлять признак порядка сортировки для каждого атрибута индекса */
	v_IndexRec		RECORD;			/* Данные об индексе*/
	v_Scale			INTEGER;		/* Масштаб колонки */
	v_TableOID		INTEGER;		/* OID таблицы*/
	v_IndexOID		INTEGER;		/* OID индекса*/
	v_IndexKeyNos		SMALLINT[];		/* Массив порядковых номеров атрибутов*/
	v_IndexKeyOps		SMALLINT[];		/* Массив опций атрибутов индекса*/
	v_IndexName		NAME;			/* Название индекса */
	v_IndexAMName		NAME;			/* Наименование типа индекса (метода доступа) */
	v_isUnique	 	BOOLEAN;		/* Признак уникальности индекса*/
	v_isPrimary	 	BOOLEAN;		/* Признак того что индекс представляет Primary KEY таблицы*/
	v_AttributeNum		INTEGER;		/* Порядковый номер атрибута*/
	v_AttributeOrderCode	INTEGER;		/* Признак упорядоченности атрибута*/
	v_AttributeName	NAME;			/* Наименование атрибута*/
	v_AmCanOrder		BOOLEAN;	 	/* Поддерживает ли метод доступа упорядоченное сканирование по значению */
							/* индексируемого столбца?*/
	v_OrderDirect		VARCHAR(10);		/* Направление упорядоченности атрибута в индексе*/
	v_IndexKeyNames	TEXT;			/* Строка со списком атрибутов индекса*/
	v_IndexAttributeLBound	INTEGER;		/* Нижний индекс масссива атрибутов индекса*/		
	v_IndexAttributeUBound	INTEGER;		/* Верхний индекс масссива атрибутов индекса*/		
	v_IndexAttributeIndx	INTEGER;		/* Текущий индекс масссива атрибутов индекса*/		
	v_pg_am_Version	INTEGER; 	/* Версия таблицы 'pg_am':  0 - версия postgresql ниже 9.6; 1 - версия postgresql выше 9.6  */
	v_isOrderNeed		BOOLEAN; 	/* Нужно ли добавлять признак порядка сортировки для каждого атрибута индекса? */
	v_SELECTStatement	TEXT; 		/* Оператор SELECT  */
	--******************************************************************************************************		
BEGIN
	v_isOrderNeed:=COALESCE(a_isOrderNeed,c_withOrder);
	IF  admfn_Table_isAttributeExists(c_AdmnSchemaName,'pg_am',c_AmCanOrderAttrName) THEN
		v_pg_am_Version:=0;
 	ELSE
		v_pg_am_Version:=1;
	END IF;
	FOR v_IndexRec IN SELECT inxcls.oid AS IndexOID,tbl.oid AS TableOID,inxcls.relname AS IndexName,
				inxam.amname AS IndexAMName,inx.indisunique AS isUnique,inx.indisprimary isPrimary,
				inx.indkey::SMALLINT[] AS IndexKeyNos,inx.indoption::SMALLINT[] AS IndexKeyOps, TRUE AS AmCanOrder
			FROM pg_index inx 
				INNER JOIN pg_class inxcls ON inx.indexrelid=inxcls.oid 
				INNER JOIN pg_am inxam ON inxcls.relam=inxam.oid
				INNER JOIN  pg_class tbl ON inx.indrelid=tbl.oid
				INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid 
			WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND inxcls.relkind=c_IndexKind
				AND tbl.relname=LOWER(a_TableName)
			ORDER BY inxam.amname, inxcls.relname LOOP
		v_IndexOID:=v_IndexRec.IndexOID;
		v_TableOID:=v_IndexRec.TableOID;
		v_IndexName:=v_IndexRec.IndexName;
		v_IndexAMName:=v_IndexRec.IndexAMName;
		v_isUnique:=v_IndexRec.isUnique;
		v_isPrimary:=v_IndexRec.isPrimary;
		v_IndexKeyNos:=v_IndexRec.IndexKeyNos;
		v_IndexKeyOps:=v_IndexRec.IndexKeyOps;
		v_AmCanOrder:=v_IndexRec.AmCanOrder;
		v_IndexKeyNames:='';
		v_IndexAttributeLBound:=array_lower(v_IndexKeyNos,1);
		v_IndexAttributeUBound:=array_upper(v_IndexKeyNos,1);
 		IF v_pg_am_Version=0 THEN
			v_SELECTStatement:='SELECT inxam.amcanorder AS AmCanOrder FROM pg_am AS inxam
				WHERE inxam.oid='||TRIM(TO_CHAR(v_IndexOID,'9999999999'));
			EXECUTE v_SELECTStatement INTO v_AmCanOrder;  
		END IF;  
		FOR v_IndexAttributeIndx IN v_IndexAttributeLBound .. v_IndexAttributeUBound
		LOOP
			v_AttributeNum:=v_IndexKeyNos[v_IndexAttributeIndx];
			v_AttributeOrderCode:=v_IndexKeyOps[v_IndexAttributeIndx];
			SELECT INTO v_AttributeName attr.attname::VARCHAR(100) AS r_AttributeName
				FROM pg_attribute attr 
				WHERE attr.attrelid=v_TableOID AND attr.attnum=v_AttributeNum;
			v_OrderDirect:=CASE WHEN NOT v_AmCanOrder THEN ''
					ELSE CASE WHEN v_AttributeOrderCode & 1=1 THEN 'DESC' 
						  ELSE 'ASC' 
					      END 
					END;
			v_IndexKeyNames:=v_IndexKeyNames||CASE WHEN v_IndexKeyNames='' THEN '' ELSE c_Delimiter||' ' END || 
						v_AttributeName||CASE WHEN NOT v_isOrderNeed OR v_OrderDirect='' THEN '' 
									ELSE ' '||v_OrderDirect END;	
		END LOOP;		
		RETURN QUERY SELECT v_IndexName,v_IndexAMName,v_isUnique,v_isPrimary,v_IndexKeyNames;					
	END LOOP;			
	RETURN;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admtf_Table_Indexes(a_SchemaName NAME, a_TableName NAME,a_isOrderNeed BOOLEAN) IS 'Возвращает список индексов таблицы';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;

BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admtf_Table_Indexes (a_SchemaName VARCHAR(256), a_TableName VARCHAR(256));
DROP FUNCTION IF EXISTS admtf_Table_Indexes (a_SchemaName VARCHAR(256), a_TableName VARCHAR(256),a_isOrderNeed BOOLEAN);
/********************************************************************************************************/
/*  Функция возвращает список индексов таблицы								*/
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admtf_Table_Indexes
			(a_SchemaName	VARCHAR(256) default 'public',/* название схемы базы данных		*/				
			a_TableName	VARCHAR(256) default NULL, 	/* Название таблицы */
			a_isOrderNeed	BOOLEAN default TRUE		/* Нужно ли добавлять признак порядка сортировки */
									/* для каждого атрибута индекса? */
)

RETURNS  TABLE (r_IndexName VARCHAR(256),r_IndexType VARCHAR(256),r_isUnique BOOLEAN,r_isPrimary BOOLEAN, r_IndexKeyNames TEXT) AS
$BODY$
DECLARE
--******************************************************************************************************		
BEGIN
	RETURN QUERY SELECT ti.r_IndexName::VARCHAR(256),ti.r_IndexType::VARCHAR(256),ti.r_isUnique::BOOLEAN,
				ti.r_isPrimary::BOOLEAN, ti.r_IndexKeyNames::TEXT
			FROM admtf_Table_Indexes(a_SchemaName::NAME,a_TableName::NAME,a_isOrderNeed) ti;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admtf_Table_Indexes(a_SchemaName VARCHAR(256), a_TableName VARCHAR(256),a_isOrderNeed BOOLEAN)
					IS 'Возвращает список индексов таблицы';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT * FROM admtf_Table_Indexes('public'::NAME,'Street'::NAME,FALSE);
SELECT * FROM admtf_Table_Indexes('public'::VARCHAR(256),'Street'::VARCHAR(256));

Создание функции admfn_Table_isAttributeExists

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_Table_isAttributeExists (a_SchemaName VARCHAR(256),a_TableName VARCHAR(256),a_AttributeName VARCHAR(256));
/********************************************************************************************************/
/*  Функция возвращает признак наличия в таблице атрибута (колонки) с заданным именем			*/
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_isAttributeExists
			(a_SchemaName	VARCHAR(256) default 'public',	/* название схемы базы данных*/
			a_TableName	VARCHAR(256) default NULL,	/* Название таблицы */
			a_AttributeName VARCHAR(256) default NULL	/* Название атрибута*/
) 
RETURNS  BOOLEAN AS
$BODY$
DECLARE	c_TableKind	CONSTANT CHAR:='r';
	c_DoubleQuote	CONSTANT CHAR:='"';
	v_TableOID		OID;			/*OID таблицы */
	v_AttributeNo		SMALLINT;		/* Порядковый номер найденного атрибута */
	v_AttributeName		VARCHAR(256);		/* Название найденного атрибута */

	v_isAttributeExists	BOOLEAN;		/* признак наличия аттрибута в таблице */
	--******************************************************************************************************		
BEGIN
	v_TableOID:=NULL;	
	IF STRPOS(a_TableName, c_DoubleQuote)>0 OR cnfn_String_IsLatin(a_TableName)<>'LatinAll' THEN
		SELECT INTO v_TableOID tbl.oid 
				FROM pg_class tbl 
		
					INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid 
				WHERE nsp.nspname=a_SchemaName AND tbl.relname=REPLACE(a_TableName,c_DoubleQuote,'') AND tbl.relkind=LOWER(c_TableKind);

	ELSE
		SELECT INTO v_TableOID tbl.oid 
				FROM pg_class tbl 
		
					INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid 
				WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName) AND tbl.relkind=LOWER(c_TableKind);
	END IF;			
	IF NOT FOUND THEN 
		v_TableOID:=NULL;		
	END IF;
	IF v_TableOID IS NULL THEN 		
		v_isAttributeExists:=NULL;
	ELSE	
		IF STRPOS(a_AttributeName, c_DoubleQuote)>0 OR cnfn_String_IsLatin(a_AttributeName)<>'LatinAll' THEN
			SELECT INTO v_AttributeName,v_AttributeNo att.attname,att.attnum FROM pg_attribute att 
					WHERE att.attrelid=v_TableOID AND att.attname=REPLACE(a_AttributeName,c_DoubleQuote,''); 
		ELSE
			SELECT INTO v_AttributeName,v_AttributeNo att.attname,att.attnum FROM pg_attribute att 
					WHERE att.attrelid=v_TableOID AND att.attname=LOWER(a_AttributeName); 
		END IF;
		IF FOUND THEN
			v_isAttributeExists:=true;
		ELSE
			v_isAttributeExists:=false;
		END IF;
	END IF;
	RETURN v_isAttributeExists;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_isAttributeExists(a_SchemaName VARCHAR(256),a_TableName VARCHAR(256),a_AttributeName VARCHAR(256)) IS 'Возвращает признак наличия в таблице атрибута (колонки) с заданным именем';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT admfn_Table_isAttributeExists('public','Street','StreetTypeAcrm'),
       admfn_Table_isAttributeExists('public','Street','StreetTypeName'),
       admfn_Table_isAttributeExists('public','StreetType','StreetTypeName');

Создание функции admfn_Table_TableNameArray

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_Table_TableNameArray (a_SchemaName VARCHAR(256),a_WHEREoption VARCHAR(256));
/********************************************************************************************************/
/*  Функция возвращает массив названий таблиц, созданный из записей запроса на выбор таблиц 		*/
/* из системного каталога, условие которого передано одним из параметров			  	*/
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_TableNameArray
			(a_SchemaName	VARCHAR(256) default 'public',	/* название схемы базы данных		*/				
			a_WHEREoption	VARCHAR(1000) default NULL	/* условие запроса на выбор таблиц из системного каталога */
			
	
 )										 

RETURNS  VARCHAR(256)[] AS
$BODY$
DECLARE	c_TableKind	CONSTANT CHAR:='r';
	c_SELECT_StatementBase CONSTANT VARCHAR(1000):='SELECT tbl.relname FROM pg_class tbl INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid 
								WHERE LOWER(nsp.nspname)=LOWER('|| quote_literal(a_SchemaName)||') AND tbl.relkind=LOWER('||quote_literal(c_TableKind)||')';
	c_ORDER_BY_OPTION CONSTANT VARCHAR(100):='ORDER BY tbl.relname';

	cursor_pg_class refcursor;

	v_TableRowCount		INTEGER;		/* Счетчик выбранных таблиц*/
	v_SELECT_Statement 	VARCHAR(1000);		/* Текст оператора SELECT для выбора таблиц*/
	v_AloneTableName 	VARCHAR(256);		/* Название одной из выбранных таблиц */
	v_TableNameArray	VARCHAR(256)[];		/* Массив выбранных таблиц  */
	v_isExpressionCorrect	BOOLEAN;		/* Признак правильности выражения WHERE  */
	v_Return_Error		INTEGER:=0;		/* Код возврата*/
	--******************************************************************************************************		
BEGIN		
	v_SELECT_Statement:=c_SELECT_StatementBase || CASE WHEN COALESCE(TRIM(a_WHEREoption),'')='' THEN ' ' ELSE ' AND ('||TRIM(a_WHEREoption)||') ' END
							|| c_ORDER_BY_OPTION || ';';
    BEGIN
        EXECUTE v_SELECT_Statement;
        v_isExpressionCorrect:=TRUE;
        IF v_Return_Error=0 AND v_SELECT_Statement IS NOT NULL AND v_isExpressionCorrect THEN							
            OPEN cursor_pg_class FOR EXECUTE v_SELECT_Statement;
            v_TableRowCount:=0;
            FETCH FIRST FROM cursor_pg_class INTO v_AloneTableName;
            WHILE FOUND
            LOOP
                v_TableRowCount:=v_TableRowCount+1;	
                v_TableNameArray[v_TableRowCount]:=CASE WHEN v_AloneTableName~ E'[A-ZА-ЯЁ]' THEN ''''||v_AloneTableName||'''' ELSE v_AloneTableName END;
                FETCH NEXT  FROM cursor_pg_class INTO v_AloneTableName;
            END LOOP;
            CLOSE cursor_pg_class;
        END IF;	
        EXCEPTION
       WHEN OTHERS THEN
            v_isExpressionCorrect:=FALSE;
            v_TableNameArray:=NULL;
            RAISE NOTICE 'ОШИБКА. Проверьте правильность ВЫРАЖЕНИЯ для WHERE. 
                          ПАРАМЕТРЫ создания: ********************************************************************
                        Схема                           - "%"
                        условие выбора                  - "%"
                        Системный код (SQLSTATE)        - "%"
                        Системное сообщение (SQLERRM)   - "%"
                                            **********************************************************************',
                                           a_SchemaName,a_WHEREoption,SQLSTATE,SQLERRM;

    END;

	RETURN v_TableNameArray;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_TableNameArray(a_SchemaName VARCHAR(256),a_WHEREoption VARCHAR(1000)) IS 'Возвращает массив названий таблиц, созданный из записей запроса';

--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT * FROM unnest(admfn_Table_TableNameArray('public','tbl.relname ~* E''^Street'''));

Создание функции admfn_Table_LinkSign

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_Table_LinkSign (a_SchemaName NAME,a_TableName NAME,a_FKConstrantName NAME);
/************************************************************************************************************************/
/*  Функция возвращает Возвращает признак связи между таблицами ('N:1','1:1'), соответствующей ограничению Foreign Key	*/
/************************************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_LinkSign
			(a_SchemaName		NAME default 'public',	/* название схемы базы данных		*/				
			a_TableName		NAME default NULL,	/* Название таблицы */
			a_FKConstrantName	NAME default NULL	/* Название ограничения ForeignKey */
)
RETURNS  VARCHAR(5) AS
$BODY$
DECLARE	c_ManySign		CONSTANT CHAR:='N';
	c_OneSign		CONSTANT CHAR:='1';
	c_DelimiterSign		CONSTANT CHAR:=':';
	c_ReferencedRole	CONSTANT VARCHAR(20):='Referenced';	/* Обозначение таблицы, на которую ссылается 'внешний ключ */
	c_ConstrainedRole	CONSTANT VARCHAR(20):='Constrained';	/* обозначение  таблицы, которая содержат ограничение 'внешний ключ' */
	v_ForeignKeyRole		VARCHAR(20);			/* РОль таблицы в ограничении 'внешний ключ' */
	v_CurrSign		CHAR;				/* Признак таблицы в текущей связи */
	v_LinkSign		VARCHAR(5);			/* Признак связи по отношения к таклице*/
	v_ConstrainedTableName	NAME;			/* Название таблицы, которая содержат ограничение 'внешний ключ' */
	--******************************************************************************************************		
BEGIN		
	v_ForeignKeyRole	:=admfn_Table_ForeignKeyRole(a_SchemaName,a_TableName,a_FKConstrantName);
	IF v_ForeignKeyRole=c_ConstrainedRole THEN
		v_CurrSign:=admfn_Table_ConstrainedLinkSign(a_SchemaName,a_TableName,a_FKConstrantName);
		v_LinkSign:=v_CurrSign||c_DelimiterSign||c_OneSign;		
	ELSIF v_ForeignKeyRole=c_ReferencedRole THEN
		v_ConstrainedTableName:=admfn_ForeignKey_ConstrainedTableName(a_SchemaName,a_FKConstrantName);
		v_CurrSign:=admfn_Table_ConstrainedLinkSign(a_SchemaName,v_ConstrainedTableName,a_FKConstrantName);
		v_LinkSign:=c_OneSign||c_DelimiterSign||v_CurrSign;
	ELSE
		v_CurrSign:=NULL;
	END IF;	
	RETURN v_LinkSign;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_LinkSign(a_SchemaName NAME,a_TableName NAME,a_FKConstrantName NAME) IS 'Возвращает признак связи между таблицами (''N:1'',''1:1''), соответствующей ограничению Foreign Key  ';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_Table_LinkSign (a_SchemaName VARCHAR(256),a_TableName VARCHAR(256),a_FKConstrantName VARCHAR(256));
/********************************************************************************************************/
/*  Функция возвращает Возвращает признак связи между таблицами ('N:1','1:1'), соответствующей ограничению Foreign Key	*/
/************************************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_LinkSign
			(a_SchemaName		VARCHAR(256) default 'public',	/* название схемы базы данных		*/				
			a_TableName		VARCHAR(256) default NULL,	/* Название таблицы */
			a_FKConstrantName	VARCHAR(256) default NULL	/* Название ограничения ForeignKey */
			
	
 )										 

RETURNS  VARCHAR(5) AS
$BODY$
DECLARE	v_LinkSign		VARCHAR(5);			/* Признак связи по отношения к таклице*/
	--******************************************************************************************************		
BEGIN		
	v_LinkSign:=admfn_Table_LinkSign(a_SchemaName::NAME,a_TableName::NAME,a_FKConstrantName::NAME);
	RETURN v_LinkSign;

END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_LinkSign(a_SchemaName VARCHAR(256),a_TableName VARCHAR(256),a_FKConstrantName VARCHAR(256)) IS 'Возвращает признак связи между таблицами (''N:1'',''1:1''), соответствующей ограничению Foreign Key  ';
COMMIT TRANSACTION;
--ROLLBACK TRANSACTION;
SELECT admfn_Table_LinkSign('public'::name,'Street'::name,'fk_street_StreetType'::name);--N:1
SELECT admfn_Table_LinkSign('public'::name,'Street'::name,'fk_streetsynonym_street'::name); --1:N
SELECT admfn_Table_LinkSign('public'::name,'StreetSynonym'::name,'fk_streetsynonym_street'::name); --N:1

Создание функции admfn_Table_ForeignKeyRole

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_Table_ForeignKeyRole(a_SchemaName name, a_TableName name,a_FKConstrantName name);
/********************************************************************************************************/
/*  Функция возвращает признак роли (места) таблицы в ограничении ForeignKey:							*/
/*  Referenced  - таблица, на которую ссылается 'внешний ключ'                                          */
/*  Constrained - таблица, которая ссылается на другаю таблицу через 'внешний ключ'                     */
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_ForeignKeyRole
			(a_SchemaName		name default 'public',	/* название схемы базы данных		*/				
			a_TableName		name default NULL,	/* Название таблицы */
			a_FKConstrantName	name default NULL	/* Название ограничения ForeignKey */
	
 )										 

RETURNS VARCHAR(10) AS
$BODY$
DECLARE
	c_ForeignTypeType	CONSTANT VARCHAR(2):='f';	/* Буквенное обозначение типа ограничения 'внешний ключ' */
	c_ReferencedRole	CONSTANT VARCHAR(20):='Referenced';	/* Обозначение таблицы, на которую ссылается 'внешний ключ' */
	c_ConstrainedRole	CONSTANT VARCHAR(20):='Constrained';	/* обозначение  таблицы, которая содержат ограничение 'внешний ключ' */
	v_ForeignKeyRole		VARCHAR(20);				/* РОль таблицы в ограничении 'внешний ключ' */
	--******************************************************************************************************		
BEGIN		
	v_ForeignKeyRole:=NULL;		
	SELECT INTO v_ForeignKeyRole CASE WHEN (SELECT tbl.relname FROM pg_class tbl WHERE con.conrelid=tbl.oid) =LOWER(a_TableName)
						THEN c_ConstrainedRole 
					WHEN (SELECT reftbl.relname FROM pg_class reftbl WHERE con.confrelid=reftbl.oid) =LOWER(a_TableName)
						THEN c_ReferencedRole
					ELSE 
						NULL
				END		
			FROM pg_constraint con INNER JOIN pg_namespace nsp ON con.connamespace=nsp.oid 
			WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(con.conname)=LOWER(a_FKConstrantName)
				AND con.contype=c_ForeignTypeType;
	RETURN v_ForeignKeyRole;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_ForeignKeyRole(a_SchemaName name, a_TableName name,a_FKConstrantName name) 
            IS 'Возвращает признак роли (места) таблицы в ограничении ForeignKey: Referenced  - таблица, на которую ссылается "внешний ключ"; Constrained - таблица, которая ссылается на другаю таблицу через "внешний ключ"';

--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_Table_ForeignKeyRole(a_SchemaName VARCHAR(256), a_TableName VARCHAR(256),a_FKConstrantName VARCHAR(256));
/********************************************************************************************************/
/*  Функция возвращает признак роли (места) таблицы в ограничении ForeignKey:							*/
/*  Referenced  - таблица, на которую ссылается 'внешний ключ'                                          */
/*  Constrained - таблица, которая ссылается на другаю таблицу через 'внешний ключ'                     */
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_ForeignKeyRole
			(a_SchemaName	VARCHAR(256) default 'public',	/* название схемы базы данных */
			a_TableName	VARCHAR(256) default NULL,	/* Название таблицы */
			a_FKConstrantName	VARCHAR(256) default NULL	/* Название ограничения ForeignKey */
	
 )										 

RETURNS   VARCHAR(10) AS
$BODY$
DECLARE
	c_ForeignTypeType	CONSTANT VARCHAR(2):='f';	/* Буквенное обозначение типа ограничения 'внешний ключ ключ' */
	c_ReferencedRole	CONSTANT VARCHAR(20):='Referenced';	/* Обозначение таблицы, на которую ссылается 'внешний ключ */
	c_ConstrainedRole	CONSTANT VARCHAR(20):='Constrained';	/* обозначение  таблицы, которая содержат ограничение 'внешний ключ' */
	v_ForeignKeyRole		VARCHAR(20);				/* РОль таблицы в ограничении 'внешний ключ' */
	--******************************************************************************************************		
BEGIN		
	v_ForeignKeyRole:=admfn_Table_ForeignKeyRole(a_SchemaName::NAME,a_TableName::NAME,a_FKConstrantName::NAME);
	RETURN v_ForeignKeyRole;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_ForeignKeyRole(a_SchemaName VARCHAR(256), a_TableName VARCHAR(256),a_FKConstrantName VARCHAR(256)) 
            IS 'Возвращает признак роли (места) таблицы в ограничении ForeignKey: Referenced  - таблица, на которую ссылается "внешний ключ"; Constrained - таблица, которая ссылается на другаю таблицу через "внешний ключ"';


--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;

SELECT admfn_Table_ForeignKeyRole('public'::name,'Street'::name,'fk_StreetSynonym_Street'::name);
SELECT admfn_Table_ForeignKeyRole('public'::name,'StreetSynonym'::name,'fk_StreetSynonym_Street'::name);

Создание функции admfn_Table_ConstrainedLinkSign

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_Table_ConstrainedLinkSign (a_SchemaName NAME,a_TableName NAME,a_FKConstrantName NAME);
/********************************************************************************************************************/
/*  Функция возвращает признак количества записей, участвующих в связи, соответствующей ограничению Foreign Key,    */ 
/*  со стороны таблицы: "N" - много,"1"- одна.                                                                      */
/*------------------------------------------------------------------------------------------------------------------*/
/* admfn_Table_ConstrainedLinkSign('public'::VARCHAR,'Street'::VARCHAR,'fk_StreetSynonym_Street'::VARCHAR);         */
/*------------------------------------------------------------------------------------------------------------------*/
/*  "1"                                                                                                             */
/*------------------------------------------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------------------------------------------*/
/* admfn_Table_ConstrainedLinkSign('public'::VARCHAR,'StreetSynonym'::VARCHAR,'fk_StreetSynonym_Street'::VARCHAR);  */
/*------------------------------------------------------------------------------------------------------------------*/
/*  "N"                                                                                                             */
/********************************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_ConstrainedLinkSign
			(a_SchemaName		NAME default 'public',	/* название схемы базы данных		*/				
			a_TableName	NAME default NULL,	/* Название таблицы */
			a_FKConstrantName	NAME default NULL	/* Название ограничения ForeignKey */
			
	
 )										 

RETURNS  VARCHAR(1) AS
$BODY$
DECLARE	c_ManySign		CONSTANT CHAR:='N';
	c_OneSign		    CONSTANT CHAR:='1';
	c_DelimiterSign		CONSTANT CHAR:=':';
	c_DelimiterKeys		CONSTANT VARCHAR(2):=',';
	c_ConstraintType	CONSTANT VARCHAR(2):='u';	/* Буквенное обозначение типа ограничения 'уникальный ключ' */
	c_ForeignTypeType	CONSTANT VARCHAR(2):='f';	/* Буквенное обозначение типа ограничения 'внешний ключ ключ' */
	c_ReferencedRole	CONSTANT VARCHAR(20):='Referenced';	/* Обозначение таблицы, на которую ссылается 'внешний ключ */
	c_ConstrainedRole	CONSTANT VARCHAR(20):='Constrained';	/* обозначение  таблицы, которая содержат ограничение 'внешний ключ' */
	c_withoutOrder      CONSTANT BOOLEAN:=FALSE; /* Не добавлять признак порядка сортировки для каждого атрибута индекса */
	v_ForeignKeyRole		VARCHAR(20);			/* РОль таблицы в ограничении 'внешний ключ' */
	v_UniqueIndex		RECORD;				/* Запись об уникальном индексе таблицы*/
	v_UniqueConstraint	RECORD;				/* Запись об уникальном  уникальном ключе таблицы*/
	v_IndexKeyNames		TEXT;			/* Строка со списком атрибутов индекса*/
	v_CurrSign		CHAR;				/* Признак таблицы в текущей связи */
	v_TablePK		NAME[];			/* Массив Названий атрибутов, составляющих внешний ключ */
	v_TableFK		NAME[];			/* Массив Названий атрибутов, составляющих внешний ключ */
	v_TableIK		NAME[];			/* Массив Названий атрибутов, в составе индекса */
	v_TableUK		NAME[];			/* Массив Названий атрибутов, составляющих уникальный ключ (уникальное значение) */
	v_IndexName		NAME;			/* Название индекса */
	v_ConstraintName	NAME;			/* Название ограничения */
	v_ConstraintType	VARCHAR(256);			/* Буквенное обозначение типа ограничения */
	v_LinkSign		VARCHAR(5);			/* Признак связи по отношения к таклице*/
    v_haveUniqueIndexes	BOOLEAN;		/* признак наличия уникальных индексов в описании таблицы */
	--******************************************************************************************************		
BEGIN		
	v_CurrSign:=c_ManySign;
	v_TableFK:=admfn_Table_TableFK(a_SchemaName,a_TableName,a_FKConstrantName);
	v_TablePK:=admfn_Table_TablePK(a_SchemaName,a_TableName);
 	v_haveUniqueIndexes:=admfn_Table_haveUniqueIndexes(a_SchemaName,a_TableName);
	IF v_TableFK=v_TablePK THEN /* CONSTRAINT в составе таблицы a_TableName и атрибуты первичного ключа совпадают с атрибутами внешнего*/
		v_CurrSign:=c_OneSign;
	ELSIF COALESCE(v_TableFK,ARRAY[NULL::NAME])=ARRAY[NULL::NAME] THEN /* CONSTRAINT ссылается на таблицу a_TableName*/
		v_ForeignKeyRole:=admfn_Table_ForeignKeyRole(a_SchemaName,a_TableName,a_FKConstrantName);
		v_CurrSign:=CASE WHEN v_ForeignKeyRole = c_ReferencedRole THEN c_OneSign ELSE NULL END;
	ELSIF v_haveUniqueIndexes THEN /* проверка совпадения списка атрибутов внешнего ключа и уникального индекса*/                                               
		FOR v_UniqueConstraint IN SELECT * FROM  admtf_Table_UniqueIndexes(a_SchemaName,a_TableName,c_withoutOrder)
						WHERE NOT r_isPrimary ORDER BY r_IndexName LOOP
			v_IndexName:=v_UniqueConstraint.r_IndexName;
			v_IndexKeyNames=v_UniqueConstraint.r_IndexKeyNames;
			v_TableIK:=string_to_array(v_IndexKeyNames, c_DelimiterKeys);
 			IF v_TableFK=v_TableIK THEN
				v_CurrSign:=c_OneSign;
			END IF;
		END LOOP;
	ELSE /* проверка совпадения списка атрибутов внешнего ключа и уникального CONSTRAINT UNIQUE*/  
		FOR v_UniqueConstraint IN SELECT * FROM admtf_Table_Constraintes(a_SchemaName,a_TableName)
						WHERE r_ConstraintType=c_ConstraintType ORDER BY r_ConstraintName 
		LOOP
			v_ConstraintName:=v_UniqueConstraint.r_ConstraintName;
			v_TableUK:=string_to_array(v_UniqueConstraint.r_ConstraintKeyNames, c_DelimiterKeys);
			IF v_TableFK=v_TableUK THEN
				v_CurrSign:=c_OneSign;
			END IF;
		END LOOP;
	END IF;
	RETURN v_CurrSign;

END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_ConstrainedLinkSign(a_SchemaName NAME,a_TableName NAME,a_FKConstrantName NAME) 
IS 'Возвращает признак количества записей, участвующих в связи, соответствующей ограничению Foreign Key,со стороны таблицы: "N" - много,"1"- одна.';

COMMIT TRANSACTION;
--ROLLBACK TRANSACTION;
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_Table_ConstrainedLinkSign (a_SchemaName VARCHAR(256),a_TableName VARCHAR(256),a_FKConstrantName VARCHAR(256));
/********************************************************************************************************************/
/*  Функция возвращает признак количества записей, участвующих в связи, соответствующей ограничению Foreign Key,    */ 
/*  со стороны таблицы: "N" - много,"1"- одна.                                                                      */
/*------------------------------------------------------------------------------------------------------------------*/
/* admfn_Table_ConstrainedLinkSign('public'::VARCHAR,'Street'::VARCHAR,'fk_StreetSynonym_Street'::VARCHAR);         */
/*------------------------------------------------------------------------------------------------------------------*/
/*  "1"                                                                                                             */
/*------------------------------------------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------------------------------------------*/
/* admfn_Table_ConstrainedLinkSign('public'::VARCHAR,'StreetSynonym'::VARCHAR,'fk_StreetSynonym_Street'::VARCHAR);  */
/*------------------------------------------------------------------------------------------------------------------*/
/*  "N"                                                                                                             */
/********************************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_ConstrainedLinkSign
			(a_SchemaName		VARCHAR(256) default 'public',/* название схемы базы данных	*/
			a_TableName		VARCHAR(256) default NULL,	/* Название таблицы */
			a_FKConstrantName	VARCHAR(256) default NULL	/* Название ограничения ForeignKey */
)

RETURNS  VARCHAR(5) AS
$BODY$
DECLARE	v_CurrSign		CHAR; 	 /* Признак таблицы в текущей связи */
	--******************************************************************************************************		
BEGIN		
	v_CurrSign:=admfn_Table_ConstrainedLinkSign(a_SchemaName::NAME,a_TableName::NAME,a_FKConstrantName::NAME);
	RETURN v_CurrSign;

END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_ConstrainedLinkSign(a_SchemaName VARCHAR(256),a_TableName VARCHAR(256),a_FKConstrantName VARCHAR(256)) 
IS 'Возвращает признак количества записей, участвующих в связи, соответствующей ограничению Foreign Key,со стороны таблицы: "N" - много,"1"- одна.';
COMMIT TRANSACTION;
--ROLLBACK TRANSACTION;
SELECT admfn_Table_ConstrainedLinkSign('public'::VARCHAR,'Street'::VARCHAR,'fk_StreetSynonym_Street'::VARCHAR);--"1"
SELECT admfn_Table_ConstrainedLinkSign('public'::VARCHAR,'StreetSynonym'::VARCHAR,'fk_StreetSynonym_Street'::VARCHAR);--"N"

Создание функции admfn_ForeignKey_ConstrainedTableName

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_ForeignKey_ConstrainedTableName (a_SchemaName name,a_FKConstrantName name);
/*******************************************************************************************************/
/*  Функция возвращает название таблицы, в которой объявлен внешний ключ					*/
/*******************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_ForeignKey_ConstrainedTableName
				(a_SchemaName		name default 'public',	/* название схемы базы данных		*/
				a_FKConstrantName	name default NULL	/* Название внешнего ключа таблицы */
 )
RETURNS  name AS
$BODY$
DECLARE	
	v_ConstrainedTableName	name;	/* Наименование таблицы, на которую ссылается внешний ключ */
	--******************************************************************************************************		
BEGIN		
	 SELECT INTO v_ConstrainedTableName
				tbl.relname
		FROM pg_constraint con 
			INNER JOIN pg_namespace nspc ON con.connamespace = nspc.oid 
			INNER JOIN pg_class tbl ON con.conrelid=tbl.oid

							
		WHERE nspc.nspname=LOWER(a_SchemaName) AND con.contype =c_ForeignKeyKind
			AND con.conname =LOWER(a_FKConstrantName);
		
	RETURN v_ConstrainedTableName;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_ForeignKey_ConstrainedTableName(a_SchemaName name,a_FKConstrantName name) IS 'Возвращает название таблицы, в которой объявлен внешний ключ';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_ForeignKey_ConstrainedTableName (a_SchemaName VARCHAR(256),a_FKConstrantName VARCHAR(256));
/********************************************************************************************************/
/*  Функция возвращает название таблицы, в которой объявлен внешний ключ	*/
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_ForeignKey_ConstrainedTableName
				(a_SchemaName		VARCHAR(256) default 'public',	/* название схемы базы данных		*/
				a_FKConstrantName	VARCHAR(256) default NULL	/* Название внешнего ключа таблицы */
 )										 

RETURNS  VARCHAR(256) AS
$BODY$
DECLARE	
	c_ForeignKeyKind	CONSTANT CHAR:='f';
	v_ConstrainedTableName	VARCHAR(256);	/* Наименование таблицы, на которую ссылается внешний ключ */
	--******************************************************************************************************		
BEGIN		
	 v_ConstrainedTableName:=admfn_ForeignKey_ConstrainedTableName(a_SchemaName::NAME,a_FKConstrantName::NAME);
	RETURN v_ConstrainedTableName;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_ForeignKey_ConstrainedTableName(a_SchemaName VARCHAR(256),a_FKConstrantName VARCHAR(256)) IS 'Возвращает название таблицы, в которой объявлен внешний ключ';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT * FROM admfn_ForeignKey_ConstrainedTableName('public'::VARCHAR(256),'fk_StreetSynonym_Street'::VARCHAR(256));
SELECT * FROM admfn_ForeignKey_ConstrainedTableName('public'::VARCHAR(256),'fk_StreetTypeSynonym_StreetType'::VARCHAR(256));
SELECT * FROM admfn_ForeignKey_ConstrainedTableName('public'::NAME,'fk_StreetSynonym_Street'::NAME);
SELECT * FROM admfn_ForeignKey_ConstrainedTableName('public'::NAME,'fk_StreetTypeSynonym_StreetType'::NAME);

Создание функции admfn_Table_TablePK

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_Table_TablePK (a_SchemaName VARCHAR(256),a_TableName VARCHAR(256));
/********************************************************************************************************/
/*  Функция возвращает массив названий атрибутов, составляющих первичный ключ				*/
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_TablePK
			(a_SchemaName	VARCHAR(256) default 'public',	/* название схемы базы данных	 */
			a_TableName	VARCHAR(256) default NULL	/* Название таблицы */
)

RETURNS  VARCHAR(256)[] AS
$BODY$
DECLARE	c_TableKind	CONSTANT CHAR:='r';
	c_PrimaryKeyKind	CONSTANT CHAR:='p';
	c_PKAttributeList_NDims CONSTANT INTEGER:=1;	/* Размерность массива атрибутов первичного ключа*/
	v_TableOID		OID;			/*OID таблицы */
	v_PKAttributeList	SMALLINT[];	/* Список ИД атрибутов, составляющих первичный ключ */
	v_PKAttributeID		SMALLINT;	/* ИД атрибута, первичного ключа */
	v_PKAttributeNo		SMALLINT;	/* Порядковый номер атрибута, первичного ключа */
	v_PKAttributeName	VARCHAR(256);	/* Название атрибута, составляющих первичный ключ */

	v_TablePK	VARCHAR(256)[];			/* Названий атрибутов, составляющих первичный ключ */
	--******************************************************************************************************		
BEGIN		
	SELECT INTO v_TableOID tbl.oid FROM pg_class tbl 
				INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid 
			WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName)
				AND tbl.relkind=LOWER(c_TableKind); 
	SELECT INTO v_PKAttributeList conkey FROM pg_constraint c WHERE c.contype=c_PrimaryKeyKind and c.conrelid=v_TableOID;
	IF FOUND  AND v_PKAttributeList IS NOT NULL THEN
		v_PKAttributeNo:=array_lower(v_PKAttributeList,c_PKAttributeList_NDims);
		FOREACH v_PKAttributeID	 IN ARRAY v_PKAttributeList
		LOOP
			SELECT INTO v_PKAttributeName att.attname FROM pg_attribute att 
									WHERE att.attrelid=v_TableOID AND att.attnum=v_PKAttributeID;
			v_TablePK[v_PKAttributeNo]:=v_PKAttributeName;
			v_PKAttributeNo := v_PKAttributeNo + 1;
		END LOOP;
	END IF;

	RETURN v_TablePK;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_TablePK(a_SchemaName VARCHAR(256),a_TableName VARCHAR(256)) IS 'Возвращает массив названий атрибутов, составляющих первичный ключ';
DROP FUNCTION IF EXISTS admfn_Table_TablePK (a_SchemaName name,a_TableName name);
/********************************************************************************************************/
/*  Функция возвращает массив названий атрибутов, составляющих первичный ключ				*/
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_TablePK
			(a_SchemaName	name default 'public',	/* название схемы базы данных		*/				
			a_TableName	name default NULL	/* Название таблицы */
			
	
 )										 

RETURNS  name[] AS
$BODY$
DECLARE	c_TableKind	CONSTANT CHAR:='r';
	c_PrimaryKeyKind	CONSTANT CHAR:='p';
	c_PKAttributeList_NDims CONSTANT INTEGER:=1;	/* Размерность массива атрибутов первичного ключа*/
	v_TableOID	OID;			/*OID таблицы */
	v_PKAttributeList	SMALLINT[];	/* Список ИД атрибутов, составляющих первичный ключ */
	v_PKAttributeID		SMALLINT;	/* ИД атрибута, первичного ключа */
	v_PKAttributeNo		SMALLINT;	/* Порядковый номер атрибута, первичного ключа */
	v_PKAttributeName	name;		/* Название атрибута, составляющих первичный ключ */

	v_TablePK	name[];			/* Названий атрибутов, составляющих первичный ключ */
	--******************************************************************************************************		
BEGIN		
	SELECT INTO v_TableOID tbl.oid FROM pg_class tbl 
				INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid 
			WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName)
				AND tbl.relkind=LOWER(c_TableKind); 
	SELECT INTO v_PKAttributeList conkey FROM pg_constraint c WHERE c.contype=c_PrimaryKeyKind and c.conrelid=v_TableOID;
	IF FOUND AND v_PKAttributeList IS NOT NULL THEN
		v_PKAttributeNo:=array_lower(v_PKAttributeList,c_PKAttributeList_NDims);
		FOREACH v_PKAttributeID	 IN ARRAY v_PKAttributeList
		LOOP
			SELECT INTO v_PKAttributeName att.attname FROM pg_attribute att WHERE att.attrelid=v_TableOID
										AND att.attnum=v_PKAttributeID;
			v_TablePK[v_PKAttributeNo]:=v_PKAttributeName;
			v_PKAttributeNo := v_PKAttributeNo + 1;
		END LOOP;
	END IF;

	RETURN v_TablePK;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_TablePK(a_SchemaName name,a_TableName name) IS 'Возвращает массив названий атрибутов, составляющих первичный ключ';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT admfn_Table_TablePK('public'::name,'Street'::name);
SELECT admfn_Table_TablePK('public':: VARCHAR,'Street'::VARCHAR);

Создание функции admfn_Table_TableFK

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_Table_TableFK (a_SchemaName VARCHAR(256),a_TableName VARCHAR(256),a_FKConstrantName VARCHAR(256));
/********************************************************************************************************/
/*  Функция возвращает массив названий атрибутов таблицы, составляющих внешний ключ			*/
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_TableFK
			(a_SchemaName		VARCHAR(256) default 'public',	/* название схемы базы данных*/
			a_TableName		VARCHAR(256) default NULL,	/* Название таблицы */
			a_FKConstrantName	VARCHAR(256) default NULL	/* Название ограничения ForeignKey */
)

RETURNS  VARCHAR(256)[] AS
$BODY$
DECLARE	c_TableKind	CONSTANT CHAR:='r';
	c_ForeignKeyKind	CONSTANT CHAR:='f';
	c_FKAttributeList_NDims CONSTANT INTEGER:=1;	/* Размерность массива атрибутов внешнего ключа*/
	v_TableOID		OID;			/*OID таблицы */
	v_FKAttributeList	SMALLINT[];	/* Список ИД атрибутов, составляющих внешний ключ */
	v_FKAttributeID		SMALLINT;	/* ИД атрибута, внешнего ключа */
	v_FKAttributeNo		SMALLINT;	/* Порядковый номер атрибута, внешнего ключа */
	v_FKAttributeName	VARCHAR(256);	/* Название атрибута, составляющих внешний ключ */

	v_TableFK	VARCHAR(256)[];			/* Названий атрибутов, составляющих внешний ключ */
	--******************************************************************************************************		
BEGIN
	SELECT INTO v_TableOID tbl.oid FROM pg_class tbl 
				INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid 
			WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName)
				AND tbl.relkind=LOWER(c_TableKind); 
	SELECT INTO v_FKAttributeList conkey FROM pg_constraint c WHERE c.contype=c_ForeignKeyKind and c.conrelid=v_TableOID
										AND c.conname=LOWER(TRIM(a_FKConstrantName));
	IF FOUND  AND v_FKAttributeList IS NOT NULL THEN
		v_FKAttributeNo:=array_lower(v_FKAttributeList,c_FKAttributeList_NDims);
		FOREACH v_FKAttributeID	 IN ARRAY v_FKAttributeList
		LOOP
			SELECT INTO v_FKAttributeName att.attname FROM pg_attribute att WHERE att.attrelid=v_TableOID
													AND att.attnum=v_FKAttributeID;
			v_TableFK[v_FKAttributeNo]:=v_FKAttributeName;
			v_FKAttributeNo := v_FKAttributeNo + 1;
		END LOOP;
	END IF;

	RETURN v_TableFK;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_TableFK(a_SchemaName VARCHAR(256),a_TableName VARCHAR(256),a_FKConstrantName VARCHAR(256)) IS 'Возвращает массив названий атрибутов таблицы, составляющих внешний ключ';
DROP FUNCTION IF EXISTS admfn_Table_TableFK (a_SchemaName name,a_TableName name,a_FKConstrantName name);
/********************************************************************************************************/
/*  Функция возвращает массив названий атрибутов таблицы, составляющих внешний ключ			*/
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_TableFK
			(a_SchemaName		name default 'public',	/* название схемы базы данных*/
			a_TableName		name default NULL,	/* Название таблицы */
			a_FKConstrantName	name default NULL	/* Название ограничения ForeignKey */
) 

RETURNS  name[] AS
$BODY$
DECLARE	c_TableKind	CONSTANT CHAR:='r';
	c_ForeignKeyKind	CONSTANT CHAR:='f';
	c_FKAttributeList_NDims CONSTANT INTEGER:=1;	/* Размерность массива атрибутов внешнего ключа*/
	v_TableOID	OID;			/*OID таблицы */
	v_FKAttributeList	SMALLINT[];	/* Список ИД атрибутов, составляющих внешний ключ */
	v_FKAttributeID		SMALLINT;	/* ИД атрибута, внешнего ключа */
	v_FKAttributeNo		SMALLINT;	/* Порядковый номер атрибута, внешнего ключа */
	v_FKAttributeName	name;		/* Название атрибута, составляющих внешний ключ */
	v_TableFK	name[];			/* Названий атрибутов, составляющих внешний ключ */
	--******************************************************************************************************
BEGIN
	SELECT INTO v_TableOID tbl.oid FROM pg_class tbl 
				INNER JOIN pg_namespace nsp ON tbl.relnamespace=nsp.oid 
			WHERE LOWER(nsp.nspname)=LOWER(a_SchemaName) AND LOWER(tbl.relname)=LOWER(a_TableName)
				AND tbl.relkind=LOWER(c_TableKind); 
	SELECT INTO v_FKAttributeList conkey FROM pg_constraint c WHERE c.contype=c_ForeignKeyKind and c.conrelid=v_TableOID
										AND c.conname=LOWER(TRIM(a_FKConstrantName));
	IF FOUND AND v_FKAttributeList IS NOT NULL THEN
		v_FKAttributeNo:=array_lower(v_FKAttributeList,c_FKAttributeList_NDims);
		FOREACH v_FKAttributeID	 IN ARRAY v_FKAttributeList
		LOOP
			SELECT INTO v_FKAttributeName att.attname FROM pg_attribute att WHERE att.attrelid=v_TableOID
													AND att.attnum=v_FKAttributeID;
			v_TableFK[v_FKAttributeNo]:=v_FKAttributeName;
			v_FKAttributeNo := v_FKAttributeNo + 1;
		END LOOP;
	END IF;
	RETURN v_TableFK;
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_TableFK(a_SchemaName name,a_TableName name,a_FKConstrantName name) IS 'Возвращает массив названий атрибутов таблицы, составляющих внешний ключ';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT admfn_Table_TableFK('public'::name,'StreetSynonym'::name,'fk_streetsynonym_street'::name);--'{wcrccode,localityid,streetid}'

Создание функции admfn_Table_haveUniqueIndexes

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_Table_haveUniqueIndexes (a_SchemaName NAME,a_TableName NAME,a_isPrimaryNeed BOOLEAN);
/********************************************************************************************************/
/*  Функция возвращает признак наличия уникальных индексов в описании таблицы					*/
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_haveUniqueIndexes
			(a_SchemaName   NAME default 'public',	/* название схемы базы данных		*/
			a_TableName     NAME default NULL, 		/* Название таблицы */
			a_isPrimaryNeed BOOLEAN default FALSE	/* Нужно ли учитывать уникальный индекс на основе первичного ключа*/
									/*PRIMARY KEY? */
) 

RETURNS  BOOLEAN AS
$BODY$
DECLARE
	c_SchemaName    CONSTANT NAME:='public';	/* название схемы базы данных		*/				
	c_isPrimaryNeed CONSTANT BOOLEAN:=FALSE;	/* Не нажно учитывать уникальный индекс на основе первичного ключа PRIMARY KEY */
	v_haveUniqueIndexes	BOOLEAN:=FALSE;	/* признак наличия уникальных индексов в описании таблицы */
	v_SchemaName		NAME; 			/* название схемы базы данных		*/			
	v_isPrimaryNeed	BOOLEAN;		/* Нужно ли учитывать уникальный индекс на основе первичного ключа PRIMARY KEY? */
	v_isINDEXExists	BOOLEAN;		/* Существуют ли  интедсксы в таблице*/
	--******************************************************************************************************		
BEGIN
	v_SchemaName:=COALESCE(a_SchemaName,c_SchemaName);
	v_isPrimaryNeed:=COALESCE(a_isPrimaryNeed,c_isPrimaryNeed);
	SELECT INTO v_haveUniqueIndexes inx.indisunique
		FROM pg_index inx 
		WHERE inx.indrelid=(TRIM(LOWER(a_SchemaName))||'.'||TRIM(LOWER(a_TableName)))::regclass AND inx.indisunique
			AND (inx.indisprimary=FALSE OR inx.indisprimary = v_isPrimaryNeed)  ;
	RETURN COALESCE(v_haveUniqueIndexes,FALSE);
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_haveUniqueIndexes(a_SchemaName NAME,a_TableName NAME,a_isPrimaryNeed BOOLEAN) 
                                    IS 'Функция возвращает признак наличия уникальных индексов в описании таблицы';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS admfn_Table_haveUniqueIndexes (a_SchemaName VARCHAR(256),a_TableName VARCHAR(256),a_isPrimaryNeed BOOLEAN);
/********************************************************************************************************/
/*  Функция возвращает признак наличия уникальных индексов в описании таблицы					*/
/********************************************************************************************************/
CREATE OR REPLACE FUNCTION admfn_Table_haveUniqueIndexes
			(a_SchemaName   VARCHAR(256) default 'public',	/* название схемы базы данных */
			a_TableName     VARCHAR(256) default NULL, 		/* Название таблицы */
			a_isPrimaryNeed BOOLEAN default FALSE		/* Нужно ли учитывать уникальный индекс на основе */
										/* первичного ключа PRIMARY KEY? */
) 

RETURNS  BOOLEAN AS
$BODY$
DECLARE
	v_haveUniqueIndexes	BOOLEAN:=FALSE;		/* признак наличия уникальных индексов в описании таблицы */
	--******************************************************************************************************		
BEGIN
	v_haveUniqueIndexes:=admfn_Table_haveUniqueIndexes(a_SchemaName::NAME,a_TableName::NAME,a_isPrimaryNeed);
	RETURN COALESCE(v_haveUniqueIndexes,FALSE);
END
$BODY$
LANGUAGE plpgsql;
COMMENT ON FUNCTION admfn_Table_haveUniqueIndexes(a_SchemaName VARCHAR(256),a_TableName VARCHAR(256),a_isPrimaryNeed BOOLEAN) 
				IS 'Функция возвращает признак наличия уникальных индексов в описании таблицы';
--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT admfn_Table_haveUniqueIndexes('public'::VARCHAR(256),'Street'::VARCHAR(256)) Street;

Создание функции admtf_Table_UniqueIndexes

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст

Создание функции cnfn_Array_LowerCase

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS cnfn_Array_LowerCase(a_SourceArray VARCHAR(8000)[]);
/************************************************************************************************************************/
/* Возвращает массив строк из строчных букв										*/
/************************************************************************************************************************/
CREATE OR REPLACE FUNCTION cnfn_Array_LowerCase(
				a_SourceArray VARCHAR(8000)[] /* Исходный массив */
			)

RETURNS   VARCHAR(8000)[] AS
$BODY$
DECLARE
	v_LowerCaseArray VARCHAR(8000)[]; 	/* Массив, с неповторяющимися элементами */
	v_CurrentElement VARCHAR(8000); 		/* Текущий элемент массива */
	v_ElementAsArray  VARCHAR(8000)[1]; 	/* Текущий элемент, как массив */
	
--******************************************************************************************************       
--******************************************************************************************************
 BEGIN
     --**************************************************************************************** 
	v_LowerCaseArray:='{}';
    IF COALESCE(a_SourceArray,'{}')<>'{}' THEN
        FOREACH v_CurrentElement  IN ARRAY a_SourceArray LOOP
            v_ElementAsArray:='{}';
            v_CurrentElement:=LOWER(v_CurrentElement);
            v_ElementAsArray:=array_append(v_ElementAsArray,v_CurrentElement);
            IF NOT (v_ElementAsArray <@ v_LowerCaseArray) THEN
                v_LowerCaseArray:=array_append(v_LowerCaseArray,v_CurrentElement);
            END IF;
        END LOOP;
    END IF;
 	RETURN v_LowerCaseArray;
  END;
  $BODY$
 LANGUAGE plpgsql;
COMMENT ON FUNCTION cnfn_Array_LowerCase(a_SourceArray VARCHAR(8000)[])
					IS 'Возвращает массив строк из строчных букв ';


--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT cnfn_Array_LowerCase(NULL::VARCHAR(256)[]);
SELECT cnfn_Array_LowerCase('{}'::VARCHAR(256)[]);
SELECT cnfn_Array_LowerCase('{fias_ActualStatus,fias_ACTUAL,fias_addressobjects}'::VARCHAR(256)[]);
SELECT cnfn_Array_LowerCase('{ОдИн,Два,ТРИ, черыре}'::VARCHAR(256)[]);

Создание функции cnfn_Array_QuotesWrapperWithout

Комментарии к исходному коду функции можно посмотреть здесь.

Скрытый текст
BEGIN TRANSACTION;
DROP FUNCTION IF EXISTS cnfn_Array_QuotesWrapperWithout(a_SourceArray VARCHAR(8000)[]);
/************************************************************************************************************************/
/* Возвращает массив строк, в которых удалены начальная и конечная одиночные кавычки, если такие есть					*/
/************************************************************************************************************************/
CREATE OR REPLACE FUNCTION cnfn_Array_QuotesWrapperWithout(
				a_SourceArray VARCHAR(8000)[] /* Исходный массив */
			)

RETURNS   VARCHAR(8000)[] AS
$BODY$
DECLARE
	v_QuotesWrapperWithoutArray VARCHAR(8000)[]; 	/* Массив, с неповторяющимися элементами */
	v_CurrentElement VARCHAR(8000); 		/* Текущий элемент массива */
	v_ElementAsArray  VARCHAR(8000)[1]; 	/* Текущий элемент, как массив */
	
--******************************************************************************************************       
--******************************************************************************************************
 BEGIN
     --**************************************************************************************** 
	v_QuotesWrapperWithoutArray:='{}';
    IF COALESCE(a_SourceArray,'{}')<>'{}' THEN
        FOREACH v_CurrentElement  IN ARRAY a_SourceArray LOOP
            v_ElementAsArray:='{}';
            v_CurrentElement:=TRIM(v_CurrentElement);
            v_CurrentElement:=CASE WHEN LEFT(v_CurrentElement,1)='''' AND RIGHT(v_CurrentElement,1)='''' 
                                                                     THEN SUBSTRING(v_CurrentElement FROM 2 FOR LENGTH(v_CurrentElement)-2)
                                                                     ELSE v_CurrentElement END;
            v_ElementAsArray:=array_append(v_ElementAsArray,v_CurrentElement);
            v_QuotesWrapperWithoutArray:=array_append(v_QuotesWrapperWithoutArray,v_CurrentElement);
        END LOOP;
    END IF;
    RETURN v_QuotesWrapperWithoutArray;
  END;
  $BODY$
 LANGUAGE plpgsql;
COMMENT ON FUNCTION cnfn_Array_QuotesWrapperWithout(a_SourceArray VARCHAR(8000)[])
					IS 'Возвращает массив строк, в которых удалены начальная и конечная одиночные кавычки, если такие есть';


--ROLLBACK TRANSACTION;
COMMIT TRANSACTION;
SELECT cnfn_Array_QuotesWrapperWithout(NULL::VARCHAR(256)[]);
SELECT cnfn_Array_QuotesWrapperWithout('{}'::VARCHAR(256)[]);
SELECT cnfn_Array_QuotesWrapperWithout(ARRAY['''fias_ActualStatus''','''{''','}','fias_ACTUAL']::VARCHAR(256)[]);

ПРИЛОЖЕНИЕ 2. Дополнительные материалы

Краткий комментарий к загрузке контрольного примера

К этой статье дополнительно прилагается архив, содержащий список файлов с текстами описанных функций и процедур, а также с PlantUML-скриптами, сгенерированными ими.

Кроме того, архив содержит контрольный пример с SQL описаниями таблиц, использованных в приведенных здесь примерах. Если создать базу данных и выполнить несложные действия по включению в неё таблиц и функций, то результаты выполнения функций можно получить самостоятельно.

Структура архива

Скрытый текст

Архив PostgreSQL_for_PlantUML.rar развёртывается в форме набора вложенных папок с файлами, список которых демонстрирует Таблица 24.

ArchiveFolders_Tbl024
Таблица 24. Состав вложенных папок архива

Создание контрольного примера лучше всего начать с создания новой базы данных PostgreSQL, например, с названием «Sandbox» (Песочница). После этого следует открыть папку PostgreSQL_for_PlantUML\ControlExample\Install, содержащую все файлы необходимые для создания контрольного примера.

Первым следует выполнить скрипт «TotalSchema.sql», содержащий описания всех таблиц контрольного примера и связей между ними на языке описания данных (DDL[*6]) СУБД Posgresql.

Затем должен быть выполнен скрипт «TotalSQLScript.sql», предназначенный для создания всех процедур и функций в базе данных.

При условии безошибочного исполнения этих двух скриптов, контрольный пример можно считать созданным. Остаётся проверить правильность его создания, для этого необходимо выполнить скрипт «TotalScript_test.sql», содержащий операторы проверки выполнимости всех созданных функций.

ВНИМАНИЕ. Процедуры в качестве одного из параметров принимают путь папки, в которой должны создаваться PlantUML-скрипты. Этот путь должен быть заменён на тот, где должны создаваться PlantUML-скрипты на Вашем компьютере. Тесты выполнимости процедур находятся в самом начале скрипта «TotalScript_test.sql».

Дополнительно в этой папке находятся два файла DROP «TotalSchema.sql» и «DROP ALL Functions.sql» со скриптами для удаления всех таблиц и функций соответственно.

Во вложенной папке ControlExample находятся файлы со скриптами создания процедур и функций, разбитых на группы так, как показывает Таблица 25.

ArchiveCExample_Tbl025
Таблица 25. Дополнительные скрипты массового создания процедур и функций

Назначение файлов из других вложенных папок соответствует назначениям самих папок.

Состав таблиц контрольного примера

Скрытый текст

Контрольный пример кроме процедур и функций состоит из четырёх таблиц: Список улиц (Street), Список синонимов названий улиц (StreetSynonym), Справочник типов улиц (StreetType), Список синонимов наименований типов улиц (StreetTypeSynonym).
Так как рассматриваемые в статье функции обращаются к данным каталога базы данных, а не к данным самих таблиц, то подробное описание назначения таблиц схемы здесь не приводится.

Рис. 17 показывает связи между таблицами, образованные по описаниям их внешних ключей и уникальных индексов. Поэтому «TotalSchema.sql» содержит операторы создания индексов таблиц.

CE_Schema
Рис. 17. Схема связей таблиц контрольного примера

Создание таблиц контрольного примера

Скрытый текст
ALTER TABLE IF EXISTS Street   DROP CONSTRAINT IF EXISTS fk_Street_StreetType;
ALTER TABLE IF EXISTS StreetSynonym   DROP CONSTRAINT IF EXISTS fk_StreetSynonym_Street;
ALTER TABLE IF EXISTS StreetTypeSynonym DROP CONSTRAINT IF EXISTS fk_StreetTypeSynonym_StreetType;
DROP TABLE IF EXISTS StreetSynonym;
DROP TABLE IF EXISTS Street;
DROP TABLE IF EXISTS StreetTypeSynonym;
DROP TABLE IF EXISTS StreetType;
DROP DOMAIN IF EXISTS  WCRCCode;
DROP DOMAIN IF EXISTS  LocalityID;
DROP DOMAIN IF EXISTS  StreetID;
DROP DOMAIN IF EXISTS  StreetTypeAcrm;
DROP DOMAIN IF EXISTS public.SynonymID;
CREATE DOMAIN public.WCRCCode AS SMALLINT NOT NULL;
COMMENT ON DOMAIN public.WCRCCode IS 'Код ОКСМ (общероссийский классификатор стран мира).';
CREATE DOMAIN public.LocalityID AS SMALLINT NOT NULL;
COMMENT ON DOMAIN public.LocalityID IS 'ИД населенного пункта';
CREATE DOMAIN public.StreetID AS SMALLINT NOT NULL;
COMMENT ON DOMAIN public.StreetID IS 'ИД улицы населенного пункта';
CREATE DOMAIN public.StreetTypeAcrm AS CHAR(10) NOT NULL;
COMMENT ON DOMAIN public.StreetTypeAcrm  IS 'Краткие названия типов улиц населенных пунктов';
CREATE DOMAIN public.SynonymID AS SMALLINT NULL;
COMMENT ON DOMAIN public.SynonymID IS 'ИД синонима названия (териториии, населенного пункта, улицы и т.д.).';
CREATE TABLE IF NOT EXISTS StreetType(
	StreetTypeAcrm StreetTypeAcrm NOT NULL,
	StreetTypeName varchar(30) NULL,
 CONSTRAINT XPKStreetType PRIMARY KEY(StreetTypeAcrm))WITH (OIDS=false);
COMMENT ON TABLE StreetType IS 'Справочник типов улиц'; 
COMMENT ON COLUMN StreetType.StreetTypeAcrm IS 'Акроним (буквенный код) типа улицы';
COMMENT ON COLUMN StreetType.StreetTypeName IS 'Наименование типа улицы';
DELETE FROM StreetType;
insert into StreetType values ('УЛ  ', 'Улица'); 
insert into StreetType values ('ПР-КТ', 'Проспект'); 
insert into StreetType values ('ПЕР ', 'Переулок'); 
insert into StreetType values ('ПЛ  ', 'Площадь'); 
insert into StreetType values ('ПРОЕЗД', 'Проезд'); 
insert into StreetType values ('ПЕРЕЕЗД', 'Переезд'); 
insert into StreetType values ('ПЛОЩАДКА', 'Площадка');
insert into StreetType values ('ПРОСЕЛОК', 'Проселок');
insert into StreetType values ('ПРОСЕК', 'Просек');
insert into StreetType values ('ПРОСЕКА', 'Просека');
insert into StreetType values ('ПРОУЛ', 'Проулок');



insert into StreetType values ('УРЧЩ', 'Урочище'); 
insert into StreetType values ('ЗАС ', 'Застава'); 
insert into StreetType values ('ЗЗД ', 'Заезд');
insert into StreetType values ('КВ-Л', 'Квартал'); 
insert into StreetType values ('НАБ ', 'Набережная'); 
insert into StreetType values ('МГСТР', 'Магистраль');
insert into StreetType values ('МКР ', 'Микрорайон'); 
insert into StreetType values ('ТРАКТ', 'Тракт'); 
insert into StreetType values ('САД ', 'Сад'); 
insert into StreetType values ('СТ  ', 'Станция'); 
insert into StreetType values ('ПСТН', 'Полустанок'); 
insert into StreetType values ('РЗД ', 'Разъезд'); 
insert into StreetType values ('РЯД ', 'Ряд(ы)');

insert into StreetType values ('СЛ  ', 'Слобода'); 
insert into StreetType values ('ТЕР ', 'Территория'); 
insert into StreetType values ('Ш   ', 'Шоссе'); 
insert into StreetType values ('Б-Р', 'Бульвар'); 
insert into StreetType values ('НИЗВ', 'Неизвестно'); 
insert into StreetType values ('СКВР', 'Сквер'); 
insert into StreetType values ('С-К ', 'Спуск');
insert into StreetType values ('СЗД ', 'Съезд');

insert into StreetType values ('ТУП', 'Тупик'); 
insert into StreetType values ('НП  ', 'Населенный пункт'); 
insert into StreetType values ('ГОРОДОК', 'Городок'); 
insert into StreetType values ('КМ', 'Километр'); 



insert into StreetType values ('ААЛ', 'Аал'); 
insert into StreetType values ('АЛЛЕЯ', 'Аллея'); 
insert into StreetType values ('АРБАН', 'Арбан'); 
insert into StreetType values ('ВЫСЕЛ', 'Выселки(ок)'); 
insert into StreetType values ('ВЪЕЗД', 'Въезд'); 
insert into StreetType values ('ДОР', 'Дорога'); 
insert into StreetType values ('КОЛЬЦО', 'Кольцо'); 
insert into StreetType values ('КОСА', 'Коса'); 

insert into StreetType values ('ЛИНИЯ', 'Линия'); 
insert into StreetType values ('ОСТ-В', 'Остров'); 

CREATE TABLE IF NOT EXISTS StreetTypeSynonym(
	StreetTypeAcrm StreetTypeAcrm NOT NULL,
	SynonymID SynonymID NOT NULL,
	StreetTypeSynonymAcrm VARCHAR(15) NULL,
 CONSTRAINT XPKStreetTypeSynonym PRIMARY KEY(StreetTypeAcrm,SynonymID))WITH (OIDS=false);
COMMENT ON TABLE StreetTypeSynonym IS 'Справочник синонимов типов улиц'; 
COMMENT ON COLUMN StreetTypeSynonym.StreetTypeAcrm IS 'Акроним (буквенный код) типа улицы';
COMMENT ON COLUMN StreetTypeSynonym.SynonymID IS 'ИД (идентификатор) синонима типа улицы';
COMMENT ON COLUMN StreetTypeSynonym.StreetTypeSynonymAcrm  IS 'Акроним (буквенный код) синонима типа улицы';
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('АЛЛЕЯ',1,'АЛЛЕЯ');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('Б-Р',1,'Б-Р.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('Б-Р',2,'БУЛ.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('Б-Р',3,'БУЛ');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('Б-Р',4,'БУЛЬВАР');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ДОР',1,'ДОР.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ДОР',2,'АВТОДОР.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ДОР',3,'АВТОДОР');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('КВ-Л',1,'КВ-Л.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('КВ-Л',2,'КВАРТАЛ');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('КВ-Л',3,'КВ.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('КВ-Л',4,'КВ');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('МКР',1,'МИКР.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('МКР',2,'МИКР');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('МКР',3,'МИКРОРАЙОН');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('МКР',4,'МКР.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('МКР',5,'МКРН');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('МКР',6,'МКРН.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('МКР',7,'М-Н');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('МКР',8,'М-ОН');

INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('НАБ',1,'НАБ.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('НАБ',2,'НАБЕРЕЖНАЯ');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ОСТ-В',1,'О-В');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ОСТ-В',2,'ОСТРОВ');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ОСТ-В',3,'О');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ОСТ-В',4,'О.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ПЕР',1,'ПЕР.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ПЕР',2,'ПЕРЕУЛОК');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ПЛ',1,'ПЛ.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ПЛ',2,'ПЛОЩАДЬ');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ПР-КТ',1,'ПР-КТ.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ПР-КТ',2,'ПРОСП.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ПР-КТ',3,'ПРОСП');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ПР-КТ',4,'ПР.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ПР-КТ',5,'ПР');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('РЗД',1,'РЗД.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('СКВР',1,'СКВР.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('СКВР',2,'СКВЕР');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('СКВР',3,'С-Р');

INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ТУП',1,'ТУП.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ТУП',2,'ТУПИК');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ТЕР',1,'ТЕР.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ТЕР',2,'ТЕРРИТОРИЯ');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ТЕР',3,'ПРОМЗОНА');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ТЕР',4,'П/Р');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ТЕР',5,'ПРОМРАЙОН');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ТЕР',6,'ЗОНА');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ТЕР',7,'П\Р');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('УЛ',1,'УЛ.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('УЛ',2,'УЛИЦА');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('Ш',1,'Ш.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('Ш',2,'ШОССЕ');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ПРОУЛ',1,'ПРОУЛ.');
INSERT INTO StreetTypeSynonym(StreetTypeAcrm,SynonymID,StreetTypeSynonymAcrm) VALUES('ПРОУЛ',2,'ПРОУЛОК');

CREATE TABLE IF NOT EXISTS Street(
	WCRCCode WCRCCode NOT NULL,
	LocalityID LocalityID NOT NULL,
	StreetID StreetID NOT NULL,
	StreetTypeAcrm StreetTypeAcrm NULL,
	StreetName varchar(150) NOT NULL,
	StreetTSVector TSVector NULL,
	StreetTSLiteVector TSVector NULL,
 CONSTRAINT XPKStreet PRIMARY KEY(WCRCCode,LocalityID,StreetID)) WITH (OIDS=false);
 COMMENT ON TABLE Street IS 'Список улиц в населенных пунктах'; 
COMMENT ON COLUMN Street.WCRCCode IS 'Код страны';
COMMENT ON COLUMN Street.LocalityID IS 'ИД населенного пункта';
COMMENT ON COLUMN Street.StreetID IS 'ИД улицы населенного пункта';
COMMENT ON COLUMN Street.StreetTypeAcrm IS 'Акроним типа улицы';
COMMENT ON COLUMN Street.StreetName IS 'Наименование улицы населенного пункта';
COMMENT ON COLUMN Street.StreetTSVector IS 'Расширенные характеристики улицы в населенном пункте, преобразованные в вектор полнотекстового поиска';
COMMENT ON COLUMN Street.StreetTSLiteVector IS 'Характеристики улицы в населенном пункте, преобразованные в вектор полнотекстового поиска';

CREATE  INDEX XIE1Street ON Street (WCRCCode,LocalityID,StreetTypeAcrm,StreetName);
COMMENT ON INDEX XIE1Street IS 'Индекс по типу и названию улицы населенного пункта';
CREATE  INDEX XIE2Street ON Street (WCRCCode,LocalityID,StreetName);
COMMENT ON INDEX XIE2Street IS 'Индекс по названию улицы населенного пункта';
CREATE  INDEX XIE3Street ON Street (StreetName);
COMMENT ON INDEX XIE3Street IS 'Индекс по названиям улиц во всех населенных пунктов';
CREATE INDEX XTS1Street ON Street USING gin(StreetTSVector);	
COMMENT ON INDEX XTS1Street IS 'Полнотекстовый индекс по названиях улицы, населенного пункта, териитории и их синонимам';
CREATE INDEX XTS2Street ON Street USING gin(StreetTSLiteVector);	
COMMENT ON INDEX XTS1Street IS 'Полнотекстовый индекс только по названию улицы в населенном пункте и его синонимам';
INSERT INTO Street(WCRCCode,LocalityID,StreetID,StreetTypeAcrm,StreetName) 
VALUES(   643,     11,    401, 'УЛ', 'им. газеты "Пионерская правда"');
INSERT INTO Street(WCRCCode,LocalityID,StreetID,StreetTypeAcrm,StreetName) VALUES(   643,11,1518,'ПР-КТ','им.газеты "Красноярский рабочий"');
UPDATE Street SET StreetTypeAcrm='Б-Р '
		WHERE StreetTypeAcrm='БУЛВ';
UPDATE Street SET StreetTypeAcrm='ПРОЕЗД'
		WHERE StreetTypeAcrm='ПР-Д';
UPDATE Street SET StreetTypeAcrm='ПР-КТ'
		WHERE StreetTypeAcrm='ПР-Т';
UPDATE Street SET StreetTypeAcrm='ТРАКТ'
		WHERE StreetTypeAcrm='ТРКТ';
UPDATE Street SET StreetTypeAcrm='ТУП'
		WHERE StreetTypeAcrm='ТУПК';

CREATE TABLE IF NOT EXISTS StreetSynonym(
	WCRCCode WCRCCode NOT NULL,
	LocalityID LocalityID NOT NULL,
	StreetID StreetID NOT NULL,
	SynonymID SynonymID NOT NULL,
	StreetTypeAcrm StreetTypeAcrm NULL,
	StreetSynonymName varchar(200) NULL,
 CONSTRAINT XPKStreetSynonym PRIMARY KEY(WCRCCode,LocalityID,StreetID,SynonymID))WITH (OIDS=false);
COMMENT ON TABLE StreetSynonym IS 'Список синонимов названий улиц в населенных пунктах';  
COMMENT ON COLUMN StreetSynonym.WCRCCode IS 'Код страны';
COMMENT ON COLUMN StreetSynonym.LocalityID IS 'ИД населенного пункта';
COMMENT ON COLUMN StreetSynonym.StreetID IS 'ИД улицы населенного пункта';
COMMENT ON COLUMN StreetSynonym.SynonymID IS 'ИД синонима улицы населенного пункта';
COMMENT ON COLUMN StreetSynonym.StreetTypeAcrm IS 'Акроним типа синонима улицы';
COMMENT ON COLUMN StreetSynonym.StreetSynonymName IS 'Наименование синонима улицы населенного пункта';
CREATE  INDEX XIE1StreetSynonym ON StreetSynonym (WCRCCode,LocalityID,StreetID,StreetTypeAcrm,StreetSynonymName);
COMMENT ON INDEX XIE1StreetSynonym IS 'Индекс по типу и названию синонима улицы населенного пункта';
CREATE  INDEX XIE2StreetSynonym ON StreetSynonym (WCRCCode,LocalityID,StreetID,StreetSynonymName);
COMMENT ON INDEX XIE2StreetSynonym IS 'Индекс по названию синонима улицы населенного пункта';

CREATE  INDEX XIE3StreetSynonym ON StreetSynonym (StreetSynonymName);
COMMENT ON INDEX XIE3StreetSynonym IS 'Индекс по названию синонимов всех улиц во всех населенных пунктах';
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,      3, 'УЛ', 'им газеты Пионерская Правда');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,      4, 'УЛ', 'им. газеты Пионерская Правда');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,      5, 'УЛ', 'им газеты "Пионерская Правда"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,      6, 'УЛ', 'Пионерской Правды');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,      7, 'УЛ', 'газеты "Пионерская правда"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,      8, 'УЛ', 'газеты Пионерская Правда');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,      9, 'УЛ', 'газеты Пионерской Правды');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     10, 'УЛ', 'газеты "Пионерской Правды"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     11, 'УЛ', '"Пионерской Правды"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     12, 'УЛ', 'им.газеты "Пионерская правда"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     13, 'УЛ', 'им.газеты Пионерская Правда');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     14, 'УЛ', 'им.газеты Пионерской Правды');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     15, 'УЛ', 'им.газеты "Пионерской Правды"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     16, 'УЛ', 'им. газеты Пионерской Правды');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     17, 'УЛ', 'им. газеты "Пионерской Правды"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     18, 'УЛ', 'им газеты Пионерской Правды');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     19, 'УЛ', 'им газеты "Пионерской Правды"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     20, 'УЛ', 'имени газеты "Пионерская правда"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     21, 'УЛ', 'имени газеты Пионерская Правда');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     22, 'УЛ', 'имени газеты Пионерской Правды');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,    401,     23, 'УЛ', 'имени газеты "Пионерской Правды"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,      2, 'ПР-КТ', 'имени газеты Красноярский Рабочий');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,      3, 'ПР-КТ', 'газеты Красноярский Рабочий');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,      4, 'ПР-КТ', 'им. газеты "Красноярский рабочий"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,      5, 'ПР-КТ', 'имени газеты "Красноярский Рабочий"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,      6, 'ПР-КТ', 'им. газ. "Красноярский рабочий"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,      7, 'ПР-КТ', 'им.газ."Красноярский рабочий"');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,      8, 'ПР-КТ', 'им.газ.Красноярский рабочий');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,      9, 'ПР-КТ', 'им. газ. Красноярский рабочий');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     10, 'УЛ', 'Красноярский Рабочий');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     11, 'ПР-КТ', 'Красноярский Рабочий');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     12, 'ПР-КТ', 'им. газеты Красноярский Рабочий');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     13, 'ПР-КТ', 'им.газеты Красноярский рабочий');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     14, 'ПР-КТ', 'Красраб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     15, 'УЛ', 'Красраб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     16, 'ПР-КТ', 'Крас.Раб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     17, 'УЛ', 'Крас.Раб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     18, 'ПР-КТ', 'Крас.Раб.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     19, 'УЛ', 'Крас.Раб.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     20, 'ПР-КТ', 'Крас. Раб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     21, 'УЛ', 'Крас. Раб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     22, 'ПР-КТ', 'Крас. Раб.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     23, 'УЛ', 'Крас. Раб.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     24, 'ПР-КТ', 'им. газеты «Красноярский рабочий»');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     25, 'ПР-КТ', 'им. газ.«Красноярский рабочий»');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     26, 'ПР-КТ', 'им. газ. «Красноярский рабочий»');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     27, 'ПР-КТ', 'Крас Раб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     28, 'УЛ', 'Крас Раб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     29, 'ПР-КТ', 'КРАСН.РАБОЧИЙ');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     30, 'ПР-КТ', 'КРАСН. РАБОЧИЙ');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     31, 'УЛ', 'КРАСН.РАБОЧИЙ');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     32, 'УЛ', 'КРАСН. РАБОЧИЙ');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     33, 'ПР-КТ', 'КРАСН РАБОЧИЙ');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     34, 'УЛ', 'КРАСН РАБОЧИЙ');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     35, 'ПР-КТ', 'КРАСН.РАБ.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     36, 'ПР-КТ', 'КРАСН. РАБ.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     37, 'УЛ', 'КРАСН.РАБ.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     38, 'УЛ', 'КРАСН. РАБ.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     39, 'ПР-КТ', 'КРАСН РАБ');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     40, 'УЛ', 'КРАСН РАБ');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     41, 'ПР-КТ', 'КРАСН РАБ.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     42, 'УЛ', 'КРАСН РАБ.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     43, 'ПР-КТ', 'им газеты Красноярский Рабочий');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     44, 'ПР-КТ', 'Кр.Раб.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     45, 'ПР-КТ', 'Кр.Раб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     46, 'ПР-КТ', 'Кр. Раб.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     47, 'ПР-КТ', 'КрРаб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     48, 'ПР-КТ', 'Краб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     49, 'УЛ', 'Кр.Раб.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     50, 'УЛ', 'Кр.Раб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     51, 'УЛ', 'Кр. Раб.');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     52, 'УЛ', 'КрРаб');
INSERT INTO StreetSynonym(WCRCCode,LocalityID,StreetID,SynonymID,StreetTypeAcrm,StreetSynonymName) VALUES(   643,     11,   1518,     53, 'УЛ', 'Краб');

UPDATE StreetSynonym SET StreetTypeAcrm='Б-Р '
		WHERE StreetTypeAcrm='БУЛВ';
UPDATE StreetSynonym SET StreetTypeAcrm='ПРОЕЗД'
		WHERE StreetTypeAcrm='ПР-Д';
UPDATE StreetSynonym SET StreetTypeAcrm='ПР-КТ'
		WHERE StreetTypeAcrm='ПР-Т';
UPDATE StreetSynonym SET StreetTypeAcrm='ТРАКТ'
		WHERE StreetTypeAcrm='ТРКТ';
UPDATE StreetSynonym SET StreetTypeAcrm='ТУП'
		WHERE StreetTypeAcrm='ТУПК';

UPDATE Street s SET StreetTSVector=setweight(to_tsvector(StreetTypeAcrm),'A')||setweight(to_tsvector(StreetName),'A')||setweight(to_tsvector((SELECT StreetTypeName FROM StreetType st WHERE UPPER(TRIM(st.StreetTypeAcrm))=UPPER(TRIM(s.StreetTypeAcrm)))),'A')||
											                            setweight(cnfn_Street_AllSynonymTsVector(WCRCCode,LocalityID,StreetID),'D'),
                StreetTSLiteVector=setweight(to_tsvector(StreetTypeAcrm),'A')||setweight(to_tsvector(StreetName),'A')||setweight(to_tsvector((SELECT StreetTypeName FROM StreetType st WHERE UPPER(TRIM(st.StreetTypeAcrm))=UPPER(TRIM(s.StreetTypeAcrm)))),'A');


ALTER TABLE Street   ADD  CONSTRAINT fk_Street_StreetType FOREIGN KEY(StreetTypeAcrm) REFERENCES StreetType (StreetTypeAcrm) MATCH FULL;
ALTER TABLE Street   ADD  CONSTRAINT ck_Street_StreetTypeAcrm CHECK(StreetTypeAcrm !~* E'[a-z]');
ALTER TABLE StreetSynonym   ADD  CONSTRAINT fk_StreetSynonym_Street FOREIGN KEY(WCRCCode, LocalityID, StreetID) REFERENCES Street (WCRCCode, LocalityID, StreetID) MATCH FULL;
ALTER TABLE StreetSynonym   ADD  CONSTRAINT fk_StreetSynonym_StreetType FOREIGN KEY(StreetTypeAcrm) REFERENCES StreetType (StreetTypeAcrm) MATCH FULL;
ALTER TABLE StreetTypeSynonym ADD  CONSTRAINT fk_StreetTypeSynonym_StreetType FOREIGN KEY(StreetTypeAcrm) REFERENCES StreetType(StreetTypeAcrm) MATCH FULL;

Комментарии (0)