Первинне завантаження даних реєстру (initial load)

🌐 Цей документ доступний українською та англійською мовами. Використовуйте перемикач у правому верхньому куті, щоб змінити версію.

Для первинного завантаження даних використовується БД-процедура PL/pgSQL.

Перед виконанням процедури первинного завантаження даних, переконайтеся, що виконано передумови, описані в секції Підготовка даних до міграції.

1. Специфікація PL/pgSQL процедури для БД Платформи

Процедура для завантаження даних
CREATE OR REPLACE PROCEDURE p_load_table_from_csv(
p_table_name text
, p_file_name text
, p_table_columns text[]
, p_target_table_columns text[] DEFAULT NULL::text[]
);
Опис параметрів:
  • p_table_name - назва цільової таблиці;

  • p_file_name - повний шлях до файлу з даними для завантаження у форматі CSV;

  • p_table_columns - масив назв полів у файлі даних (перший рядок CSV-файлу);

  • p_target_table_columns - масив полів цільової таблиці (відсутній, якщо перелік полів збігається із полями в CSV-файлі).

    Значення елементів параметра p_target_table_columns може набувати різних форматів:

    • <col_name> — поле в цільовій таблиці отримує значення відповідного (за назвою) поля в CSV-файлі;

    • <col_name>::<some_calculation_based_on_source_cols> — поле отримує значення — результат виразу

      Детальніше — див. підрозділ Складний сценарій 1 у прикладах виклику.
    • <col_name>::ref(lookup_col:<lookup_col_name>, ref_table:<ref_table_name>, ref_col:<ref_col_name>, ref_id:<ref_id_col_name>) — для даного формату "під капотом" виконується наступний SQL-запит, що реалізовує пошук id по таблиці довідника:

      SELECT <ref_id> INTO <col_name> FROM <ref_table> WHERE <ref_col> = <lookup_col>;

      Такий запит застосовується у випадку, коли поле містить зовнішній ключ (англ. — foreign key) посилання на довідник, а значення для завантаження у CSV-файлі представлене не ідентифікатором, а описовим полем, проте значення цього поля мають бути унікальними.

      Детальніше — див. підрозділ Cкладний сценарій 2 у прикладах виклику.
    • <col_name>::ref_array(lookup_col:<lookup_col_name>, ref_table:<ref_table_name>, ref_col:<ref_col_name>, ref_id:<ref_id_col_name>, delim:<delimeter_character>) — цей формат відмінний від попереднього лише тим, що поле <lookup_col_name> у CSV-файлі містить масив описових значень. В результаті повертатиметься масив відповідних id.

2. Приклади виклику процедури

2.1. Простий сценарій

CALL p_load_table_from_csv('research',
'D:\PostgreSQL\csv\uuid\01-Typy_doslidgen_uuid.csv', array
['uuid','research_type'], array['research_type']);

2.2. Складний сценарій 1

