Диаграммы классов UML из PostgreSQL. Окончание

от автора

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' THENv_CurrSign:=admfn_Table_ConstrainedLinkSign(a_SchemaName,a_TableName,a_FKConstrantName);v_LinkSign:=v_CurrSign||':'||'1'; ELSIF v_ForeignKeyRole='Referenced'THENv_ConstrainedTableName:=admfn_ForeignKey_ConstrainedTableName(a_SchemaName,a_FKConstrantName);v_CurrSign:=admfn_Table_ConstrainedLinkSign(a_SchemaName,v_ConstrainedTableName,a_FKConstrantName);v_LinkSign:= '1'||':'||v_CurrSign; ELSEv_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_SchemaNameNAME default 'public',/* название схемы базы данных*/ a_TableNameNAME default NULL, /* Название таблицы */ a_isOrderNeedBOOLEAN 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_IndexRecRECORD;/* Данные об индексе*/ v_ScaleINTEGER;/* Масштаб колонки */ v_TableOIDINTEGER;/* OID таблицы*/ v_IndexOIDINTEGER;/* OID индекса*/ v_IndexKeyNosSMALLINT[];/* Массив порядковых номеров атрибутов*/ v_IndexKeyOpsSMALLINT[];/* Массив опций атрибутов индекса*/ v_IndexNameNAME;/* Название индекса */ v_IndexAMNameNAME;/* Наименование типа индекса (метода доступа) */ v_isUnique BOOLEAN;/* Признак уникальности индекса*/ v_isPrimary BOOLEAN;/* Признак того что индекс представляет Primary KEY таблицы*/ v_AttributeNumINTEGER;/* Порядковый номер атрибута*/ v_AttributeOrderCodeINTEGER;/* Признак упорядоченности атрибута*/ v_AttributeNameNAME;/* Наименование атрибута*/ v_AmCanOrderBOOLEAN; /* Поддерживает ли метод доступа упорядоченное сканирование по значению */ /* индексируемого столбца?*/ v_OrderDirectVARCHAR(10);/* Направление упорядоченности атрибута в индексе*/ v_IndexKeyNamesTEXT;/* Строка со списком атрибутов индекса*/ v_IndexAttributeLBoundINTEGER;/* Нижний индекс масссива атрибутов индекса*/ v_IndexAttributeUBoundINTEGER;/* Верхний индекс масссива атрибутов индекса*/ v_IndexAttributeIndxINTEGER;/* Текущий индекс масссива атрибутов индекса*/ v_pg_am_VersionINTEGER; /* Версия таблицы 'pg_am':  0 - версия postgresql ниже 9.6; 1 - версия postgresql выше 9.6  */ v_isOrderNeedBOOLEAN; /* Нужно ли добавлять признак порядка сортировки для каждого атрибута индекса? */ v_SELECTStatementTEXT; /* Оператор 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_SchemaNameVARCHAR(256) default 'public',/* название схемы базы данных*/ a_TableNameVARCHAR(256) default NULL, /* Название таблицы */ a_isOrderNeedBOOLEAN 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_SchemaNameVARCHAR(256) default 'public',/* название схемы базы данных*/ a_TableNameVARCHAR(256) default NULL,/* Название таблицы */ a_AttributeName VARCHAR(256) default NULL/* Название атрибута*/ )  RETURNS  BOOLEAN AS $BODY$ DECLAREc_TableKindCONSTANT CHAR:='r'; c_DoubleQuoteCONSTANT CHAR:='"'; v_TableOIDOID;/*OID таблицы */ v_AttributeNoSMALLINT;/* Порядковый номер найденного атрибута */ v_AttributeNameVARCHAR(256);/* Название найденного атрибута */  v_isAttributeExistsBOOLEAN;/* признак наличия аттрибута в таблице */ --****************************************************************************************************** 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_SchemaNameVARCHAR(256) default 'public',/* название схемы базы данных*/ a_WHEREoptionVARCHAR(1000) default NULL/* условие запроса на выбор таблиц из системного каталога */    )   RETURNS  VARCHAR(256)[] AS $BODY$ DECLAREc_TableKindCONSTANT 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_TableRowCountINTEGER;/* Счетчик выбранных таблиц*/ v_SELECT_Statement VARCHAR(1000);/* Текст оператора SELECT для выбора таблиц*/ v_AloneTableName VARCHAR(256);/* Название одной из выбранных таблиц */ v_TableNameArrayVARCHAR(256)[];/* Массив выбранных таблиц  */ v_isExpressionCorrectBOOLEAN;/* Признак правильности выражения WHERE  */ v_Return_ErrorINTEGER:=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_SchemaNameNAME default 'public',/* название схемы базы данных*/ a_TableNameNAME default NULL,/* Название таблицы */ a_FKConstrantNameNAME default NULL/* Название ограничения ForeignKey */ ) RETURNS  VARCHAR(5) AS $BODY$ DECLAREc_ManySignCONSTANT CHAR:='N'; c_OneSignCONSTANT CHAR:='1'; c_DelimiterSignCONSTANT CHAR:=':'; c_ReferencedRoleCONSTANT VARCHAR(20):='Referenced';/* Обозначение таблицы, на которую ссылается 'внешний ключ */ c_ConstrainedRoleCONSTANT VARCHAR(20):='Constrained';/* обозначение  таблицы, которая содержат ограничение 'внешний ключ' */ v_ForeignKeyRoleVARCHAR(20);/* РОль таблицы в ограничении 'внешний ключ' */ v_CurrSignCHAR;/* Признак таблицы в текущей связи */ v_LinkSignVARCHAR(5);/* Признак связи по отношения к таклице*/ v_ConstrainedTableNameNAME;/* Название таблицы, которая содержат ограничение 'внешний ключ' */ --****************************************************************************************************** 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_SchemaNameVARCHAR(256) default 'public',/* название схемы базы данных*/ a_TableNameVARCHAR(256) default NULL,/* Название таблицы */ a_FKConstrantNameVARCHAR(256) default NULL/* Название ограничения ForeignKey */    )   RETURNS  VARCHAR(5) AS $BODY$ DECLAREv_LinkSignVARCHAR(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_SchemaNamename default 'public',/* название схемы базы данных*/ a_TableNamename default NULL,/* Название таблицы */ a_FKConstrantNamename default NULL/* Название ограничения ForeignKey */   )   RETURNS VARCHAR(10) AS $BODY$ DECLARE c_ForeignTypeTypeCONSTANT VARCHAR(2):='f';/* Буквенное обозначение типа ограничения 'внешний ключ' */ c_ReferencedRoleCONSTANT VARCHAR(20):='Referenced';/* Обозначение таблицы, на которую ссылается 'внешний ключ' */ c_ConstrainedRoleCONSTANT VARCHAR(20):='Constrained';/* обозначение  таблицы, которая содержат ограничение 'внешний ключ' */ v_ForeignKeyRoleVARCHAR(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_SchemaNameVARCHAR(256) default 'public',/* название схемы базы данных */ a_TableNameVARCHAR(256) default NULL,/* Название таблицы */ a_FKConstrantNameVARCHAR(256) default NULL/* Название ограничения ForeignKey */   )   RETURNS   VARCHAR(10) AS $BODY$ DECLARE c_ForeignTypeTypeCONSTANT VARCHAR(2):='f';/* Буквенное обозначение типа ограничения 'внешний ключ ключ' */ c_ReferencedRoleCONSTANT VARCHAR(20):='Referenced';/* Обозначение таблицы, на которую ссылается 'внешний ключ */ c_ConstrainedRoleCONSTANT VARCHAR(20):='Constrained';/* обозначение  таблицы, которая содержат ограничение 'внешний ключ' */ v_ForeignKeyRoleVARCHAR(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_SchemaNameNAME default 'public',/* название схемы базы данных*/ a_TableNameNAME default NULL,/* Название таблицы */ a_FKConstrantNameNAME default NULL/* Название ограничения ForeignKey */    )   RETURNS  VARCHAR(1) AS $BODY$ DECLAREc_ManySignCONSTANT CHAR:='N'; c_OneSign    CONSTANT CHAR:='1'; c_DelimiterSignCONSTANT CHAR:=':'; c_DelimiterKeysCONSTANT VARCHAR(2):=','; c_ConstraintTypeCONSTANT VARCHAR(2):='u';/* Буквенное обозначение типа ограничения 'уникальный ключ' */ c_ForeignTypeTypeCONSTANT VARCHAR(2):='f';/* Буквенное обозначение типа ограничения 'внешний ключ ключ' */ c_ReferencedRoleCONSTANT VARCHAR(20):='Referenced';/* Обозначение таблицы, на которую ссылается 'внешний ключ */ c_ConstrainedRoleCONSTANT VARCHAR(20):='Constrained';/* обозначение  таблицы, которая содержат ограничение 'внешний ключ' */ c_withoutOrder      CONSTANT BOOLEAN:=FALSE; /* Не добавлять признак порядка сортировки для каждого атрибута индекса */ v_ForeignKeyRoleVARCHAR(20);/* РОль таблицы в ограничении 'внешний ключ' */ v_UniqueIndexRECORD;/* Запись об уникальном индексе таблицы*/ v_UniqueConstraintRECORD;/* Запись об уникальном  уникальном ключе таблицы*/ v_IndexKeyNamesTEXT;/* Строка со списком атрибутов индекса*/ v_CurrSignCHAR;/* Признак таблицы в текущей связи */ v_TablePKNAME[];/* Массив Названий атрибутов, составляющих внешний ключ */ v_TableFKNAME[];/* Массив Названий атрибутов, составляющих внешний ключ */ v_TableIKNAME[];/* Массив Названий атрибутов, в составе индекса */ v_TableUKNAME[];/* Массив Названий атрибутов, составляющих уникальный ключ (уникальное значение) */ v_IndexNameNAME;/* Название индекса */ v_ConstraintNameNAME;/* Название ограничения */ v_ConstraintTypeVARCHAR(256);/* Буквенное обозначение типа ограничения */ v_LinkSignVARCHAR(5);/* Признак связи по отношения к таклице*/     v_haveUniqueIndexesBOOLEAN;/* признак наличия уникальных индексов в описании таблицы */ --****************************************************************************************************** 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_SchemaNameVARCHAR(256) default 'public',/* название схемы базы данных*/ a_TableNameVARCHAR(256) default NULL,/* Название таблицы */ a_FKConstrantNameVARCHAR(256) default NULL/* Название ограничения ForeignKey */ )  RETURNS  VARCHAR(5) AS $BODY$ DECLAREv_CurrSignCHAR;  /* Признак таблицы в текущей связи */ --****************************************************************************************************** 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_SchemaNamename default 'public',/* название схемы базы данных*/ a_FKConstrantNamename default NULL/* Название внешнего ключа таблицы */  ) RETURNS  name AS $BODY$ DECLARE v_ConstrainedTableNamename;/* Наименование таблицы, на которую ссылается внешний ключ */ --****************************************************************************************************** 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_SchemaNameVARCHAR(256) default 'public',/* название схемы базы данных*/ a_FKConstrantNameVARCHAR(256) default NULL/* Название внешнего ключа таблицы */  )   RETURNS  VARCHAR(256) AS $BODY$ DECLARE c_ForeignKeyKindCONSTANT CHAR:='f'; v_ConstrainedTableNameVARCHAR(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_SchemaNameVARCHAR(256) default 'public',/* название схемы базы данных */ a_TableNameVARCHAR(256) default NULL/* Название таблицы */ )  RETURNS  VARCHAR(256)[] AS $BODY$ DECLAREc_TableKindCONSTANT CHAR:='r'; c_PrimaryKeyKindCONSTANT CHAR:='p'; c_PKAttributeList_NDims CONSTANT INTEGER:=1;/* Размерность массива атрибутов первичного ключа*/ v_TableOIDOID;/*OID таблицы */ v_PKAttributeListSMALLINT[];/* Список ИД атрибутов, составляющих первичный ключ */ v_PKAttributeIDSMALLINT;/* ИД атрибута, первичного ключа */ v_PKAttributeNoSMALLINT;/* Порядковый номер атрибута, первичного ключа */ v_PKAttributeNameVARCHAR(256);/* Название атрибута, составляющих первичный ключ */  v_TablePKVARCHAR(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_SchemaNamename default 'public',/* название схемы базы данных*/ a_TableNamename default NULL/* Название таблицы */    )   RETURNS  name[] AS $BODY$ DECLAREc_TableKindCONSTANT CHAR:='r'; c_PrimaryKeyKindCONSTANT CHAR:='p'; c_PKAttributeList_NDims CONSTANT INTEGER:=1;/* Размерность массива атрибутов первичного ключа*/ v_TableOIDOID;/*OID таблицы */ v_PKAttributeListSMALLINT[];/* Список ИД атрибутов, составляющих первичный ключ */ v_PKAttributeIDSMALLINT;/* ИД атрибута, первичного ключа */ v_PKAttributeNoSMALLINT;/* Порядковый номер атрибута, первичного ключа */ v_PKAttributeNamename;/* Название атрибута, составляющих первичный ключ */  v_TablePKname[];/* Названий атрибутов, составляющих первичный ключ */ --****************************************************************************************************** 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_SchemaNameVARCHAR(256) default 'public',/* название схемы базы данных*/ a_TableNameVARCHAR(256) default NULL,/* Название таблицы */ a_FKConstrantNameVARCHAR(256) default NULL/* Название ограничения ForeignKey */ )  RETURNS  VARCHAR(256)[] AS $BODY$ DECLAREc_TableKindCONSTANT CHAR:='r'; c_ForeignKeyKindCONSTANT CHAR:='f'; c_FKAttributeList_NDims CONSTANT INTEGER:=1;/* Размерность массива атрибутов внешнего ключа*/ v_TableOIDOID;/*OID таблицы */ v_FKAttributeListSMALLINT[];/* Список ИД атрибутов, составляющих внешний ключ */ v_FKAttributeIDSMALLINT;/* ИД атрибута, внешнего ключа */ v_FKAttributeNoSMALLINT;/* Порядковый номер атрибута, внешнего ключа */ v_FKAttributeNameVARCHAR(256);/* Название атрибута, составляющих внешний ключ */  v_TableFKVARCHAR(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_SchemaNamename default 'public',/* название схемы базы данных*/ a_TableNamename default NULL,/* Название таблицы */ a_FKConstrantNamename default NULL/* Название ограничения ForeignKey */ )   RETURNS  name[] AS $BODY$ DECLAREc_TableKindCONSTANT CHAR:='r'; c_ForeignKeyKindCONSTANT CHAR:='f'; c_FKAttributeList_NDims CONSTANT INTEGER:=1;/* Размерность массива атрибутов внешнего ключа*/ v_TableOIDOID;/*OID таблицы */ v_FKAttributeListSMALLINT[];/* Список ИД атрибутов, составляющих внешний ключ */ v_FKAttributeIDSMALLINT;/* ИД атрибута, внешнего ключа */ v_FKAttributeNoSMALLINT;/* Порядковый номер атрибута, внешнего ключа */ v_FKAttributeNamename;/* Название атрибута, составляющих внешний ключ */ v_TableFKname[];/* Названий атрибутов, составляющих внешний ключ */ --****************************************************************************************************** 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_haveUniqueIndexesBOOLEAN:=FALSE;/* признак наличия уникальных индексов в описании таблицы */ v_SchemaNameNAME; /* название схемы базы данных*/ v_isPrimaryNeedBOOLEAN;/* Нужно ли учитывать уникальный индекс на основе первичного ключа PRIMARY KEY? */ v_isINDEXExistsBOOLEAN;/* Существуют ли  интедсксы в таблице*/ --****************************************************************************************************** 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_haveUniqueIndexesBOOLEAN:=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;


ссылка на оригинал статьи https://habr.com/ru/articles/841926/