HomeWork20250220.sql 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. do $$
  2. begin
  3. /*
  4. Скрипт создания информационной базы данных
  5. Согласно технического задания https://git.hostfl.ru/VolovikovAlex/Study2025
  6. Редакция 2025-02-12
  7. Edit by valex
  8. */
  9. /*
  10. 1. Удаляем старые элементы
  11. ======================================
  12. */
  13. raise notice 'Запускаем создание новой структуры базы данных meteo';
  14. begin
  15. -- Связи
  16. alter table if exists public.measurment_input_params
  17. drop constraint if exists measurment_type_id_fk;
  18. alter table if exists public.employees
  19. drop constraint if exists military_rank_id_fk;
  20. alter table if exists public.measurment_baths
  21. drop constraint if exists measurment_input_param_id_fk;
  22. alter table if exists public.measurment_baths
  23. drop constraint if exists emploee_id_fk;
  24. -- Таблицы
  25. drop table if exists public.measurment_input_params;
  26. drop table if exists public.measurment_baths;
  27. drop table if exists public.employees;
  28. drop table if exists public.measurment_types;
  29. drop table if exists public.military_ranks;
  30. drop table if exists public.measurment_settings;
  31. -- Нумераторы
  32. drop sequence if exists public.measurment_input_params_seq;
  33. drop sequence if exists public.measurment_baths_seq;
  34. drop sequence if exists public.employees_seq;
  35. drop sequence if exists public.military_ranks_seq;
  36. drop sequence if exists public.measurment_types_seq;
  37. end;
  38. raise notice 'Удаление старых данных выполнено успешно';
  39. /*
  40. 2. Добавляем структуры данных
  41. ================================================
  42. */
  43. -- Справочник должностей
  44. create table military_ranks
  45. (
  46. id integer primary key not null,
  47. description character varying(255)
  48. );
  49. insert into military_ranks(id, description)
  50. values(1,'Рядовой'),(2,'Лейтенант');
  51. create sequence military_ranks_seq start 3;
  52. alter table military_ranks alter column id set default nextval('public.military_ranks_seq');
  53. -- Пользователя
  54. create table employees
  55. (
  56. id integer primary key not null,
  57. name text,
  58. birthday timestamp ,
  59. military_rank_id integer
  60. );
  61. insert into employees(id, name, birthday,military_rank_id )
  62. values(1, 'Воловиков Александр Сергеевич','1978-06-24', 2);
  63. create sequence employees_seq start 2;
  64. alter table employees alter column id set default nextval('public.employees_seq');
  65. -- Устройства для измерения
  66. create table measurment_types
  67. (
  68. id integer primary key not null,
  69. short_name character varying(50),
  70. description text
  71. );
  72. insert into measurment_types(id, short_name, description)
  73. values(1, 'ДМК', 'Десантный метео комплекс'),
  74. (2,'ВР','Ветровое ружье');
  75. create sequence measurment_types_seq start 3;
  76. alter table measurment_types alter column id set default nextval('public.measurment_types_seq');
  77. -- Таблица с параметрами
  78. create table measurment_input_params
  79. (
  80. id integer primary key not null,
  81. measurment_type_id integer not null,
  82. height numeric(8,2) default 0,
  83. temperature numeric(8,2) default 0,
  84. pressure numeric(8,2) default 0,
  85. wind_direction numeric(8,2) default 0,
  86. wind_speed numeric(8,2) default 0
  87. );
  88. insert into measurment_input_params(id, measurment_type_id, height, temperature, pressure, wind_direction,wind_speed )
  89. values(1, 1, 100,12,34,0.2,45);
  90. create sequence measurment_input_params_seq start 2;
  91. alter table measurment_input_params alter column id set default nextval('public.measurment_input_params_seq');
  92. -- Таблица с историей
  93. create table measurment_baths
  94. (
  95. id integer primary key not null,
  96. emploee_id integer not null,
  97. measurment_input_param_id integer not null,
  98. started timestamp default now()
  99. );
  100. insert into measurment_baths(id, emploee_id, measurment_input_param_id)
  101. values(1, 1, 1);
  102. create sequence measurment_baths_seq start 2;
  103. alter table measurment_baths alter column id set default nextval('public.measurment_baths_seq');
  104. -- Таблица с настройками
  105. create table measurment_settings
  106. (
  107. key character varying(100) primary key not null,
  108. value character varying(255) ,
  109. description text
  110. );
  111. insert into measurment_settings(key, value, description)
  112. values('min_temperature', '-10', 'Минимальное значение температуры'),
  113. ('max_temperature', '50', 'Максимальное значение температуры'),
  114. ('min_pressure','500','Минимальное значение давления'),
  115. ('max_pressure','900','Максимальное значение давления'),
  116. ('min_wind_direction','0','Минимальное значение направления ветра'),
  117. ('max_wind_direction','59','Максимальное значение направления ветра'),
  118. ('calc_table_temperature','15.9','Табличное значение температуры'),
  119. ('calc_table_pressure','750','Табличное значение наземного давления');
  120. raise notice 'Создание общих справочников и наполнение выполнено успешно';
  121. /*
  122. 3. Подготовка расчетных структур
  123. ==========================================
  124. */
  125. drop table if exists calc_temperatures_correction;
  126. create table calc_temperatures_correction
  127. (
  128. temperature numeric(8,2) primary key,
  129. correction numeric(8,2)
  130. );
  131. insert into public.calc_temperatures_correction(temperature, correction)
  132. Values(0, 0.5),(5, 0.5),(10, 1), (20,1), (25, 2), (30, 3.5), (40, 4.5);
  133. drop type if exists interpolation_type;
  134. create type interpolation_type as
  135. (
  136. x0 numeric(8,2),
  137. x1 numeric(8,2),
  138. y0 numeric(8,2),
  139. y1 numeric(8,2)
  140. );
  141. drop type if exists input_params;
  142. create type input_params as
  143. (
  144. height numeric(8,2),
  145. temperature numeric(8,2),
  146. pressure numeric(8,2),
  147. wind_direction numeric(8,2),
  148. wind_speed numeric(8,2)
  149. );
  150. raise notice 'Расчетные структуры сформированы';
  151. /*
  152. 4. Создание связей
  153. ==========================================
  154. */
  155. begin
  156. alter table public.measurment_baths
  157. add constraint emploee_id_fk
  158. foreign key (emploee_id)
  159. references public.employees (id);
  160. alter table public.measurment_baths
  161. add constraint measurment_input_param_id_fk
  162. foreign key(measurment_input_param_id)
  163. references public.measurment_input_params(id);
  164. alter table public.measurment_input_params
  165. add constraint measurment_type_id_fk
  166. foreign key(measurment_type_id)
  167. references public.measurment_types (id);
  168. alter table public.employees
  169. add constraint military_rank_id_fk
  170. foreign key(military_rank_id)
  171. references public.military_ranks (id);
  172. end;
  173. raise notice 'Связи сформированы';
  174. /*
  175. 4. Создает расчетные и вспомогательные функции
  176. ==========================================
  177. */
  178. drop function if exists public.fn_calc_header_temperature;
  179. create function public.fn_calc_header_temperature(
  180. par_temperature numeric(8,2))
  181. returns numeric(8,2)
  182. language 'plpgsql'
  183. as $BODY$
  184. declare
  185. default_temperature numeric(8,2) default 15.9;
  186. default_temperature_key character varying default 'calc_table_temperature' ;
  187. virtual_temperature numeric(8,2) default 0;
  188. deltaTv numeric(8,2) default 0;
  189. var_result numeric(8,2) default 0;
  190. begin
  191. raise notice 'Расчет отклонения приземной виртуальной температуры по температуре %', par_temperature;
  192. -- Определим табличное значение температуры
  193. Select coalesce(value::numeric(8,2), default_temperature)
  194. from public.measurment_settings
  195. into virtual_temperature
  196. where
  197. key = default_temperature_key;
  198. -- Вирутальная поправка
  199. deltaTv := par_temperature +
  200. public.fn_calc_temperature_interpolation(par_temperature => par_temperature);
  201. -- Отклонение приземной виртуальной температуры
  202. var_result := deltaTv - virtual_temperature;
  203. return var_result;
  204. end;
  205. $BODY$;
  206. drop function if exists public.fn_calc_header_period;
  207. create function public.fn_calc_header_period(
  208. par_period timestamp with time zone)
  209. RETURNS text
  210. LANGUAGE 'sql'
  211. COST 100
  212. VOLATILE PARALLEL UNSAFE
  213. 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));
  214. drop function if exists public.fn_calc_header_pressure;
  215. create function public.fn_calc_header_pressure
  216. (
  217. par_pressure numeric(8,2))
  218. returns numeric(8,2)
  219. language 'plpgsql'
  220. as $body$
  221. declare
  222. default_pressure numeric(8,2) default 750;
  223. table_pressure numeric(8,2) default null;
  224. default_pressure_key character varying default 'calc_table_pressure' ;
  225. begin
  226. raise notice 'Расчет отклонения наземного давления для %', par_pressure;
  227. -- Определяем граничное табличное значение
  228. if not exists (select 1 from public.measurment_settings where key = default_pressure_key ) then
  229. Begin
  230. table_pressure := default_pressure;
  231. end;
  232. else
  233. begin
  234. select value::numeric(18,2)
  235. into table_pressure
  236. from public.measurment_settings where key = default_pressure_key;
  237. end;
  238. end if;
  239. -- Результат
  240. return par_pressure - coalesce(table_pressure,table_pressure) ;
  241. end;
  242. $body$;
  243. drop function if exists public.fn_calc_temperature_interpolation;
  244. create function public.fn_calc_temperature_interpolation(
  245. par_temperature numeric(8,2))
  246. returns numeric
  247. language 'plpgsql'
  248. as $body$
  249. -- Расчет интерполяции
  250. declare
  251. var_interpolation interpolation_type;
  252. var_result numeric(8,2) default 0;
  253. var_min_temparure numeric(8,2) default 0;
  254. var_max_temperature numeric(8,2) default 0;
  255. var_denominator numeric(8,2) default 0;
  256. begin
  257. raise notice 'Расчет интерполяции для температуры %', par_temperature;
  258. -- Проверим, возможно температура совпадает со значением в справочнике
  259. if exists (select 1 from public.calc_temperatures_correction where temperature = par_temperature ) then
  260. begin
  261. select correction
  262. into var_result
  263. from public.calc_temperatures_correction
  264. where
  265. temperature = par_temperature;
  266. end;
  267. else
  268. begin
  269. -- Получим диапазон в котором работают поправки
  270. select min(temperature), max(temperature)
  271. into var_min_temparure, var_max_temperature
  272. from public.calc_temperatures_correction;
  273. if par_temperature < var_min_temparure or
  274. par_temperature > var_max_temperature then
  275. raise exception 'Некорректно передан параметр! Невозможно рассчитать поправку. Значение должно укладываться в диаппазон: %, %',
  276. var_min_temparure, var_max_temperature;
  277. end if;
  278. -- Получим граничные параметры
  279. select x0, y0, x1, y1
  280. into var_interpolation.x0, var_interpolation.y0, var_interpolation.x1, var_interpolation.y1
  281. from
  282. (
  283. select t1.temperature as x0, t1.correction as y0
  284. from public.calc_temperatures_correction as t1
  285. where t1.temperature <= par_temperature
  286. order by t1.temperature desc
  287. limit 1
  288. ) as leftPart
  289. cross join
  290. (
  291. select t1.temperature as x1, t1.correction as y1
  292. from public.calc_temperatures_correction as t1
  293. where t1.temperature >= par_temperature
  294. order by t1.temperature
  295. limit 1
  296. ) as rightPart;
  297. raise notice 'Граничные значения %', var_interpolation;
  298. -- Расчет поправки
  299. var_denominator := var_interpolation.x1 - var_interpolation.x0;
  300. if var_denominator = 0.0 then
  301. raise exception 'Деление на нуль. Возможно, некорректные данные в таблице с поправками!';
  302. end if;
  303. var_result := (par_temperature - var_interpolation.x0) * (var_interpolation.y1 - var_interpolation.y0) / var_denominator + var_interpolation.y0;
  304. end;
  305. end if;
  306. return var_result;
  307. end;
  308. $body$;
  309. -- Функция для генерации случайной даты
  310. drop function if exists fn_get_random_timestamp;
  311. create function fn_get_random_timestamp(
  312. par_min_value timestamp,
  313. par_max_value timestamp)
  314. returns timestamp
  315. language 'plpgsql'
  316. as $body$
  317. begin
  318. return random() * (par_max_value - par_min_value) + par_min_value;
  319. end;
  320. $body$;
  321. -- Функция для генерации случайного целого числа из диаппазона
  322. drop function if exists fn_get_randon_integer;
  323. create function fn_get_randon_integer(
  324. par_min_value integer,
  325. par_max_value integer
  326. )
  327. returns integer
  328. language 'plpgsql'
  329. as $body$
  330. begin
  331. return floor((par_max_value + 1 - par_min_value)*random())::integer + par_min_value;
  332. end;
  333. $body$;
  334. -- Функция для гнерации случайного текста
  335. drop function if exists fn_get_random_text;
  336. create function fn_get_random_text(
  337. par_length int,
  338. par_list_of_chars text DEFAULT 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюяABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789'
  339. )
  340. returns text
  341. language 'plpgsql'
  342. as $body$
  343. declare
  344. var_len_of_list integer default length(par_list_of_chars);
  345. var_position integer;
  346. var_result text = '';
  347. var_random_number integer;
  348. var_max_value integer;
  349. var_min_value integer;
  350. begin
  351. var_min_value := 10;
  352. var_max_value := 50;
  353. for var_position in 1 .. par_length loop
  354. -- добавляем к строке случайный символ
  355. var_random_number := fn_get_randon_integer(var_min_value, var_max_value );
  356. var_result := var_result || substr(par_list_of_chars, var_random_number ,1);
  357. end loop;
  358. return var_result;
  359. end;
  360. $body$;
  361. raise notice 'Структура сформирована успешно';
  362. end $$;
  363. -- Проверка расчета
  364. do $$
  365. declare
  366. var_pressure_value numeric(8,2) default 0;
  367. var_temperature_value numeric(8,2) default 0;
  368. var_period text;
  369. var_pressure text;
  370. var_height text;
  371. var_temperature text;
  372. begin
  373. var_pressure_value := public.fn_calc_header_pressure(743);
  374. var_temperature_value := public.fn_calc_header_temperature(23);
  375. select
  376. -- Дата
  377. public.fn_calc_header_period(now()) as "ДДЧЧМ",
  378. --Высота расположения метеопоста над уровнем моря.
  379. lpad( 340::text, 4, '0' ) as "ВВВВ",
  380. -- Отклонение наземного давления атмосферы
  381. lpad(
  382. case when var_pressure_value < 0 then
  383. '5'
  384. else ''
  385. end ||
  386. lpad ( abs((var_pressure_value)::int)::text,2,'0')
  387. , 3, '0') as "БББ",
  388. -- Отклонение приземной виртуальной температуры
  389. lpad(
  390. case when var_temperature_value < 0 then
  391. '5'
  392. else
  393. ''
  394. end ||
  395. (var_temperature_value::int)::text
  396. , 2,'0') as "TT"
  397. into
  398. var_period, var_height, var_pressure, var_temperature;
  399. raise notice '==============================';
  400. raise notice 'Пример расчета метео приближенный';
  401. raise notice ' ДДЧЧМ %, ВВВВ %, БББ % , TT %', var_period, var_height, var_pressure, var_temperature;
  402. end $$;
  403. -- Генерация тестовых данных
  404. do $$
  405. declare
  406. var_position integer;
  407. var_emploee_ids integer[];
  408. var_emploee_quantity integer default 5;
  409. var_min_rank integer;
  410. var_max_rank integer;
  411. var_emploee_id integer;
  412. var_current_emploee_id integer;
  413. var_index integer;
  414. var_measure_type_id integer;
  415. var_measure_input_data_id integer;
  416. begin
  417. -- Определяем макс дипазон по должностям
  418. select min(id), max(id)
  419. into var_min_rank,var_max_rank
  420. from public.military_ranks;
  421. -- Формируем список пользователей
  422. for var_position in 1 .. var_emploee_quantity loop
  423. insert into public.employees(name, birthday, military_rank_id )
  424. select
  425. fn_get_random_text(25), -- name
  426. fn_get_random_timestamp('1978-01-01','2000-01-01'), -- birthday
  427. fn_get_randon_integer(var_min_rank, var_max_rank) -- military_rank_id
  428. ;
  429. select id into var_emploee_id from public.employees order by id desc limit 1;
  430. var_emploee_ids := var_emploee_ids || var_emploee_id;
  431. end loop;
  432. raise notice 'Сформированы тестовые пользователи %', var_emploee_ids;
  433. -- Формируем для каждого по 100 измерений
  434. foreach var_current_emploee_id in ARRAY var_emploee_ids LOOP
  435. for var_index in 1 .. 100 loop
  436. var_measure_type_id := fn_get_randon_integer(1,2);
  437. insert into public.measurment_input_params(measurment_type_id, height, temperature, pressure, wind_direction, wind_speed)
  438. select
  439. var_measure_type_id,
  440. fn_get_randon_integer(0,600)::numeric(8,2), -- height
  441. fn_get_randon_integer(0, 50)::numeric(8,2), -- temperature
  442. fn_get_randon_integer(500, 850)::numeric(8,2), -- pressure
  443. fn_get_randon_integer(0,59)::numeric(8,2), -- ind_direction
  444. fn_get_randon_integer(0,59)::numeric(8,2) -- wind_speed
  445. ;
  446. select id into var_measure_input_data_id from measurment_input_params order by id desc limit 1;
  447. insert into public.measurment_baths( emploee_id, measurment_input_param_id, started)
  448. select
  449. var_current_emploee_id,
  450. var_measure_input_data_id,
  451. fn_get_random_timestamp('2025-02-01 00:00', '2025-02-05 00:00')
  452. ;
  453. end loop;
  454. end loop;
  455. raise notice 'Набор тестовых данных сформирован успешно';
  456. end $$;
  457. -- Проверки
  458. select * from public.measurment_input_params;