HomeWork20250221.sql 37 KB

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