HomeWork20250214.sql 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  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. ('min_height','0','Минимальная высота'),
  121. ('max_height','400','Максимальная высота');
  122. raise notice 'Создание общих справочников и наполнение выполнено успешно';
  123. /*
  124. 3. Подготовка расчетных структур
  125. ==========================================
  126. */
  127. drop table if exists calc_temperatures_correction;
  128. create table calc_temperatures_correction
  129. (
  130. temperature numeric(8,2) primary key,
  131. correction numeric(8,2)
  132. );
  133. insert into public.calc_temperatures_correction(temperature, correction)
  134. Values(0, 0.5),(5, 0.5),(10, 1), (20,1), (25, 2), (30, 3.5), (40, 4.5);
  135. drop type if exists interpolation_type;
  136. create type interpolation_type as
  137. (
  138. x0 numeric(8,2),
  139. x1 numeric(8,2),
  140. y0 numeric(8,2),
  141. y1 numeric(8,2)
  142. );
  143. drop type if exists input_params cascade;
  144. create type input_params as
  145. (
  146. height numeric(8,2),
  147. temperature numeric(8,2),
  148. pressure numeric(8,2),
  149. wind_direction numeric(8,2),
  150. wind_speed numeric(8,2)
  151. );
  152. raise notice 'Расчетные структуры сформированы';
  153. /*
  154. 4. Создание связей
  155. ==========================================
  156. */
  157. begin
  158. alter table public.measurment_baths
  159. add constraint emploee_id_fk
  160. foreign key (emploee_id)
  161. references public.employees (id);
  162. alter table public.measurment_baths
  163. add constraint measurment_input_param_id_fk
  164. foreign key(measurment_input_param_id)
  165. references public.measurment_input_params(id);
  166. alter table public.measurment_input_params
  167. add constraint measurment_type_id_fk
  168. foreign key(measurment_type_id)
  169. references public.measurment_types (id);
  170. alter table public.employees
  171. add constraint military_rank_id_fk
  172. foreign key(military_rank_id)
  173. references public.military_ranks (id);
  174. end;
  175. raise notice 'Связи сформированы';
  176. /*
  177. 4. Создает расчетные и вспомогательные функции
  178. ==========================================
  179. */
  180. -- Функция для расчета отклонения приземной виртуальной температуры
  181. drop function if exists public.fn_calc_header_temperature;
  182. create function public.fn_calc_header_temperature(
  183. par_temperature numeric(8,2))
  184. returns numeric(8,2)
  185. language 'plpgsql'
  186. as $BODY$
  187. declare
  188. default_temperature numeric(8,2) default 15.9;
  189. default_temperature_key character varying default 'calc_table_temperature' ;
  190. virtual_temperature numeric(8,2) default 0;
  191. deltaTv numeric(8,2) default 0;
  192. var_result numeric(8,2) default 0;
  193. begin
  194. raise notice 'Расчет отклонения приземной виртуальной температуры по температуре %', par_temperature;
  195. -- Определим табличное значение температуры
  196. Select coalesce(value::numeric(8,2), default_temperature)
  197. from public.measurment_settings
  198. into virtual_temperature
  199. where
  200. key = default_temperature_key;
  201. -- Вирутальная поправка
  202. deltaTv := par_temperature +
  203. public.fn_calc_temperature_interpolation(par_temperature => par_temperature);
  204. -- Отклонение приземной виртуальной температуры
  205. var_result := deltaTv - virtual_temperature;
  206. return var_result;
  207. end;
  208. $BODY$;
  209. -- Функция для формирования даты в специальном формате
  210. drop function if exists public.fn_calc_header_period;
  211. create function public.fn_calc_header_period(
  212. par_period timestamp with time zone)
  213. RETURNS text
  214. LANGUAGE 'sql'
  215. COST 100
  216. VOLATILE PARALLEL UNSAFE
  217. 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));
  218. -- Функция для расчета отклонения наземного давления
  219. drop function if exists public.fn_calc_header_pressure;
  220. create function public.fn_calc_header_pressure
  221. (
  222. par_pressure numeric(8,2))
  223. returns numeric(8,2)
  224. language 'plpgsql'
  225. as $body$
  226. declare
  227. default_pressure numeric(8,2) default 750;
  228. table_pressure numeric(8,2) default null;
  229. default_pressure_key character varying default 'calc_table_pressure' ;
  230. begin
  231. raise notice 'Расчет отклонения наземного давления для %', par_pressure;
  232. -- Определяем граничное табличное значение
  233. if not exists (select 1 from public.measurment_settings where key = default_pressure_key ) then
  234. Begin
  235. table_pressure := default_pressure;
  236. end;
  237. else
  238. begin
  239. select value::numeric(18,2)
  240. into table_pressure
  241. from public.measurment_settings where key = default_pressure_key;
  242. end;
  243. end if;
  244. -- Результат
  245. return par_pressure - coalesce(table_pressure,table_pressure) ;
  246. end;
  247. $body$;
  248. -- Функция для проверки входных параметров
  249. drop function if exists public.fn_check_input_params;
  250. create function public.fn_check_input_params(
  251. par_height numeric(8,2),
  252. par_temperature numeric(8,2),
  253. par_pressure numeric(8,2),
  254. par_wind_direction numeric(8,2),
  255. par_wind_speed numeric(8,2),
  256. par_bullet_demolition_range numeric(8,2)
  257. )
  258. returns public.input_params
  259. language 'plpgsql'
  260. as $body$
  261. declare
  262. var_result public.input_params;
  263. begin
  264. -- Температура
  265. if not exists (
  266. select 1 from (
  267. select
  268. coalesce(min_temperature , '0')::numeric(8,2) as min_temperature,
  269. coalesce(max_temperature, '0')::numeric(8,2) as max_temperature
  270. from
  271. (select 1 ) as t
  272. cross join
  273. ( select value as min_temperature from public.measurment_settings where key = 'min_temperature' ) as t1
  274. cross join
  275. ( select value as max_temperature from public.measurment_settings where key = 'max_temperature' ) as t2
  276. ) as t
  277. where
  278. par_temperature between min_temperature and max_temperature
  279. ) then
  280. raise exception 'Температура % не укладывает в диаппазон!', par_temperature;
  281. end if;
  282. var_result.temperature = par_temperature;
  283. -- Давление
  284. if not exists (
  285. select 1 from (
  286. select
  287. coalesce(min_pressure , '0')::numeric(8,2) as min_pressure,
  288. coalesce(max_pressure, '0')::numeric(8,2) as max_pressure
  289. from
  290. (select 1 ) as t
  291. cross join
  292. ( select value as min_pressure from public.measurment_settings where key = 'min_pressure' ) as t1
  293. cross join
  294. ( select value as max_pressure from public.measurment_settings where key = 'max_pressure' ) as t2
  295. ) as t
  296. where
  297. par_pressure between min_pressure and max_pressure
  298. ) then
  299. raise exception 'Давление % не укладывает в диаппазон!', par_pressure;
  300. end if;
  301. var_result.pressure = par_pressure;
  302. -- Высота
  303. if not exists (
  304. select 1 from (
  305. select
  306. coalesce(min_height , '0')::numeric(8,2) as min_height,
  307. coalesce(max_height, '0')::numeric(8,2) as max_height
  308. from
  309. (select 1 ) as t
  310. cross join
  311. ( select value as min_height from public.measurment_settings where key = 'min_height' ) as t1
  312. cross join
  313. ( select value as max_height from public.measurment_settings where key = 'max_height' ) as t2
  314. ) as t
  315. where
  316. par_height between min_height and max_height
  317. ) then
  318. raise exception 'Высота % не укладывает в диаппазон!', par_height;
  319. end if;
  320. var_result.height = par_height;
  321. -- Напрвление ветра
  322. if not exists (
  323. select 1 from (
  324. select
  325. coalesce(min_wind_direction , '0')::numeric(8,2) as min_wind_direction,
  326. coalesce(max_wind_direction, '0')::numeric(8,2) as max_wind_direction
  327. from
  328. (select 1 ) as t
  329. cross join
  330. ( select value as min_wind_direction from public.measurment_settings where key = 'min_wind_direction' ) as t1
  331. cross join
  332. ( select value as max_wind_direction from public.measurment_settings where key = 'max_wind_direction' ) as t2
  333. )
  334. where
  335. par_wind_direction between min_wind_direction and max_wind_direction
  336. ) then
  337. raise exception 'Направление ветра % не укладывает в диаппазон!', par_wind_direction;
  338. end if;
  339. var_result.wind_direction = par_wind_direction;
  340. var_result.wind_speed = par_wind_speed;
  341. return var_result;
  342. end;
  343. $body$;
  344. -- Функция для расчета интерполяции
  345. drop function if exists public.fn_calc_temperature_interpolation;
  346. create function public.fn_calc_temperature_interpolation(
  347. par_temperature numeric(8,2))
  348. returns numeric
  349. language 'plpgsql'
  350. as $body$
  351. -- Расчет интерполяции
  352. declare
  353. var_interpolation interpolation_type;
  354. var_result numeric(8,2) default 0;
  355. var_min_temparure numeric(8,2) default 0;
  356. var_max_temperature numeric(8,2) default 0;
  357. var_denominator numeric(8,2) default 0;
  358. begin
  359. raise notice 'Расчет интерполяции для температуры %', par_temperature;
  360. -- Проверим, возможно температура совпадает со значением в справочнике
  361. if exists (select 1 from public.calc_temperatures_correction where temperature = par_temperature ) then
  362. begin
  363. select correction
  364. into var_result
  365. from public.calc_temperatures_correction
  366. where
  367. temperature = par_temperature;
  368. end;
  369. else
  370. begin
  371. -- Получим диапазон в котором работают поправки
  372. select min(temperature), max(temperature)
  373. into var_min_temparure, var_max_temperature
  374. from public.calc_temperatures_correction;
  375. if par_temperature < var_min_temparure or
  376. par_temperature > var_max_temperature then
  377. raise exception 'Некорректно передан параметр! Невозможно рассчитать поправку. Значение должно укладываться в диаппазон: %, %',
  378. var_min_temparure, var_max_temperature;
  379. end if;
  380. -- Получим граничные параметры
  381. select x0, y0, x1, y1
  382. into var_interpolation.x0, var_interpolation.y0, var_interpolation.x1, var_interpolation.y1
  383. from
  384. (
  385. select t1.temperature as x0, t1.correction as y0
  386. from public.calc_temperatures_correction as t1
  387. where t1.temperature <= par_temperature
  388. order by t1.temperature desc
  389. limit 1
  390. ) as leftPart
  391. cross join
  392. (
  393. select t1.temperature as x1, t1.correction as y1
  394. from public.calc_temperatures_correction as t1
  395. where t1.temperature >= par_temperature
  396. order by t1.temperature
  397. limit 1
  398. ) as rightPart;
  399. raise notice 'Граничные значения %', var_interpolation;
  400. -- Расчет поправки
  401. var_denominator := var_interpolation.x1 - var_interpolation.x0;
  402. if var_denominator = 0.0 then
  403. raise exception 'Деление на нуль. Возможно, некорректные данные в таблице с поправками!';
  404. end if;
  405. var_result := (par_temperature - var_interpolation.x0) * (var_interpolation.y1 - var_interpolation.y0) / var_denominator + var_interpolation.y0;
  406. end;
  407. end if;
  408. return var_result;
  409. end;
  410. $body$;
  411. -- Функция для генерации случайной даты
  412. drop function if exists fn_get_random_timestamp;
  413. create function fn_get_random_timestamp(
  414. par_min_value timestamp,
  415. par_max_value timestamp)
  416. returns timestamp
  417. language 'plpgsql'
  418. as $body$
  419. begin
  420. return random() * (par_max_value - par_min_value) + par_min_value;
  421. end;
  422. $body$;
  423. -- Функция для генерации случайного целого числа из диаппазона
  424. drop function if exists fn_get_randon_integer;
  425. create function fn_get_randon_integer(
  426. par_min_value integer,
  427. par_max_value integer
  428. )
  429. returns integer
  430. language 'plpgsql'
  431. as $body$
  432. begin
  433. return floor((par_max_value + 1 - par_min_value)*random())::integer + par_min_value;
  434. end;
  435. $body$;
  436. -- Функция для гнерации случайного текста
  437. drop function if exists fn_get_random_text;
  438. create function fn_get_random_text(
  439. par_length int,
  440. par_list_of_chars text DEFAULT 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюяABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789'
  441. )
  442. returns text
  443. language 'plpgsql'
  444. as $body$
  445. declare
  446. var_len_of_list integer default length(par_list_of_chars);
  447. var_position integer;
  448. var_result text = '';
  449. var_random_number integer;
  450. var_max_value integer;
  451. var_min_value integer;
  452. begin
  453. var_min_value := 10;
  454. var_max_value := 50;
  455. for var_position in 1 .. par_length loop
  456. -- добавляем к строке случайный символ
  457. var_random_number := fn_get_randon_integer(var_min_value, var_max_value );
  458. var_result := var_result || substr(par_list_of_chars, var_random_number ,1);
  459. end loop;
  460. return var_result;
  461. end;
  462. $body$;
  463. raise notice 'Структура сформирована успешно';
  464. end $$;
  465. -- Проверка расчета
  466. do $$
  467. declare
  468. var_pressure_value numeric(8,2) default 0;
  469. var_temperature_value numeric(8,2) default 0;
  470. var_period text;
  471. var_pressure text;
  472. var_height text;
  473. var_temperature text;
  474. begin
  475. var_pressure_value := public.fn_calc_header_pressure(743);
  476. var_temperature_value := public.fn_calc_header_temperature(23);
  477. select
  478. -- Дата
  479. public.fn_calc_header_period(now()) as "ДДЧЧМ",
  480. --Высота расположения метеопоста над уровнем моря.
  481. lpad( 340::text, 4, '0' ) as "ВВВВ",
  482. -- Отклонение наземного давления атмосферы
  483. lpad(
  484. case when var_pressure_value < 0 then
  485. '5'
  486. else ''
  487. end ||
  488. lpad ( abs((var_pressure_value)::int)::text,2,'0')
  489. , 3, '0') as "БББ",
  490. -- Отклонение приземной виртуальной температуры
  491. lpad(
  492. case when var_temperature_value < 0 then
  493. '5'
  494. else
  495. ''
  496. end ||
  497. (var_temperature_value::int)::text
  498. , 2,'0') as "TT"
  499. into
  500. var_period, var_height, var_pressure, var_temperature;
  501. raise notice '==============================';
  502. raise notice 'Пример расчета метео приближенный';
  503. raise notice ' ДДЧЧМ %, ВВВВ %, БББ % , TT %', var_period, var_height, var_pressure, var_temperature;
  504. end $$;
  505. -- Генерация тестовых данных
  506. do $$
  507. declare
  508. var_position integer;
  509. var_emploee_ids integer[];
  510. var_emploee_quantity integer default 5;
  511. var_min_rank integer;
  512. var_max_rank integer;
  513. var_emploee_id integer;
  514. var_current_emploee_id integer;
  515. var_index integer;
  516. var_measure_type_id integer;
  517. var_measure_input_data_id integer;
  518. begin
  519. -- Определяем макс дипазон по должностям
  520. select min(id), max(id)
  521. into var_min_rank,var_max_rank
  522. from public.military_ranks;
  523. -- Формируем список пользователей
  524. for var_position in 1 .. var_emploee_quantity loop
  525. insert into public.employees(name, birthday, military_rank_id )
  526. select
  527. fn_get_random_text(25), -- name
  528. fn_get_random_timestamp('1978-01-01','2000-01-01'), -- birthday
  529. fn_get_randon_integer(var_min_rank, var_max_rank) -- military_rank_id
  530. ;
  531. select id into var_emploee_id from public.employees order by id desc limit 1;
  532. var_emploee_ids := var_emploee_ids || var_emploee_id;
  533. end loop;
  534. raise notice 'Сформированы тестовые пользователи %', var_emploee_ids;
  535. -- Формируем для каждого по 100 измерений
  536. foreach var_current_emploee_id in ARRAY var_emploee_ids LOOP
  537. for var_index in 1 .. 100 loop
  538. var_measure_type_id := fn_get_randon_integer(1,2);
  539. insert into public.measurment_input_params(measurment_type_id, height, temperature, pressure, wind_direction, wind_speed)
  540. select
  541. var_measure_type_id,
  542. fn_get_randon_integer(0,600)::numeric(8,2), -- height
  543. fn_get_randon_integer(0, 50)::numeric(8,2), -- temperature
  544. fn_get_randon_integer(500, 850)::numeric(8,2), -- pressure
  545. fn_get_randon_integer(0,59)::numeric(8,2), -- ind_direction
  546. fn_get_randon_integer(0,59)::numeric(8,2) -- wind_speed
  547. ;
  548. select id into var_measure_input_data_id from measurment_input_params order by id desc limit 1;
  549. insert into public.measurment_baths( emploee_id, measurment_input_param_id, started)
  550. select
  551. var_current_emploee_id,
  552. var_measure_input_data_id,
  553. fn_get_random_timestamp('2025-02-01 00:00', '2025-02-05 00:00')
  554. ;
  555. end loop;
  556. end loop;
  557. raise notice 'Набор тестовых данных сформирован успешно';
  558. end $$;
  559. -- Проверки
  560. select * from public.measurment_input_params;
  561. select public.fn_check_input_params(
  562. 0, 0, 0, 0, 0, 0);