CALL p_load_table_from_csv('koatuu','${dataLoadPath}03-KOATUU_simple.csv'
, array['code','category','name']
, array['code','category','name'
,'level1::substring(code,1,2)||''00000000'''
,'level2::substring(code,1,5)||''00000'''
,'type::CASE WHEN code ~ ''[0-9]{2}0{8}'' AND code !~ ''(80|85)0{8}'' THEN ''О''
            WHEN code ~ ''[0-9]{2}2[0-9]{2}0{5}'' AND code !~ ''[0-9]{2}20{7}'' THEN ''Р''
            WHEN coalesce(category, ''Р'') != ''Р''
            OR code IN (SELECT DISTINCT substring(code,1,5)||''00000'' FROM koatuu_csv k2 WHERE category = ''Р'') AND category IS NULL
            OR code ~ ''(80|85)0{8}'' THEN ''НП''
            ELSE NULL END']
);

2.3. Складний сценарій 2

CALL p_load_table_from_csv('staff','D:\PostgreSQL\csv\staff.csv'

,array['uuid','laboratory','staff_status','researches','full_name','hygienist_flag','full_time_flag','salary','fixed_term_contract_flag','contract_end_date','specialization_date','specialization_end_date','dismissal_flag','dismissal_date']
  ,array['laboratory_id::ref(lookup_col:laboratory,ref_table:laboratory,ref_col:name,ref_id:laboratory_id)'
        ,'staff_status_id::ref(lookup_col:staff_status,ref_table:staff_status,ref_col:name,ref_id:staff_status_id)'
        ,'researches::ref_array(lookup_col:researches,ref_table:research,ref_col:research_type,ref_id:research_id,delim:#)'
        ,'full_name','hygienist_flag','full_time_flag','salary','fixed_term_contract_flag','contract_end_date','specialization_date','specialization_end_date','dismissal_flag','dismissal_date']
     );

3. Завантаження даних до БД

Для завантаження даних в БД використовуємо стандартну функціональність liquibase.

Приклад XML-шаблону для завантаження даних
<databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.2.xsd
        http://www.liquibase.org/xml/ns/dbchangelog-ext xsd/dbchangelog-ext.xsd">

    <property  name="dataLoadPath"  value="D:\PostgreSQL\csv\"/>

    <changeSet author="registry owner" id="load data to dictionaries">
        <sql dbms="postgresql" endDelimiter=";" splitStatements="true" stripComments="true">
            CALL p_load_table_from_csv('research','${dataLoadPath}01-Typy doslidgen.csv', array['code','research_type'], array['research_type']);
            CALL p_load_table_from_csv('refusal_reason','${dataLoadPath}02-Prichiny vidmov.csv', array['code','document_type','name','constant_code'], array['document_type','name','constant_code']);
            CALL p_load_table_from_csv('kopfg','${dataLoadPath}05-kopfg.csv', array['code','name']);
            CALL p_load_table_from_csv('solution_type','${dataLoadPath}06-Typy rishen.csv', array['id','name','constant_code'], array['name','constant_code']);
            CALL p_load_table_from_csv('factor','${dataLoadPath}14-Himichni_factory_OBRV.csv', array['code','name','col3','col4'], array['name','factor_type::''Хімічний: ОБРВ''']);
            CALL p_load_table_from_csv('factor','${dataLoadPath}15-Himichni factory dovilni.csv', array['code','name'], array['name','factor_type::''Хімічний: довільні''']);
        </sql>
  </changeSet>

</databaseChangeLog>
За детальною інформацією щодо створення фізичної моделі даних реєстру за допомогою інструмента Liquibase зверніться до розділу "Створення фізичної моделі даних реєстру" цього документа.

4. Вимоги до файлів для завантаження до БД

Файли, що використовуватимуться для завантаження даних до БД, повинні мати чітку структуру.

Вимоги до файлів для автоматичного завантаження в БД:
  • тільки СSV-формат;

  • назва файлу — латиницею;

  • кодування — UTF8;

  • перший рядок містить назви полів;

  • починаючи із другого рядка містяться значення полів (структура відповідає назвам полів із першого рядка);

  • значення полів одного рядка файлу повністю визначають значення полів, необхідних для створення запису в базі даних;

  • розділювач значень полів: , (кома) або ; (крапка з комою);

  • відсутність значення (NULL) передається відсутнім значенням, наприклад, …​значення 1,,значення 3…​, тобто відсутнє значення пропускається між двома комами, а не позначається пробілом);

  • наявність унікальності за одним бізнес-полем (наприклад, поле Назва) — для довідників;

  • для позначення масивів описових значень використовуються фігурні дужки {, }.

5. Рекомендації для завантаження великої кількості даних

Для завантаження великих csv-файлів (десятки і сотні мегабайт) можна використати стандартний SQL код замість процедури. Для коректної роботи реєстру такий SQL код повинен також створити історичні дані (таблиця _hst) та заповнити поля з метаданими (колонки ddm_), тобто повторити ті операції що процедура виконує автоматично. В прикладі наведений коректний та найбільш ефективний метод це зробити.

Приклад SQL коду для завантаження даних
-- Створення тимчасової проміжної таблиці, яка
-- відповідає формату csv файлу що завантажується
CREATE TABLE account_csv_stage (username text, bank_number text);

-- Завантаження даних із csv файлу в проміжну таблицю
COPY account_csv_stage (username,bank_number)
FROM '${dataLoadPath}account.csv'
WITH (HEADER, FORMAT CSV);

-- Вставка даних в основну та історичну таблиці
WITH main_table_cte AS (
    INSERT INTO account (
        username
      , bank_number
      , ddm_created_by
      , ddm_updated_by
      )
    SELECT username
         , bank_number
         , 'admin'
         , 'admin'
    FROM account_csv_stage
    RETURNING *)
INSERT INTO account_hst (
      id
    , username
    , bank_number
    , ddm_created_by
    , ddm_created_at
    , ddm_dml_op
    , ddm_system_id
    , ddm_application_id
    , ddm_business_process_id)
SELECT 	  id
        , username
        , bank_number
        , ddm_created_by
        , CURRENT_TIMESTAMP
        , 'I' as ddm_dml_op
        , (SELECT ss.system_id
             FROM ddm_source_system ss
            WHERE ss.system_name ='initial load') ddm_system_id
        , (SELECT sa.application_id
             FROM ddm_source_application sa
            WHERE sa.application_name ='initial load') ddm_application_id
        , (SELECT sb.business_process_id
             FROM ddm_source_business_process sb
            WHERE sb.business_process_name ='initial load process') ddm_business_process_id
FROM main_table_cte;

-- Видалення тимчасової проміжної таблиці
DROP TABLE account_csv_stage;

Таким кодом можна замінити виклик процедури в XML-шаблоні для завантаження даних. Для кожного завантаження файлу таким методом, варто створювати окремий ченджсет.