HomeWork20250307.sql 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297
  1. do $$
  2. begin
  3. /*
  4. Скрипт создания информационной базы данных
  5. Согласно технического задания https://git.hostfl.ru/VolovikovAlex/Study2025
  6. Редакция 2025-03-07
  7. Edit by valex
  8. */
  9. /*
  10. 1. Удаляем старые элементы
  11. ======================================
  12. */
  13. drop view if exists vw_report_fails_statistics;
  14. drop view if exists vw_report_fails_height_statistics;
  15. raise notice 'Запускаем создание новой структуры базы данных meteo';
  16. begin
  17. -- Связи
  18. -- Основные таблицы
  19. alter table if exists public.measurment_input_params
  20. drop constraint if exists measurment_type_id_fk;
  21. alter table if exists public.employees
  22. drop constraint if exists military_rank_id_fk;
  23. alter table if exists public.measurment_baths
  24. drop constraint if exists measurment_input_param_id_fk;
  25. alter table if exists public.measurment_baths
  26. drop constraint if exists emploee_id_fk;
  27. -- Рассчетные
  28. alter table if exists public.calc_correction_types
  29. drop constraint if exists calc_correction_types_measurment_type_id_fk;
  30. alter table if exists public.calc_corrections
  31. drop constraint if exists calc_corrections_calc_height_id_fk;
  32. alter table if exists public.calc_corrections
  33. drop constraint if exists calc_corrections_calc_correction_type_id_fk;
  34. alter table if exists public.calc_correction_types
  35. drop constraint if exists calc_correction_types_parent_id_fk;
  36. -- Таблицы
  37. -- Основные данные
  38. drop table if exists public.measurment_input_params;
  39. drop table if exists public.measurment_baths;
  40. drop table if exists public.employees;
  41. drop table if exists public.measurment_types;
  42. drop table if exists public.military_ranks;
  43. drop table if exists public.measurment_settings;
  44. -- Расчетные данные
  45. drop table if exists public.calc_corrections_temperature;
  46. drop table if exists public.calc_correction_types;
  47. drop table if exists public.calc_corrections;
  48. drop table if exists public.calc_corrections_height;
  49. -- Нумераторы
  50. -- Основные нумераторы
  51. drop sequence if exists public.measurment_input_params_seq cascade;
  52. drop sequence if exists public.measurment_baths_seq cascade;
  53. drop sequence if exists public.employees_seq cascade;
  54. drop sequence if exists public.military_ranks_seq cascade;
  55. drop sequence if exists public.measurment_types_seq cascade;
  56. -- Расчетные нумераторы
  57. drop sequence if exists public.calc_corrections_temperature_seq cascade;
  58. drop sequence if exists public.calc_correction_types_seq cascade;
  59. drop sequence if exists public.calc_corrections_seq cascade;
  60. drop sequence if exists public.calc_corrections_height_seq cascade;
  61. end;
  62. raise notice 'Удаление старых данных выполнено успешно';
  63. /*
  64. 2. Добавляем структуры данных
  65. ================================================
  66. */
  67. -- Справочник должностей
  68. create table military_ranks
  69. (
  70. id integer primary key not null,
  71. description character varying(255)
  72. );
  73. insert into military_ranks(id, description)
  74. values(1,'Рядовой'),(2,'Лейтенант');
  75. create sequence military_ranks_seq start 3;
  76. alter table military_ranks alter column id set default nextval('public.military_ranks_seq');
  77. -- Пользователя
  78. create table employees
  79. (
  80. id integer primary key not null,
  81. name text,
  82. birthday timestamp ,
  83. military_rank_id integer not null
  84. );
  85. insert into employees(id, name, birthday,military_rank_id )
  86. values(1, 'Воловиков Александр Сергеевич','1978-06-24', 2);
  87. create sequence employees_seq start 2;
  88. alter table employees alter column id set default nextval('public.employees_seq');
  89. -- Устройства для измерения
  90. create table measurment_types
  91. (
  92. id integer primary key not null,
  93. short_name character varying(50),
  94. description text
  95. );
  96. insert into measurment_types(id, short_name, description)
  97. values(1, 'ДМК', 'Десантный метео комплекс'),
  98. (2,'ВР','Ветровое ружье');
  99. create sequence measurment_types_seq start 3;
  100. alter table measurment_types alter column id set default nextval('public.measurment_types_seq');
  101. -- Таблица с параметрами
  102. create table measurment_input_params
  103. (
  104. id integer primary key not null,
  105. measurment_type_id integer not null,
  106. height numeric(8,2) default 0,
  107. temperature numeric(8,2) default 0,
  108. pressure numeric(8,2) default 0,
  109. wind_direction numeric(8,2) default 0,
  110. wind_speed numeric(8,2) default 0,
  111. bullet_demolition_range numeric(8,2) default 0
  112. );
  113. insert into measurment_input_params(id, measurment_type_id, height, temperature, pressure, wind_direction,wind_speed )
  114. values(1, 1, 100,12,34,0.2,45);
  115. create sequence measurment_input_params_seq start 2;
  116. alter table measurment_input_params alter column id set default nextval('public.measurment_input_params_seq');
  117. -- Таблица с историей
  118. create table measurment_baths
  119. (
  120. id integer primary key not null,
  121. emploee_id integer not null,
  122. measurment_input_param_id integer not null,
  123. started timestamp default now()
  124. );
  125. insert into measurment_baths(id, emploee_id, measurment_input_param_id)
  126. values(1, 1, 1);
  127. create sequence measurment_baths_seq start 2;
  128. alter table measurment_baths alter column id set default nextval('public.measurment_baths_seq');
  129. -- Таблица с настройками
  130. create table measurment_settings
  131. (
  132. key character varying(100) primary key not null,
  133. value character varying(255) ,
  134. description text
  135. );
  136. insert into measurment_settings(key, value, description)
  137. values('min_temperature', '-10', 'Минимальное значение температуры'),
  138. ('max_temperature', '50', 'Максимальное значение температуры'),
  139. ('min_pressure','500','Минимальное значение давления'),
  140. ('max_pressure','900','Максимальное значение давления'),
  141. ('min_wind_direction','0','Минимальное значение направления ветра'),
  142. ('max_wind_direction','59','Максимальное значение направления ветра'),
  143. ('calc_table_temperature','15.9','Табличное значение температуры'),
  144. ('calc_table_pressure','750','Табличное значение наземного давления'),
  145. ('min_height','0','Минимальная высота'),
  146. ('max_height','400','Максимальная высота');
  147. raise notice 'Создание общих справочников и наполнение выполнено успешно';
  148. /*
  149. 3. Подготовка расчетных структур
  150. ==========================================
  151. */
  152. create table calc_corrections_temperature
  153. (
  154. temperature numeric(8,2) not null primary key,
  155. correction numeric(8,2) not null
  156. );
  157. insert into public.calc_corrections_temperature(temperature, correction)
  158. Values(0, 0.5),(5, 0.5),(10, 1), (20,1), (25, 2), (30, 3.5), (40, 4.5);
  159. drop type if exists interpolation_type cascade;
  160. create type interpolation_type as
  161. (
  162. x0 numeric(8,2),
  163. x1 numeric(8,2),
  164. y0 numeric(8,2),
  165. y1 numeric(8,2)
  166. );
  167. -- Тип для входных параметров
  168. drop type if exists input_params_type cascade;
  169. create type input_params_type as
  170. (
  171. height numeric(8,2),
  172. temperature numeric(8,2),
  173. pressure numeric(8,2),
  174. wind_direction numeric(8,2),
  175. wind_speed numeric(8,2),
  176. bullet_demolition_range numeric(8,2)
  177. );
  178. -- Тип с результатами проверки
  179. drop type if exists check_result_type cascade;
  180. create type check_result_type as
  181. (
  182. is_check boolean,
  183. error_message text,
  184. params input_params_type
  185. );
  186. -- Тип с результатами расчета
  187. drop type if exists calc_result_type cascade;
  188. create type calc_result_type as
  189. (
  190. -- Тип оборудования
  191. measurement_type_id integer,
  192. -- Высота
  193. height integer,
  194. -- Отклонение наземного давления атмосферы
  195. deltaPressure numeric(8,2),
  196. -- Отклонение приземной виртуальной температуры
  197. deltaTemperature numeric(8,2),
  198. -- Среднее отклонения температуры воздуха
  199. deviationTemperature numeric(8,2),
  200. -- Скорость среднего ветра
  201. deviationWind numeric(8,2),
  202. -- Направление среднего ветра
  203. deviationWindDirection numeric(8,2)
  204. );
  205. -- Таблица с типами таблиц корректировок и заголовками
  206. create sequence calc_correction_types_seq;
  207. create table calc_correction_types
  208. (
  209. id integer not null primary key default nextval('public.calc_correction_types_seq'),
  210. parent_id integer,
  211. measurment_type_id integer,
  212. description text not null,
  213. values integer[]
  214. );
  215. insert into calc_correction_types( description, values)
  216. values( 'Таблица 2', array[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50]), -- 1
  217. ( 'Таблица 3, скорость среднего ветра', null), -- 2
  218. ( 'Таблица 3, направление среднего ветра', null); -- 3
  219. insert into calc_correction_types( description, parent_id, measurment_type_id, values)
  220. values
  221. ('Таблица 3, Скорость среднего ветра (ВР)', 2, 2, array[40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150]), -- 4
  222. ('Таблица 3, Направление среднего ветра (ВР)',3, 2, array[40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150]), -- 5
  223. ('Таблица 3,Скорость среднего ветра (ДМК)',2, 1, array[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]), -- 6
  224. ('Таблица 3, Направление среднего ветра (ДМК)',3, 1, array[3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); -- 7
  225. insert into calc_correction_types( description, parent_id)
  226. values('Таблица 2, Положительные температуры', 1 ), -- 8
  227. ('Таблица 2, Отрицательные температуры', 1 ) --9
  228. ;
  229. -- Cписок высот в разрезе типа оборудования
  230. create sequence calc_corrections_height_seq;
  231. create table calc_corrections_height
  232. (
  233. id integer primary key not null default nextval('public.calc_corrections_height_seq'),
  234. height integer not null
  235. );
  236. insert into calc_corrections_height(height)
  237. values(200),(400),(800),(1200),(1600),(2000),(2400),(3000),(4000);
  238. -- Универсальная таблица корректировок
  239. create sequence calc_corrections_seq;
  240. create table calc_corrections
  241. (
  242. id integer not null primary key default nextval('public.calc_corrections_seq'),
  243. calc_height_id integer not null,
  244. calc_correction_type_id integer not null,
  245. values integer[]
  246. );
  247. insert into calc_corrections(calc_height_id, calc_correction_type_id, values )
  248. -- Таблица 2, отрицптельные значения
  249. values(1, 8, array[-1, -2, -3, -4, -5, -6, -7, -8, -8, -9, -20, -29, -39, -49]), -- 200
  250. (2, 8, array[-1, -2, -3, -4, -5, -6, -6, -7, -8, -9, -19, -29, -39, -48]), -- 400
  251. (3, 8, array[-1, -2, -3, -4, -5, -6, -6, -7, -7, -8, -18, -28, -37, -46]), -- 800
  252. (4, 8, array[-1, -2, -3, -4, -4, -5, -5, -6, -7, -8, -17, -26, -35, -44]), -- 1200
  253. -- Таблица 2, положитеьные значения
  254. (1, 7, array[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30]), -- 200
  255. (2, 7, array[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30]), -- 400
  256. (3, 7, array[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30]), -- 800
  257. (4, 7, array[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30]), -- 1200
  258. -- Таблица 3, Скорость среднего ветра (ВР)
  259. (1, 4, array[3, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 12]), -- 200
  260. (2, 4, array[4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ,15]), -- 400
  261. (3, 4, array[4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15 ,16]), -- 800
  262. (4, 4, array[4, 5, 7, 8, 8, 9, 11, 12, 13, 15 ,15, 16]), -- 1200
  263. -- Таблица 3, Направление среднего ветра (ВР)
  264. (1, 5, array[0]), -- 200
  265. (2, 5, array[1]), -- 400
  266. (3, 5, array[2]), -- 800
  267. (4, 5, array[2]), -- 1200
  268. -- Таблица 3,Скорость среднего ветра (ДМК)
  269. (1, 6, array[4,6,8,9,10,12,14,15,16,18,20,21,22]), -- 200
  270. (2, 6, array[5,7,10,11,12,14,17,18,20,22,23,25,27]), -- 400
  271. (3, 6, array[5,8,10,11,13,15,18,19,21,23,25,27,28]), -- 800
  272. (4, 6, array[5,8,11,12,13,16,19,20,23,24,26,28,30]), -- 1200
  273. -- 'Таблица 3, Направление среднего ветра (ДМК)
  274. (1, 7, array[1]), -- 200
  275. (2, 7, array[2]), -- 400
  276. (3, 7, array[3]), -- 800
  277. (4, 7, array[3]) -- 1200
  278. ;
  279. raise notice 'Расчетные структуры сформированы';
  280. /*
  281. 4. Создание связей
  282. ==========================================
  283. */
  284. begin
  285. -- Основные связи
  286. -- Связь между пачками измерений с пользователями (один ко многим)
  287. alter table public.measurment_baths
  288. add constraint emploee_id_fk
  289. foreign key (emploee_id)
  290. references public.employees (id);
  291. -- Связь между пачками измерений и измерениями (один к одному)
  292. alter table public.measurment_baths
  293. add constraint measurment_input_param_id_fk
  294. foreign key(measurment_input_param_id)
  295. references public.measurment_input_params(id);
  296. -- Связь между измерениями и типом оборудования (один ко многим)
  297. alter table public.measurment_input_params
  298. add constraint measurment_type_id_fk
  299. foreign key(measurment_type_id)
  300. references public.measurment_types (id);
  301. -- Связь между пользователем и званием (один ко многим)
  302. alter table public.employees
  303. add constraint military_rank_id_fk
  304. foreign key(military_rank_id)
  305. references public.military_ranks (id);
  306. -- Расчетные связм
  307. -- Связь между типом измерения и типом таблицы корректировок
  308. alter table public.calc_correction_types
  309. add constraint calc_correction_types_measurment_type_id_fk
  310. foreign key(measurment_type_id)
  311. references public.measurment_types(id);
  312. -- Связь основной таблицы корректировок с высотами
  313. alter table public.calc_corrections
  314. add constraint calc_corrections_calc_height_id_fk
  315. foreign key(calc_height_id)
  316. references public.calc_corrections_height(id);
  317. -- Связь основной таблицы корректировок в типами корректировок
  318. alter table public.calc_corrections
  319. add constraint calc_corrections_calc_correction_type_id_fk
  320. foreign key(calc_correction_type_id)
  321. references public.calc_correction_types(id);
  322. -- Групповая связь для таблицы типов корректировок
  323. alter table public.calc_correction_types
  324. add constraint calc_correction_types_parent_id_fk
  325. foreign key(parent_id)
  326. references public.calc_correction_types(id);
  327. end;
  328. raise notice 'Связи сформированы';
  329. raise notice 'Формируем индексы';
  330. create index if not exists ix_measurment_baths_emploee_id on public.measurment_baths(emploee_id);
  331. create index if not exists ix_measurment_baths_measurment_input_param_id on public.measurment_baths(measurment_input_param_id);
  332. raise notice 'Индексы сформирован';
  333. /*
  334. 4. Создает расчетные и вспомогательные функции
  335. ==========================================
  336. */
  337. -- Функция для расчета отклонения приземной виртуальной температуры
  338. drop function if exists public.fn_calc_header_temperature;
  339. create function public.fn_calc_header_temperature(
  340. par_temperature numeric(8,2))
  341. returns numeric(8,2)
  342. language 'plpgsql'
  343. as $BODY$
  344. declare
  345. default_temperature numeric(8,2) default 15.9;
  346. default_temperature_key character varying default 'calc_table_temperature' ;
  347. virtual_temperature numeric(8,2) default 0;
  348. deltaTv numeric(8,2) default 0;
  349. var_result numeric(8,2) default 0;
  350. begin
  351. raise notice 'Расчет отклонения приземной виртуальной температуры по температуре %', par_temperature;
  352. -- Определим табличное значение температуры
  353. Select coalesce(value::numeric(8,2), default_temperature)
  354. from public.measurment_settings
  355. into virtual_temperature
  356. where
  357. key = default_temperature_key;
  358. -- Вирутальная поправка
  359. deltaTv := par_temperature +
  360. public.fn_calc_temperature_interpolation(par_temperature => par_temperature);
  361. -- Отклонение приземной виртуальной температуры
  362. var_result := deltaTv - virtual_temperature;
  363. return var_result;
  364. end;
  365. $BODY$;
  366. -- Функция для формирования даты в специальном формате
  367. drop function if exists public.fn_calc_header_period;
  368. create function public.fn_calc_header_period(
  369. par_period timestamp with time zone)
  370. returns text
  371. language 'sql'
  372. return
  373. -- ДД
  374. case when (extract(day from par_period) < 10::numeric) then '0'::text else ''::text end ||
  375. extract(day from par_period)::text ||
  376. -- ЧЧ
  377. case when (extract(hour from par_period) < 10::numeric) then '0'::text else ''::text end ||
  378. extract(hour from par_period)::text ||
  379. -- Десятки минут
  380. case when (extract(minute from par_period) < 10::numeric) then '0'::text
  381. else
  382. left(extract(minute from par_period)::text, 1)
  383. end;
  384. -- Функция для расчета отклонения наземного давления
  385. drop function if exists public.fn_calc_header_pressure;
  386. create function public.fn_calc_header_pressure
  387. (
  388. par_pressure numeric(8,2))
  389. returns numeric(8,2)
  390. language 'plpgsql'
  391. as $body$
  392. declare
  393. default_pressure numeric(8,2) default 750;
  394. table_pressure numeric(8,2) default null;
  395. default_pressure_key character varying default 'calc_table_pressure' ;
  396. begin
  397. raise notice 'Расчет отклонения наземного давления для %', par_pressure;
  398. -- Определяем граничное табличное значение
  399. if not exists (select 1 from public.measurment_settings where key = default_pressure_key ) then
  400. Begin
  401. table_pressure := default_pressure;
  402. end;
  403. else
  404. begin
  405. select value::numeric(18,2)
  406. into table_pressure
  407. from public.measurment_settings where key = default_pressure_key;
  408. end;
  409. end if;
  410. -- Результат
  411. return par_pressure - coalesce(table_pressure,table_pressure) ;
  412. end;
  413. $body$;
  414. -- Функция для проверки входных параметров
  415. 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));
  416. create function public.fn_check_input_params(
  417. par_height numeric,
  418. par_temperature numeric,
  419. par_pressure numeric,
  420. par_wind_direction numeric,
  421. par_wind_speed numeric,
  422. par_bullet_demolition_range numeric)
  423. returns check_result_type
  424. language 'plpgsql'
  425. as $body$
  426. declare
  427. var_result public.check_result_type;
  428. begin
  429. var_result.is_check := False;
  430. -- Температура
  431. if not exists (
  432. select 1 from (
  433. select
  434. coalesce(min_temperature , '0')::numeric(8,2) as min_temperature,
  435. coalesce(max_temperature, '0')::numeric(8,2) as max_temperature
  436. from
  437. (select 1 ) as t
  438. cross join
  439. ( select value as min_temperature from public.measurment_settings where key = 'min_temperature' ) as t1
  440. cross join
  441. ( select value as max_temperature from public.measurment_settings where key = 'max_temperature' ) as t2
  442. ) as t
  443. where
  444. par_temperature between min_temperature and max_temperature
  445. ) then
  446. var_result.error_message := format('Температура % не укладывает в диаппазон!', par_temperature);
  447. end if;
  448. var_result.params.temperature := par_temperature;
  449. -- Давление
  450. if not exists (
  451. select 1 from (
  452. select
  453. coalesce(min_pressure , '0')::numeric(8,2) as min_pressure,
  454. coalesce(max_pressure, '0')::numeric(8,2) as max_pressure
  455. from
  456. (select 1 ) as t
  457. cross join
  458. ( select value as min_pressure from public.measurment_settings where key = 'min_pressure' ) as t1
  459. cross join
  460. ( select value as max_pressure from public.measurment_settings where key = 'max_pressure' ) as t2
  461. ) as t
  462. where
  463. par_pressure between min_pressure and max_pressure
  464. ) then
  465. var_result.error_message := format('Давление %s не укладывает в диаппазон!', par_pressure);
  466. end if;
  467. var_result.params.pressure := par_pressure;
  468. -- Высота
  469. if not exists (
  470. select 1 from (
  471. select
  472. coalesce(min_height , '0')::numeric(8,2) as min_height,
  473. coalesce(max_height, '0')::numeric(8,2) as max_height
  474. from
  475. (select 1 ) as t
  476. cross join
  477. ( select value as min_height from public.measurment_settings where key = 'min_height' ) as t1
  478. cross join
  479. ( select value as max_height from public.measurment_settings where key = 'max_height' ) as t2
  480. ) as t
  481. where
  482. par_height between min_height and max_height
  483. ) then
  484. var_result.error_message := format('Высота %s не укладывает в диаппазон!', par_height);
  485. end if;
  486. var_result.params.height := par_height;
  487. -- Напрвление ветра
  488. if not exists (
  489. select 1 from (
  490. select
  491. coalesce(min_wind_direction , '0')::numeric(8,2) as min_wind_direction,
  492. coalesce(max_wind_direction, '0')::numeric(8,2) as max_wind_direction
  493. from
  494. (select 1 ) as t
  495. cross join
  496. ( select value as min_wind_direction from public.measurment_settings where key = 'min_wind_direction' ) as t1
  497. cross join
  498. ( select value as max_wind_direction from public.measurment_settings where key = 'max_wind_direction' ) as t2
  499. )
  500. where
  501. par_wind_direction between min_wind_direction and max_wind_direction
  502. ) then
  503. var_result.error_message := format('Направление ветра %s не укладывает в диаппазон!', par_wind_direction);
  504. end if;
  505. var_result.params.wind_direction := par_wind_direction;
  506. var_result.params.wind_speed := par_wind_speed;
  507. if coalesce(var_result.error_message,'') = '' then
  508. var_result.is_check = True;
  509. end if;
  510. return var_result;
  511. end;
  512. $body$;
  513. -- Функция для проверки параметров
  514. drop function if exists public.fn_check_input_params(input_params);
  515. create function public.fn_check_input_params(
  516. par_param input_params_type
  517. )
  518. returns public.input_params_type
  519. language 'plpgsql'
  520. as $body$
  521. declare
  522. var_result check_result_type;
  523. begin
  524. var_result := fn_check_input_params(
  525. par_param.height, par_param.temperature, par_param.pressure, par_param.wind_direction,
  526. par_param.wind_speed, par_param.bullet_demolition_range
  527. );
  528. if var_result.is_check = False then
  529. raise exception 'Ошибка %', var_result.error_message;
  530. end if;
  531. return var_result.params;
  532. end ;
  533. $body$;
  534. -- Расчет интерполяции
  535. drop function if exists public.fn_calc_interpolation;
  536. create function public.fn_calc_interpolation(
  537. par_temperature numeric,
  538. par_interpolation public.interpolation_type
  539. )
  540. returns numeric
  541. language 'plpgsql'
  542. as $body$
  543. -- Расчет интерполяции
  544. declare
  545. var_result numeric(8,2) default 0;
  546. var_denominator numeric(8,2) default 0;
  547. begin
  548. var_denominator := par_interpolation.x1 - par_interpolation.x0;
  549. if var_denominator = 0.0 then
  550. raise exception 'Деление на нуль. Возможно, некорректные данные в таблице с поправками!';
  551. end if;
  552. var_result := (par_temperature - par_interpolation.x0) * (par_interpolation.y1 - par_interpolation.y0) / var_denominator + par_interpolation.y0;
  553. return var_result;
  554. end;
  555. $body$;
  556. -- Функция для расчета интерполяции для метео приближенный
  557. drop function if exists public.fn_calc_temperature_interpolation;
  558. create function public.fn_calc_temperature_interpolation(
  559. par_temperature numeric(8,2))
  560. returns numeric
  561. language 'plpgsql'
  562. as $body$
  563. declare
  564. var_interpolation interpolation_type;
  565. var_result numeric(8,2) default 0;
  566. var_min_temparure numeric(8,2) default 0;
  567. var_max_temperature numeric(8,2) default 0;
  568. var_denominator numeric(8,2) default 0;
  569. begin
  570. raise notice 'Расчет интерполяции для температуры %', par_temperature;
  571. -- Проверим, возможно температура совпадает со значением в справочнике
  572. if exists (select 1 from public.calc_corrections_temperature where temperature = par_temperature ) then
  573. begin
  574. select correction
  575. into var_result
  576. from public.calc_corrections_temperature
  577. where
  578. temperature = par_temperature;
  579. end;
  580. else
  581. begin
  582. -- Получим диапазон в котором работают поправки
  583. select min(temperature), max(temperature)
  584. into var_min_temparure, var_max_temperature
  585. from public.calc_corrections_temperature;
  586. if par_temperature < var_min_temparure or
  587. par_temperature > var_max_temperature then
  588. raise exception 'Некорректно передан параметр! Невозможно рассчитать поправку. Значение должно укладываться в диаппазон: %, %',
  589. var_min_temparure, var_max_temperature;
  590. end if;
  591. -- Получим граничные параметры
  592. select x0, y0, x1, y1
  593. into var_interpolation.x0, var_interpolation.y0, var_interpolation.x1, var_interpolation.y1
  594. from
  595. (
  596. select t1.temperature as x0, t1.correction as y0
  597. from public.calc_corrections_temperature as t1
  598. where t1.temperature <= par_temperature
  599. order by t1.temperature desc
  600. limit 1
  601. ) as leftPart
  602. cross join
  603. (
  604. select t1.temperature as x1, t1.correction as y1
  605. from public.calc_corrections_temperature as t1
  606. where t1.temperature >= par_temperature
  607. order by t1.temperature
  608. limit 1
  609. ) as rightPart;
  610. raise notice 'Граничные значения %', var_interpolation;
  611. var_result := fn_calc_interpolation( par_temperature, var_interpolation);
  612. end;
  613. end if;
  614. return var_result;
  615. end;
  616. $body$;
  617. -- Функция для генерации случайной даты
  618. drop function if exists fn_get_random_timestamp;
  619. create function fn_get_random_timestamp(
  620. par_min_value timestamp,
  621. par_max_value timestamp)
  622. returns timestamp
  623. language 'plpgsql'
  624. as $body$
  625. begin
  626. return random() * (par_max_value - par_min_value) + par_min_value;
  627. end;
  628. $body$;
  629. -- Функция для генерации случайного целого числа из диаппазона
  630. drop function if exists fn_get_randon_integer;
  631. create function fn_get_randon_integer(
  632. par_min_value integer,
  633. par_max_value integer
  634. )
  635. returns integer
  636. language 'plpgsql'
  637. as $body$
  638. begin
  639. return floor((par_max_value + 1 - par_min_value)*random())::integer + par_min_value;
  640. end;
  641. $body$;
  642. -- Функция для гнерации случайного текста
  643. drop function if exists fn_get_random_text;
  644. create function fn_get_random_text(
  645. par_length int,
  646. par_list_of_chars text DEFAULT 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюяABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789'
  647. )
  648. returns text
  649. language 'plpgsql'
  650. as $body$
  651. declare
  652. var_len_of_list integer default length(par_list_of_chars);
  653. var_position integer;
  654. var_result text = '';
  655. var_random_number integer;
  656. var_max_value integer;
  657. var_min_value integer;
  658. begin
  659. var_min_value := 10;
  660. var_max_value := 50;
  661. for var_position in 1 .. par_length loop
  662. -- добавляем к строке случайный символ
  663. var_random_number := fn_get_randon_integer(var_min_value, var_max_value );
  664. var_result := var_result || substr(par_list_of_chars, var_random_number ,1);
  665. end loop;
  666. return var_result;
  667. end;
  668. $body$;
  669. -- Функция для расчета метео приближенный
  670. drop function if exists fn_calc_header_meteo_avg;
  671. create function fn_calc_header_meteo_avg(
  672. par_params input_params_type
  673. )
  674. returns text
  675. language 'plpgsql'
  676. as $body$
  677. declare
  678. var_result text;
  679. var_params input_params_type;
  680. begin
  681. -- Проверяю аргументы
  682. var_params := public.fn_check_input_params(par_params);
  683. select
  684. -- Дата
  685. public.fn_calc_header_period(now()) ||
  686. --Высота расположения метеопоста над уровнем моря.
  687. lpad( 340::text, 4, '0' ) ||
  688. -- Отклонение наземного давления атмосферы
  689. lpad(
  690. case when coalesce(var_params.pressure,0) < 0 then
  691. '5'
  692. else ''
  693. end ||
  694. lpad ( abs(( coalesce(var_params.pressure, 0) )::int)::text,2,'0')
  695. , 3, '0') as "БББ",
  696. -- Отклонение приземной виртуальной температуры
  697. lpad(
  698. case when coalesce( var_params.temperature, 0) < 0 then
  699. '5'
  700. else
  701. ''
  702. end ||
  703. ( coalesce(var_params.temperature,0)::int)::text
  704. , 2,'0')
  705. into var_result;
  706. return var_result;
  707. end;
  708. $body$;
  709. raise notice 'Структура сформирована успешно';
  710. end $$;
  711. -- //////////////////////////////////////////////////////////////////////////////
  712. -- Проверка расчета
  713. do $$
  714. declare
  715. var_pressure_value numeric(8,2) default 0;
  716. var_temperature_value numeric(8,2) default 0;
  717. var_period text;
  718. var_pressure text;
  719. var_height text;
  720. var_temperature text;
  721. begin
  722. var_pressure_value := round(public.fn_calc_header_pressure(743));
  723. var_temperature_value := round( public.fn_calc_header_temperature(3));
  724. select
  725. -- Дата
  726. public.fn_calc_header_period(now()) as "ДДЧЧМ",
  727. --Высота расположения метеопоста над уровнем моря.
  728. lpad( 340::text, 4, '0' ) as "ВВВВ",
  729. -- Отклонение наземного давления атмосферы
  730. lpad(
  731. case when var_pressure_value < 0 then
  732. '5'
  733. else ''
  734. end ||
  735. lpad ( abs((var_pressure_value)::int)::text,2,'0')
  736. , 3, '0') as "БББ",
  737. -- Отклонение приземной виртуальной температуры
  738. lpad(
  739. case when var_temperature_value < 0 then
  740. '5'
  741. else
  742. ''
  743. end ||
  744. (abs(var_temperature_value)::int)::text
  745. , 2,'0') as "TT"
  746. into
  747. var_period, var_height, var_pressure, var_temperature;
  748. raise notice '==============================';
  749. raise notice 'Пример расчета метео приближенный';
  750. raise notice 'ДДЧЧМ %, ВВВВ %, БББ % , TT %', var_period, var_height, var_pressure, var_temperature;
  751. end $$;
  752. -- Проверка входных параметров
  753. do $$
  754. declare
  755. var_result public.check_result_type;
  756. begin
  757. raise notice '=====================================';
  758. raise notice 'Проверка работы функции [fn_check_input_params]';
  759. raise notice 'Положительный сценарий';
  760. -- Корректный вариант
  761. var_result := public.fn_check_input_params(par_height => 400, par_temperature => 23, par_pressure => 740, par_wind_direction => 5, par_wind_speed => 5, par_bullet_demolition_range => 5 );
  762. if var_result.is_check != True then
  763. raise notice '-> Проверка не пройдена!';
  764. else
  765. raise notice '-> Проверка пройдена';
  766. end if;
  767. raise notice 'Сообщени: %', var_result.error_message;
  768. -- Некорректный вариант
  769. var_result := public.fn_check_input_params(par_height => -400, par_temperature => 23, par_pressure => 740, par_wind_direction => 5, par_wind_speed => 5, par_bullet_demolition_range => 5 );
  770. raise notice 'Отрицательный сценарий';
  771. if var_result.is_check != False then
  772. raise notice '-> Проверка не пройдена!';
  773. else
  774. raise notice '-> Проверка пройдена';
  775. end if;
  776. raise notice 'Сообщение: %', var_result.error_message;
  777. raise notice '=====================================';
  778. end $$;
  779. -- Генерация тестовых данных
  780. do $$
  781. declare
  782. var_position integer;
  783. var_emploee_ids integer[];
  784. var_emploee_quantity integer default 5;
  785. var_min_rank integer;
  786. var_max_rank integer;
  787. var_emploee_id integer;
  788. var_current_emploee_id integer;
  789. var_index integer;
  790. var_measure_type_id integer;
  791. var_measure_input_data_id integer;
  792. begin
  793. -- Определяем макс дипазон по должностям
  794. select min(id), max(id)
  795. into var_min_rank,var_max_rank
  796. from public.military_ranks;
  797. -- Формируем список пользователей
  798. for var_position in 1 .. var_emploee_quantity loop
  799. insert into public.employees(name, birthday, military_rank_id )
  800. select
  801. fn_get_random_text(25), -- name
  802. fn_get_random_timestamp('1978-01-01','2000-01-01'), -- birthday
  803. fn_get_randon_integer(var_min_rank, var_max_rank) -- military_rank_id
  804. ;
  805. select id into var_emploee_id from public.employees order by id desc limit 1;
  806. var_emploee_ids := var_emploee_ids || var_emploee_id;
  807. end loop;
  808. raise notice 'Сформированы тестовые пользователи %', var_emploee_ids;
  809. -- Формируем для каждого по 100 измерений
  810. foreach var_current_emploee_id in ARRAY var_emploee_ids LOOP
  811. for var_index in 1 .. 100 loop
  812. var_measure_type_id := fn_get_randon_integer(1,2);
  813. insert into public.measurment_input_params(measurment_type_id, height, temperature, pressure, wind_direction, wind_speed)
  814. select
  815. var_measure_type_id,
  816. fn_get_randon_integer(0,600)::numeric(8,2), -- height
  817. fn_get_randon_integer(0, 50)::numeric(8,2), -- temperature
  818. fn_get_randon_integer(500, 850)::numeric(8,2), -- pressure
  819. fn_get_randon_integer(0,59)::numeric(8,2), -- ind_direction
  820. fn_get_randon_integer(0,59)::numeric(8,2) -- wind_speed
  821. ;
  822. select id into var_measure_input_data_id from measurment_input_params order by id desc limit 1;
  823. insert into public.measurment_baths( emploee_id, measurment_input_param_id, started)
  824. select
  825. var_current_emploee_id,
  826. var_measure_input_data_id,
  827. fn_get_random_timestamp('2025-02-01 00:00', '2025-02-05 00:00')
  828. ;
  829. end loop;
  830. end loop;
  831. raise notice 'Набор тестовых данных сформирован успешно';
  832. end $$;
  833. create view vw_report_fails_statistics
  834. as
  835. -- Отчет: Статистика ошибок при проведении измерений
  836. -- ФИО, должность, количество измерений, количество ошибок
  837. with emploee_cte as
  838. (
  839. -- ФИО | Должность
  840. select
  841. t1.id , t1.name as user_name, t2.description as position
  842. from public.employees as t1
  843. inner join public.military_ranks as t2 on t1.military_rank_id = t2.id
  844. ),
  845. measurements_cte as
  846. (
  847. -- Кол-во измерений
  848. select count(*) as quantity, emploee_id
  849. from public.measurment_input_params as t1
  850. inner join public.measurment_baths as t2 on t2.measurment_input_param_id = t1.id
  851. group by emploee_id
  852. ),
  853. fails_cte as
  854. (
  855. -- Количество ошибочных данных
  856. select
  857. count(*) as quantity_fails,
  858. emploee_id
  859. from public.measurment_input_params as t1
  860. inner join public.measurment_baths as t2 on t2.measurment_input_param_id = t1.id
  861. where
  862. (public.fn_check_input_params(height, temperature, pressure, wind_direction, wind_speed, bullet_demolition_range)::public.check_result_type).is_check = False
  863. group by emploee_id
  864. )
  865. -- Основной запрос
  866. select
  867. user_name, position, coalesce(quantity,0) as quantity, coalesce(quantity_fails,0) as quantity_fails
  868. from emploee_cte as t1
  869. left join measurements_cte as t2 on t1.id = t2.emploee_id
  870. left join fails_cte as t3 on t1.id = t3.emploee_id
  871. order by coalesce(quantity_fails,0) desc;
  872. create view vw_report_fails_height_statistics
  873. as
  874. -- Отчет: "Самая эффективная высота измерения"
  875. -- | ФИО пользователя | Звание | Мин. высота метеопоста | Макс. высота метепоста | Всего измерений | Из них ошибочны |
  876. with emploee_cte as
  877. (
  878. -- ФИО | Должность
  879. select
  880. t1.id , t1.name as user_name, t2.description as position
  881. from public.employees as t1
  882. inner join public.military_ranks as t2 on t1.military_rank_id = t2.id
  883. ),
  884. measurements_cte as
  885. (
  886. -- Кол-во измерений
  887. select count(*) as quantity, emploee_id
  888. from public.measurment_input_params as t1
  889. inner join public.measurment_baths as t2 on t2.measurment_input_param_id = t1.id
  890. group by emploee_id
  891. ),
  892. fails_cte as
  893. (
  894. -- Количество ошибочных данных
  895. select
  896. count(*) as quantity_fails,
  897. emploee_id
  898. from public.measurment_input_params as t1
  899. inner join public.measurment_baths as t2 on t2.measurment_input_param_id = t1.id
  900. where
  901. (public.fn_check_input_params(height, temperature, pressure, wind_direction, wind_speed, bullet_demolition_range)::public.check_result_type).is_check = False
  902. group by emploee_id
  903. ),
  904. measurments_height_cte as
  905. (
  906. -- Статистика по высотам
  907. select min(height) as min_height, max(height) as max_height, emploee_id
  908. from public.measurment_input_params as t1
  909. inner join public.measurment_baths as t2 on t2.measurment_input_param_id = t1.id
  910. group by emploee_id
  911. )
  912. -- Основной запрос
  913. select
  914. user_name, position, coalesce(quantity,0) as quantity, coalesce(quantity_fails,0) as quantity_fails,
  915. coalesce(min_height, 0) as min_height, coalesce(max_height, 0) as max_height
  916. from emploee_cte as t1
  917. left join measurements_cte as t2 on t1.id = t2.emploee_id
  918. left join fails_cte as t3 on t1.id = t3.emploee_id
  919. left join measurments_height_cte as t4 on t1.id = t4.emploee_id
  920. order by coalesce(quantity_fails,0) asc, coalesce(min_height, 0) asc ;
  921. -- Процедура расчета всех поправок по всем высотам
  922. drop procedure if exists sp_calc_corrections;
  923. create procedure public.sp_calc_corrections(
  924. in par_input_params input_params_type,
  925. in par_measurement_type_id integer,
  926. inout par_results calc_result_type[]
  927. )
  928. language 'plpgsql'
  929. as $body$
  930. declare
  931. var_header integer[];
  932. var_values integer[];
  933. var_result calc_result_type;
  934. var_correction_type_id integer;
  935. var_rec_height record;
  936. var_index integer;
  937. var_left_index integer;
  938. var_right_index integer;
  939. var_left_value integer;
  940. var_right_value integer;
  941. var_interpolation interpolation_type;
  942. -- Тип таблица 2, положительные значения
  943. var_table2_positive_values integer default 7;
  944. -- Тип таблица 2, отрицательные значения
  945. var_table2_negative_values integer default 8;
  946. -- Тип таблица 2, заголовок
  947. var_table2_header integer default 1;
  948. -- Тип таблица 3, скорость среднего ветра
  949. var_table3_wind_speed integer default 2;
  950. -- Тип Таблица 3, направление среднего ветра
  951. var_table3_wind_direction integer default 3;
  952. begin
  953. -- Входные параметры
  954. par_input_params.height := 400;
  955. par_input_params.temperature := 24;
  956. par_input_params.pressure := 700;
  957. par_input_params.wind_direction := 10;
  958. par_input_params.wind_speed := 40;
  959. par_input_params.bullet_demolition_range := 6;
  960. if not exists (select 1 from public.measurment_types where id = par_measurement_type_id) then
  961. raise exception 'Некорректно указан тип оборудования по коду %', par_measurement_type_id;
  962. end if;
  963. raise notice 'Расчет для оборудования %, параметры %', par_measurement_type_id,par_input_params;
  964. par_input_params := public.fn_check_input_params(par_param => par_input_params);
  965. raise notice 'Проверка входных параметров пройдена';
  966. for var_rec_height in
  967. select * from public.calc_corrections_height loop
  968. -- Инициализация
  969. var_result.deltapressure := 0;
  970. var_result.deltatemperature := 0;
  971. var_result.deviationtemperature := 0;
  972. var_result.deviationwind := 0;
  973. var_result.deviationwinddirection := 0;
  974. -- Текущая высота
  975. var_result.height := var_rec_height.height;
  976. -- Текущий тип измерения
  977. var_result.measurement_type_id := par_measurement_type_id;
  978. -- Отклонение наземного давления атмосферы
  979. var_result.deltapressure := public.fn_calc_header_pressure( par_input_params.pressure);
  980. -- Отклонение приземной виртуальной температуры
  981. var_result.deltatemperature := public.fn_calc_header_temperature(par_input_params.temperature);
  982. -- =======================================================
  983. -- Средние отклонения температуры воздух
  984. -- Получаем заголовок Таблица 2
  985. var_header := (select values from public.calc_correction_types where id = var_table2_header );
  986. -- Получаем список значений по текущей высоте
  987. var_values :=
  988. case when var_result.deltatemperature >= 0 then
  989. -- Положительные значения
  990. (select values from public.calc_corrections
  991. where calc_correction_type_id = var_table2_positive_values
  992. and calc_height_id = var_rec_height.id limit 1)
  993. else
  994. -- Отрицательные значения
  995. (select values from public.calc_corrections
  996. where calc_correction_type_id = var_table2_negative_values
  997. and calc_height_id = var_rec_height.id limit 1)
  998. end;
  999. if var_values is null then
  1000. -- Для текущей высоты нет данных
  1001. continue;
  1002. end if;
  1003. -- Определяем индексы
  1004. select max(position)
  1005. into var_left_index
  1006. from unnest(var_header) as position
  1007. where position <= var_result.deltatemperature;
  1008. select min(position)
  1009. into var_right_index
  1010. from unnest(var_header) as position
  1011. where position > var_result.deltatemperature;
  1012. -- Определяем значения
  1013. var_left_value := var_values[ var_left_index ];
  1014. var_right_value := var_values[ var_right_index ];
  1015. -- Получаем значение корректировки путем расчета интерполяции
  1016. var_interpolation.x0 := var_left_index;
  1017. var_interpolation.x1 := var_right_index;
  1018. var_interpolation.y0 := var_left_value;
  1019. var_interpolation.y1 := var_right_value;
  1020. -- Получаем расчетное значение поаправки по температуре
  1021. var_result.deviationtemperature :=
  1022. round(fn_calc_interpolation( var_result.deltatemperature, var_interpolation));
  1023. -- ======================================================
  1024. -- Скорость среднего ветра
  1025. -- Получаем заголовок и данные
  1026. select id
  1027. into var_correction_type_id
  1028. from calc_correction_types where
  1029. measurment_type_id = par_measurement_type_id and parent_id = var_table3_wind_speed ;
  1030. var_header := (select values from public.calc_correction_types
  1031. where measurment_type_id = par_measurement_type_id and parent_id = var_table3_wind_speed );
  1032. var_values := (select values from calc_corrections
  1033. where calc_correction_type_id = var_correction_type_id
  1034. and calc_height_id = var_rec_height.id limit 1);
  1035. -- Получаем индекс
  1036. select
  1037. coalesce(min(position), 1) from
  1038. into var_index
  1039. unnest(var_header) as position
  1040. where position > round(par_input_params.wind_speed);
  1041. var_result.deviationwind := var_values[var_index] ;
  1042. -- =====================================================
  1043. -- Направление среднего ветра
  1044. select id
  1045. into var_correction_type_id
  1046. from calc_correction_types where
  1047. measurment_type_id = par_measurement_type_id and parent_id = var_table3_wind_direction ;
  1048. var_values := (select values from calc_corrections
  1049. where calc_correction_type_id = var_correction_type_id
  1050. and calc_height_id = var_rec_height.id limit 1);
  1051. select
  1052. position
  1053. into var_result.deviationwinddirection
  1054. from
  1055. unnest(var_values) as position
  1056. limit 1;
  1057. par_results := array_append(par_results, var_result);
  1058. end loop;
  1059. raise notice 'Расчет поправок завершен успешно';
  1060. end;
  1061. $body$;
  1062. -- Проверка отчета
  1063. select * from vw_report_fails_height_statistics