123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763 |
- do $$
- begin
- /*
- Скрипт создания информационной базы данных
- Согласно технического задания https://git.hostfl.ru/VolovikovAlex/Study2025
- Редакция 2025-02-12
- Edit by valex
- */
- /*
- 1. Удаляем старые элементы
- ======================================
- */
- raise notice 'Запускаем создание новой структуры базы данных meteo';
- begin
- -- Связи
- alter table if exists public.measurment_input_params
- drop constraint if exists measurment_type_id_fk;
- alter table if exists public.employees
- drop constraint if exists military_rank_id_fk;
- alter table if exists public.measurment_baths
- drop constraint if exists measurment_input_param_id_fk;
- alter table if exists public.measurment_baths
- drop constraint if exists emploee_id_fk;
-
- -- Таблицы
- drop table if exists public.measurment_input_params;
- drop table if exists public.measurment_baths;
- drop table if exists public.employees;
- drop table if exists public.measurment_types;
- drop table if exists public.military_ranks;
- drop table if exists public.measurment_settings;
- -- Нумераторы
- drop sequence if exists public.measurment_input_params_seq;
- drop sequence if exists public.measurment_baths_seq;
- drop sequence if exists public.employees_seq;
- drop sequence if exists public.military_ranks_seq;
- drop sequence if exists public.measurment_types_seq;
- end;
- raise notice 'Удаление старых данных выполнено успешно';
- /*
- 2. Добавляем структуры данных
- ================================================
- */
- -- Справочник должностей
- create table military_ranks
- (
- id integer primary key not null,
- description character varying(255)
- );
- insert into military_ranks(id, description)
- values(1,'Рядовой'),(2,'Лейтенант');
- create sequence military_ranks_seq start 3;
- alter table military_ranks alter column id set default nextval('public.military_ranks_seq');
- -- Пользователя
- create table employees
- (
- id integer primary key not null,
- name text,
- birthday timestamp ,
- military_rank_id integer
- );
- insert into employees(id, name, birthday,military_rank_id )
- values(1, 'Воловиков Александр Сергеевич','1978-06-24', 2);
- create sequence employees_seq start 2;
- alter table employees alter column id set default nextval('public.employees_seq');
- -- Устройства для измерения
- create table measurment_types
- (
- id integer primary key not null,
- short_name character varying(50),
- description text
- );
- insert into measurment_types(id, short_name, description)
- values(1, 'ДМК', 'Десантный метео комплекс'),
- (2,'ВР','Ветровое ружье');
- create sequence measurment_types_seq start 3;
- alter table measurment_types alter column id set default nextval('public.measurment_types_seq');
- -- Таблица с параметрами
- create table measurment_input_params
- (
- id integer primary key not null,
- measurment_type_id integer not null,
- height numeric(8,2) default 0,
- temperature numeric(8,2) default 0,
- pressure numeric(8,2) default 0,
- wind_direction numeric(8,2) default 0,
- wind_speed numeric(8,2) default 0,
- bullet_demolition_range numeric(8,2) default 0
- );
- insert into measurment_input_params(id, measurment_type_id, height, temperature, pressure, wind_direction,wind_speed )
- values(1, 1, 100,12,34,0.2,45);
- create sequence measurment_input_params_seq start 2;
- alter table measurment_input_params alter column id set default nextval('public.measurment_input_params_seq');
- -- Таблица с историей
- create table measurment_baths
- (
- id integer primary key not null,
- emploee_id integer not null,
- measurment_input_param_id integer not null,
- started timestamp default now()
- );
- insert into measurment_baths(id, emploee_id, measurment_input_param_id)
- values(1, 1, 1);
- create sequence measurment_baths_seq start 2;
- alter table measurment_baths alter column id set default nextval('public.measurment_baths_seq');
- -- Таблица с настройками
- create table measurment_settings
- (
- key character varying(100) primary key not null,
- value character varying(255) ,
- description text
- );
- insert into measurment_settings(key, value, description)
- values('min_temperature', '-10', 'Минимальное значение температуры'),
- ('max_temperature', '50', 'Максимальное значение температуры'),
- ('min_pressure','500','Минимальное значение давления'),
- ('max_pressure','900','Максимальное значение давления'),
- ('min_wind_direction','0','Минимальное значение направления ветра'),
- ('max_wind_direction','59','Максимальное значение направления ветра'),
- ('calc_table_temperature','15.9','Табличное значение температуры'),
- ('calc_table_pressure','750','Табличное значение наземного давления'),
- ('min_height','0','Минимальная высота'),
- ('max_height','400','Максимальная высота');
- raise notice 'Создание общих справочников и наполнение выполнено успешно';
- /*
- 3. Подготовка расчетных структур
- ==========================================
- */
- drop table if exists calc_temperatures_correction;
- create table calc_temperatures_correction
- (
- temperature numeric(8,2) primary key,
- correction numeric(8,2)
- );
- insert into public.calc_temperatures_correction(temperature, correction)
- Values(0, 0.5),(5, 0.5),(10, 1), (20,1), (25, 2), (30, 3.5), (40, 4.5);
- drop type if exists interpolation_type;
- create type interpolation_type as
- (
- x0 numeric(8,2),
- x1 numeric(8,2),
- y0 numeric(8,2),
- y1 numeric(8,2)
- );
- drop type if exists input_params cascade;
- create type input_params as
- (
- height numeric(8,2),
- temperature numeric(8,2),
- pressure numeric(8,2),
- wind_direction numeric(8,2),
- wind_speed numeric(8,2),
- bullet_demolition_range numeric(8,2)
- );
- raise notice 'Расчетные структуры сформированы';
- /*
- 4. Создание связей
- ==========================================
- */
- begin
-
- alter table public.measurment_baths
- add constraint emploee_id_fk
- foreign key (emploee_id)
- references public.employees (id);
-
- alter table public.measurment_baths
- add constraint measurment_input_param_id_fk
- foreign key(measurment_input_param_id)
- references public.measurment_input_params(id);
-
- alter table public.measurment_input_params
- add constraint measurment_type_id_fk
- foreign key(measurment_type_id)
- references public.measurment_types (id);
-
- alter table public.employees
- add constraint military_rank_id_fk
- foreign key(military_rank_id)
- references public.military_ranks (id);
- end;
- raise notice 'Связи сформированы';
- /*
- 4. Создает расчетные и вспомогательные функции
- ==========================================
- */
- -- Функция для расчета отклонения приземной виртуальной температуры
- drop function if exists public.fn_calc_header_temperature;
- create function public.fn_calc_header_temperature(
- par_temperature numeric(8,2))
- returns numeric(8,2)
- language 'plpgsql'
- as $BODY$
- declare
- default_temperature numeric(8,2) default 15.9;
- default_temperature_key character varying default 'calc_table_temperature' ;
- virtual_temperature numeric(8,2) default 0;
- deltaTv numeric(8,2) default 0;
- var_result numeric(8,2) default 0;
- begin
- raise notice 'Расчет отклонения приземной виртуальной температуры по температуре %', par_temperature;
- -- Определим табличное значение температуры
- Select coalesce(value::numeric(8,2), default_temperature)
- from public.measurment_settings
- into virtual_temperature
- where
- key = default_temperature_key;
- -- Вирутальная поправка
- deltaTv := par_temperature +
- public.fn_calc_temperature_interpolation(par_temperature => par_temperature);
-
- -- Отклонение приземной виртуальной температуры
- var_result := deltaTv - virtual_temperature;
-
- return var_result;
- end;
- $BODY$;
- -- Функция для формирования даты в специальном формате
- drop function if exists public.fn_calc_header_period;
- create function public.fn_calc_header_period(
- par_period timestamp with time zone)
- RETURNS text
- LANGUAGE 'sql'
- COST 100
- VOLATILE PARALLEL UNSAFE
- RETURN ((((CASE WHEN (EXTRACT(day FROM par_period) < (10)::numeric) THEN '0'::text ELSE ''::text END || (EXTRACT(day FROM par_period))::text) || CASE WHEN (EXTRACT(hour FROM par_period) < (10)::numeric) THEN '0'::text ELSE ''::text END) || (EXTRACT(hour FROM par_period))::text) || "left"(CASE WHEN (EXTRACT(minute FROM par_period) < (10)::numeric) THEN '0'::text ELSE (EXTRACT(minute FROM par_period))::text END, 1));
- -- Функция для расчета отклонения наземного давления
- drop function if exists public.fn_calc_header_pressure;
- create function public.fn_calc_header_pressure
- (
- par_pressure numeric(8,2))
- returns numeric(8,2)
- language 'plpgsql'
- as $body$
- declare
- default_pressure numeric(8,2) default 750;
- table_pressure numeric(8,2) default null;
- default_pressure_key character varying default 'calc_table_pressure' ;
- begin
- raise notice 'Расчет отклонения наземного давления для %', par_pressure;
-
- -- Определяем граничное табличное значение
- if not exists (select 1 from public.measurment_settings where key = default_pressure_key ) then
- Begin
- table_pressure := default_pressure;
- end;
- else
- begin
- select value::numeric(18,2)
- into table_pressure
- from public.measurment_settings where key = default_pressure_key;
- end;
- end if;
-
- -- Результат
- return par_pressure - coalesce(table_pressure,table_pressure) ;
- end;
- $body$;
- -- Функция для проверки входных параметров
- drop function if exists public.fn_check_input_params(numeric(8,2), numeric(8,2), numeric(8,2), numeric(8,2), numeric(8,2), numeric(8,2));
- create function public.fn_check_input_params(
- par_height numeric(8,2),
- par_temperature numeric(8,2),
- par_pressure numeric(8,2),
- par_wind_direction numeric(8,2),
- par_wind_speed numeric(8,2),
- par_bullet_demolition_range numeric(8,2)
- )
- returns public.input_params
- language 'plpgsql'
- as $body$
- declare
- var_result public.input_params;
- begin
-
- -- Температура
- if not exists (
- select 1 from (
- select
- coalesce(min_temperature , '0')::numeric(8,2) as min_temperature,
- coalesce(max_temperature, '0')::numeric(8,2) as max_temperature
- from
- (select 1 ) as t
- cross join
- ( select value as min_temperature from public.measurment_settings where key = 'min_temperature' ) as t1
- cross join
- ( select value as max_temperature from public.measurment_settings where key = 'max_temperature' ) as t2
- ) as t
- where
- par_temperature between min_temperature and max_temperature
- ) then
- raise exception 'Температура % не укладывает в диаппазон!', par_temperature;
- end if;
- var_result.temperature = par_temperature;
-
- -- Давление
- if not exists (
- select 1 from (
- select
- coalesce(min_pressure , '0')::numeric(8,2) as min_pressure,
- coalesce(max_pressure, '0')::numeric(8,2) as max_pressure
- from
- (select 1 ) as t
- cross join
- ( select value as min_pressure from public.measurment_settings where key = 'min_pressure' ) as t1
- cross join
- ( select value as max_pressure from public.measurment_settings where key = 'max_pressure' ) as t2
- ) as t
- where
- par_pressure between min_pressure and max_pressure
- ) then
- raise exception 'Давление % не укладывает в диаппазон!', par_pressure;
- end if;
- var_result.pressure = par_pressure;
- -- Высота
- if not exists (
- select 1 from (
- select
- coalesce(min_height , '0')::numeric(8,2) as min_height,
- coalesce(max_height, '0')::numeric(8,2) as max_height
- from
- (select 1 ) as t
- cross join
- ( select value as min_height from public.measurment_settings where key = 'min_height' ) as t1
- cross join
- ( select value as max_height from public.measurment_settings where key = 'max_height' ) as t2
- ) as t
- where
- par_height between min_height and max_height
- ) then
-
- raise exception 'Высота % не укладывает в диаппазон!', par_height;
- end if;
- var_result.height = par_height;
-
- -- Напрвление ветра
- if not exists (
- select 1 from (
- select
- coalesce(min_wind_direction , '0')::numeric(8,2) as min_wind_direction,
- coalesce(max_wind_direction, '0')::numeric(8,2) as max_wind_direction
- from
- (select 1 ) as t
- cross join
- ( select value as min_wind_direction from public.measurment_settings where key = 'min_wind_direction' ) as t1
- cross join
- ( select value as max_wind_direction from public.measurment_settings where key = 'max_wind_direction' ) as t2
- )
- where
- par_wind_direction between min_wind_direction and max_wind_direction
- ) then
- raise exception 'Направление ветра % не укладывает в диаппазон!', par_wind_direction;
- end if;
-
- var_result.wind_direction = par_wind_direction;
- var_result.wind_speed = par_wind_speed;
- return var_result;
-
- end;
- $body$;
- -- Функция для проверки параметров
- drop function if exists public.fn_check_input_params(input_params);
- create function public.fn_check_input_params(
- par_param input_params
- )
- returns public.input_params
- language 'plpgsql'
- as $body$
- declare
- var_result input_params;
- begin
- var_result := fn_check_input_params(
- par_param.height, par_param.temperature, par_param.pressure, par_param.wind_direction,
- par_param.wind_speed, par_param.bullet_demolition_range
- );
- return var_result;
-
- end ;
- $body$;
-
- -- Функция для расчета интерполяции
- drop function if exists public.fn_calc_temperature_interpolation;
- create function public.fn_calc_temperature_interpolation(
- par_temperature numeric(8,2))
- returns numeric
- language 'plpgsql'
- as $body$
- -- Расчет интерполяции
- declare
- var_interpolation interpolation_type;
- var_result numeric(8,2) default 0;
- var_min_temparure numeric(8,2) default 0;
- var_max_temperature numeric(8,2) default 0;
- var_denominator numeric(8,2) default 0;
- begin
- raise notice 'Расчет интерполяции для температуры %', par_temperature;
- -- Проверим, возможно температура совпадает со значением в справочнике
- if exists (select 1 from public.calc_temperatures_correction where temperature = par_temperature ) then
- begin
- select correction
- into var_result
- from public.calc_temperatures_correction
- where
- temperature = par_temperature;
- end;
- else
- begin
- -- Получим диапазон в котором работают поправки
- select min(temperature), max(temperature)
- into var_min_temparure, var_max_temperature
- from public.calc_temperatures_correction;
- if par_temperature < var_min_temparure or
- par_temperature > var_max_temperature then
- raise exception 'Некорректно передан параметр! Невозможно рассчитать поправку. Значение должно укладываться в диаппазон: %, %',
- var_min_temparure, var_max_temperature;
- end if;
- -- Получим граничные параметры
- select x0, y0, x1, y1
- into var_interpolation.x0, var_interpolation.y0, var_interpolation.x1, var_interpolation.y1
- from
- (
- select t1.temperature as x0, t1.correction as y0
- from public.calc_temperatures_correction as t1
- where t1.temperature <= par_temperature
- order by t1.temperature desc
- limit 1
- ) as leftPart
- cross join
- (
- select t1.temperature as x1, t1.correction as y1
- from public.calc_temperatures_correction as t1
- where t1.temperature >= par_temperature
- order by t1.temperature
- limit 1
- ) as rightPart;
- raise notice 'Граничные значения %', var_interpolation;
- -- Расчет поправки
- var_denominator := var_interpolation.x1 - var_interpolation.x0;
- if var_denominator = 0.0 then
- raise exception 'Деление на нуль. Возможно, некорректные данные в таблице с поправками!';
- end if;
- var_result := (par_temperature - var_interpolation.x0) * (var_interpolation.y1 - var_interpolation.y0) / var_denominator + var_interpolation.y0;
- end;
- end if;
- return var_result;
- end;
- $body$;
- -- Функция для генерации случайной даты
- drop function if exists fn_get_random_timestamp;
- create function fn_get_random_timestamp(
- par_min_value timestamp,
- par_max_value timestamp)
- returns timestamp
- language 'plpgsql'
- as $body$
- begin
- return random() * (par_max_value - par_min_value) + par_min_value;
- end;
- $body$;
- -- Функция для генерации случайного целого числа из диаппазона
- drop function if exists fn_get_randon_integer;
- create function fn_get_randon_integer(
- par_min_value integer,
- par_max_value integer
- )
- returns integer
- language 'plpgsql'
- as $body$
- begin
- return floor((par_max_value + 1 - par_min_value)*random())::integer + par_min_value;
- end;
- $body$;
- -- Функция для гнерации случайного текста
- drop function if exists fn_get_random_text;
- create function fn_get_random_text(
- par_length int,
- par_list_of_chars text DEFAULT 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюяABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789'
- )
- returns text
- language 'plpgsql'
- as $body$
- declare
- var_len_of_list integer default length(par_list_of_chars);
- var_position integer;
- var_result text = '';
- var_random_number integer;
- var_max_value integer;
- var_min_value integer;
- begin
- var_min_value := 10;
- var_max_value := 50;
-
- for var_position in 1 .. par_length loop
- -- добавляем к строке случайный символ
- var_random_number := fn_get_randon_integer(var_min_value, var_max_value );
- var_result := var_result || substr(par_list_of_chars, var_random_number ,1);
- end loop;
-
- return var_result;
-
- end;
- $body$;
- -- Функция для расчета метео приближенный
- drop function if exists fn_calc_header_meteo_avg;
- create function fn_calc_header_meteo_avg(
- par_params input_params
- )
- returns text
- language 'plpgsql'
- as $body$
- declare
- var_result text;
- var_params input_params;
- begin
- -- Проверяю аргументы
- var_params := public.fn_check_input_params(par_params);
-
- select
- -- Дата
- public.fn_calc_header_period(now()) ||
- --Высота расположения метеопоста над уровнем моря.
- lpad( 340::text, 4, '0' ) ||
- -- Отклонение наземного давления атмосферы
- lpad(
- case when coalesce(var_params.pressure,0) < 0 then
- '5'
- else ''
- end ||
- lpad ( abs(( coalesce(var_params.pressure, 0) )::int)::text,2,'0')
- , 3, '0') as "БББ",
- -- Отклонение приземной виртуальной температуры
- lpad(
- case when coalesce( var_params.temperature, 0) < 0 then
- '5'
- else
- ''
- end ||
- ( coalesce(var_params.temperature,0)::int)::text
- , 2,'0')
- into var_result;
- return var_result;
- end;
- $body$;
- raise notice 'Структура сформирована успешно';
- end $$;
- -- Проверка расчета
- do $$
- declare
- var_pressure_value numeric(8,2) default 0;
- var_temperature_value numeric(8,2) default 0;
-
- var_period text;
- var_pressure text;
- var_height text;
- var_temperature text;
- begin
- var_pressure_value := public.fn_calc_header_pressure(743);
- var_temperature_value := public.fn_calc_header_temperature(23);
-
- select
- -- Дата
- public.fn_calc_header_period(now()) as "ДДЧЧМ",
- --Высота расположения метеопоста над уровнем моря.
- lpad( 340::text, 4, '0' ) as "ВВВВ",
- -- Отклонение наземного давления атмосферы
- lpad(
- case when var_pressure_value < 0 then
- '5'
- else ''
- end ||
- lpad ( abs((var_pressure_value)::int)::text,2,'0')
- , 3, '0') as "БББ",
- -- Отклонение приземной виртуальной температуры
- lpad(
- case when var_temperature_value < 0 then
- '5'
- else
- ''
- end ||
- (var_temperature_value::int)::text
- , 2,'0') as "TT"
- into
- var_period, var_height, var_pressure, var_temperature;
- raise notice '==============================';
- raise notice 'Пример расчета метео приближенный';
- raise notice ' ДДЧЧМ %, ВВВВ %, БББ % , TT %', var_period, var_height, var_pressure, var_temperature;
-
- end $$;
- -- Генерация тестовых данных
- do $$
- declare
- var_position integer;
- var_emploee_ids integer[];
- var_emploee_quantity integer default 5;
- var_min_rank integer;
- var_max_rank integer;
- var_emploee_id integer;
- var_current_emploee_id integer;
- var_index integer;
- var_measure_type_id integer;
- var_measure_input_data_id integer;
- begin
- -- Определяем макс дипазон по должностям
- select min(id), max(id)
- into var_min_rank,var_max_rank
- from public.military_ranks;
-
- -- Формируем список пользователей
- for var_position in 1 .. var_emploee_quantity loop
- insert into public.employees(name, birthday, military_rank_id )
- select
- fn_get_random_text(25), -- name
- fn_get_random_timestamp('1978-01-01','2000-01-01'), -- birthday
- fn_get_randon_integer(var_min_rank, var_max_rank) -- military_rank_id
- ;
- select id into var_emploee_id from public.employees order by id desc limit 1;
- var_emploee_ids := var_emploee_ids || var_emploee_id;
- end loop;
- raise notice 'Сформированы тестовые пользователи %', var_emploee_ids;
- -- Формируем для каждого по 100 измерений
- foreach var_current_emploee_id in ARRAY var_emploee_ids LOOP
- for var_index in 1 .. 100 loop
- var_measure_type_id := fn_get_randon_integer(1,2);
-
- insert into public.measurment_input_params(measurment_type_id, height, temperature, pressure, wind_direction, wind_speed)
- select
- var_measure_type_id,
- fn_get_randon_integer(0,600)::numeric(8,2), -- height
- fn_get_randon_integer(0, 50)::numeric(8,2), -- temperature
- fn_get_randon_integer(500, 850)::numeric(8,2), -- pressure
- fn_get_randon_integer(0,59)::numeric(8,2), -- ind_direction
- fn_get_randon_integer(0,59)::numeric(8,2) -- wind_speed
- ;
- select id into var_measure_input_data_id from measurment_input_params order by id desc limit 1;
-
- insert into public.measurment_baths( emploee_id, measurment_input_param_id, started)
- select
- var_current_emploee_id,
- var_measure_input_data_id,
- fn_get_random_timestamp('2025-02-01 00:00', '2025-02-05 00:00')
- ;
- end loop;
-
- end loop;
- raise notice 'Набор тестовых данных сформирован успешно';
-
- end $$;
|