Initial commit
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
|
||||
|
||||
CREATE TABLE person_account(
|
||||
person_id int NOT NULL UNIQUE REFERENCES person,
|
||||
login varchar(60) NOT NULL,
|
||||
password varchar NOT NULL,
|
||||
CONSTRAINT empty_login CHECK (trim(login) <> '')
|
||||
);
|
||||
|
||||
CREATE OR REPLACE FUNCTION check_password(_password varchar(60))
|
||||
RETURNS void AS
|
||||
$BODY$
|
||||
BEGIN
|
||||
IF (trim(_password) = '')
|
||||
THEN
|
||||
RAISE EXCEPTION 'Пароль не может быть пустым!';
|
||||
END IF;
|
||||
|
||||
IF (trim(_password) <> _password)
|
||||
THEN
|
||||
RAISE EXCEPTION 'Пароль должен быть без пробелов!';
|
||||
END IF;
|
||||
|
||||
IF (length(_password) < 8)
|
||||
THEN
|
||||
RAISE EXCEPTION 'Пароль должен иметь больше 8 знаков!';
|
||||
END IF;
|
||||
|
||||
IF (lower(_password) = _password OR _password !~ '[0-9]')
|
||||
THEN
|
||||
RAISE EXCEPTION 'Пароль должен иметь прописные и заглавные буквы, а также цифры!';
|
||||
END IF;
|
||||
|
||||
IF ((SELECT COUNT(*) AS letter_count
|
||||
FROM ( SELECT regexp_split_to_table(_password, '') AS letter) AS letters
|
||||
GROUP BY letter
|
||||
ORDER BY letter
|
||||
LIMIT 1) >= length(_password)/2)
|
||||
THEN
|
||||
RAISE EXCEPTION 'Пароль не должен иметь больше половины одинаковых символов!';
|
||||
END IF;
|
||||
END
|
||||
$BODY$
|
||||
LANGUAGE plpgsql VOLATILE;
|
||||
|
||||
CREATE OR REPLACE FUNCTION create_person_account()
|
||||
RETURNS TRIGGER AS $$
|
||||
DECLARE
|
||||
role int;
|
||||
BEGIN
|
||||
PERFORM check_password(NEW.password);
|
||||
NEW.password := crypt(NEW.password, gen_salt('bf'));
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Создаем триггер
|
||||
CREATE TRIGGER trigger_create_account
|
||||
BEFORE INSERT ON person_account
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION create_person_account();
|
||||
|
||||
CREATE OR REPLACE FUNCTION check_enter_in_person_account(_login varchar, _password varchar)
|
||||
RETURNS int AS $$
|
||||
DECLARE
|
||||
_hash_password varchar := null;
|
||||
_person_id int;
|
||||
BEGIN
|
||||
-- Получаем person_id и проверяем авторизацию
|
||||
SELECT pa.person_id, pa.password
|
||||
INTO _person_id, _hash_password
|
||||
FROM person_account pa
|
||||
WHERE pa.login = _login
|
||||
AND pa.password = crypt(_password, pa.password);
|
||||
|
||||
-- Если запись не найдена
|
||||
IF _person_id IS NULL THEN
|
||||
RAISE EXCEPTION 'Неверный логин или пароль!';
|
||||
END IF;
|
||||
|
||||
RETURN _person_id;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
INSERT INTO person_account (person_id, login, password)
|
||||
VALUES (1, 'Иванов Н.В.', 'Иванов123'),
|
||||
(2, 'Сидоров В.А.', 'Сидоров123'),
|
||||
(3, 'Кузнецов П.С.', 'Кузнецов123'),
|
||||
(4, 'Петров И.В.', 'Петров123'),
|
||||
(5, 'Иванов Н.А.', 'Иванов123'),
|
||||
(6, 'Иванов Е.С.', 'Иванов123');
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
TRUNCATE TABLE units RESTART IDENTITY CASCADE;
|
||||
|
||||
\copy units(name) FROM 'csv/units.csv' WITH (FORMAT csv, HEADER);
|
||||
@@ -0,0 +1,10 @@
|
||||
Мария
|
||||
Анна
|
||||
Екатерина
|
||||
Ольга
|
||||
Татьяна
|
||||
Дана
|
||||
Елена
|
||||
Наталья
|
||||
Светлана
|
||||
Виктория
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Данова
|
||||
Петрова
|
||||
Сидорова
|
||||
Кузнецова
|
||||
Смирнова
|
||||
Попова
|
||||
Васильева
|
||||
Новикова
|
||||
Фёдорова
|
||||
Морозова
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Дановна
|
||||
Петровна
|
||||
Сергеевна
|
||||
Алексеевна
|
||||
Николаевна
|
||||
Владимировна
|
||||
Фёдоровна
|
||||
Андреевна
|
||||
Андреевна
|
||||
Анатольевна
|
||||
|
@@ -0,0 +1,10 @@
|
||||
ΐλεκρει
|
||||
Θβΰν
|
||||
ΟΈςπ
|
||||
Δμθςπθι
|
||||
Ρεπγει
|
||||
Νθκξλΰι
|
||||
Μθυΰθλ
|
||||
Βλΰδθμθπ
|
||||
Εγξπ
|
||||
ΐπςΈμ
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Иванов
|
||||
Петров
|
||||
Сидоров
|
||||
Кузнецов
|
||||
Смирнов
|
||||
Попов
|
||||
Васильев
|
||||
Новиков
|
||||
Фёдоров
|
||||
Морозов
|
||||
|
@@ -0,0 +1,10 @@
|
||||
Данович
|
||||
Петрович
|
||||
Сергеевич
|
||||
Алексеевич
|
||||
Николаевич
|
||||
Владимирович
|
||||
Фёдорович
|
||||
Андреевич
|
||||
Дмитриевич
|
||||
Анатольевич
|
||||
|
@@ -0,0 +1,10 @@
|
||||
ул. Ленина
|
||||
ул. Гагарина
|
||||
пр. Мира
|
||||
ул. Советская
|
||||
ул. Пушкина
|
||||
ул. Чайковского
|
||||
ул. Чехова
|
||||
ул. Кирова
|
||||
ул. Молодёжная
|
||||
ул. Комсомольская
|
||||
|
@@ -0,0 +1,10 @@
|
||||
name
|
||||
мг/л
|
||||
г/л
|
||||
ммоль/л
|
||||
мкмоль/л
|
||||
ед/л
|
||||
%
|
||||
тыс/мкл
|
||||
мл
|
||||
мм
|
||||
|
@@ -0,0 +1,72 @@
|
||||
-- Функция: нет двойных пробелов подряд
|
||||
CREATE OR REPLACE FUNCTION no_double_space (str text)
|
||||
RETURNS boolean
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN str !~ E'\s{2,}';
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql
|
||||
IMMUTABLE;
|
||||
|
||||
-- Функция: нет пробелов в начале и конце строки
|
||||
CREATE OR REPLACE FUNCTION no_space_start_end (str text)
|
||||
RETURNS boolean
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN str !~ E'^\s'
|
||||
AND str !~ E'\s$';
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql
|
||||
IMMUTABLE;
|
||||
|
||||
-- Функция: нет пробелов вообще
|
||||
CREATE OR REPLACE FUNCTION no_space (str text)
|
||||
RETURNS boolean
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN str !~ E'\s';
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql
|
||||
IMMUTABLE;
|
||||
|
||||
-- Функция: длина строки в заданных границах
|
||||
CREATE OR REPLACE FUNCTION str_length_between (str text, min_len int, max_len int)
|
||||
RETURNS boolean
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN LENGTH(str) BETWEEN min_len AND max_len;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql
|
||||
IMMUTABLE;
|
||||
|
||||
-- Функция: стандартная проверка для слова
|
||||
CREATE OR REPLACE FUNCTION default_word_check (str text)
|
||||
RETURNS boolean
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN str IS NULL
|
||||
OR no_space (str)
|
||||
AND str_length_between (str, 1, 64);
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql
|
||||
IMMUTABLE;
|
||||
|
||||
-- Функция: стандартная проверка для строки
|
||||
CREATE OR REPLACE FUNCTION default_str_check (str text)
|
||||
RETURNS boolean
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN str IS NULL
|
||||
OR no_double_space (str)
|
||||
AND no_space_start_end (str)
|
||||
AND str_length_between (str, 1, 128);
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql
|
||||
IMMUTABLE;
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
-- Генерация целочисленного значения в диапазоне
|
||||
CREATE OR REPLACE FUNCTION random_int (min_val int, max_val int)
|
||||
RETURNS int
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN min_val + trunc(random() * (max_val - min_val + 1))::int;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql
|
||||
VOLATILE;
|
||||
|
||||
-- Генерация даты рождения в диапазоне
|
||||
CREATE OR REPLACE FUNCTION random_date ()
|
||||
RETURNS date
|
||||
AS $$
|
||||
DECLARE
|
||||
from_date date := '1950-01-01';
|
||||
to_date date := '2010-12-31';
|
||||
BEGIN
|
||||
RETURN from_date + random_int (0, to_date - from_date);
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql
|
||||
VOLATILE;
|
||||
|
||||
-- Генерация адреса проживания
|
||||
CREATE OR REPLACE FUNCTION random_address (street text)
|
||||
RETURNS text
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN street || ', дом ' || random_int (1, 40) || ', квартира ' || random_int (40, 120);
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql
|
||||
VOLATILE;
|
||||
|
||||
-- --- Подготовленные общие данные ---
|
||||
CREATE TEMP TABLE streets (
|
||||
street text
|
||||
);
|
||||
|
||||
\copy streets FROM 'csv/streets.csv' (FORMAT csv);
|
||||
-- --- Подготовленные данные для генерации мужских записей ---
|
||||
CREATE TEMP TABLE male_last_names (
|
||||
last_name text
|
||||
);
|
||||
|
||||
\copy male_last_names FROM 'csv/male_last_names.csv' (FORMAT csv);
|
||||
CREATE TEMP TABLE male_first_names (
|
||||
first_name text
|
||||
);
|
||||
|
||||
\copy male_first_names FROM 'csv/male_first_names.csv' (FORMAT csv);
|
||||
CREATE TEMP TABLE male_middle_names (
|
||||
middle_name text
|
||||
);
|
||||
|
||||
\copy male_middle_names FROM 'csv/male_middle_names.csv' (FORMAT csv);
|
||||
-- Подготовленные данные для генерации женских записей
|
||||
CREATE TEMP TABLE female_last_names (
|
||||
last_name text
|
||||
);
|
||||
|
||||
\copy female_last_names FROM 'csv/female_last_names.csv' (FORMAT csv);
|
||||
CREATE TEMP TABLE female_first_names (
|
||||
first_name text
|
||||
);
|
||||
|
||||
\copy female_first_names FROM 'csv/female_first_names.csv' (FORMAT csv);
|
||||
CREATE TEMP TABLE female_middle_names (
|
||||
middle_name text
|
||||
);
|
||||
|
||||
\copy female_middle_names FROM 'csv/female_middle_names.csv' (FORMAT csv);
|
||||
-- Генерация --
|
||||
-- Удаление старых данных
|
||||
TRUNCATE TABLE persons RESTART IDENTITY CASCADE;
|
||||
|
||||
-- Генерация записей
|
||||
INSERT INTO persons (last_name, first_name, middle_name, birth_date, gender, address)
|
||||
SELECT
|
||||
last_name,
|
||||
first_name,
|
||||
middle_name,
|
||||
random_date (),
|
||||
gender_enum,
|
||||
random_address (street)
|
||||
FROM (
|
||||
-- Мужские записи
|
||||
SELECT
|
||||
ml.last_name,
|
||||
mf.first_name,
|
||||
mm.middle_name,
|
||||
'male'::gender_enum,
|
||||
s.street
|
||||
FROM
|
||||
male_last_names ml
|
||||
CROSS JOIN male_first_names mf
|
||||
CROSS JOIN male_middle_names mm
|
||||
CROSS JOIN streets s
|
||||
UNION ALL
|
||||
-- Женские записи
|
||||
SELECT
|
||||
fl.last_name,
|
||||
ff.first_name,
|
||||
fm.middle_name,
|
||||
'female'::gender_enum,
|
||||
s.street
|
||||
FROM
|
||||
female_last_names fl
|
||||
CROSS JOIN female_first_names ff
|
||||
CROSS JOIN female_middle_names fm
|
||||
CROSS JOIN streets s) all_people
|
||||
ORDER BY
|
||||
random();
|
||||
|
||||
DROP TABLE IF EXISTS streets;
|
||||
|
||||
DROP TABLE IF EXISTS male_last_names;
|
||||
|
||||
DROP TABLE IF EXISTS male_first_names;
|
||||
|
||||
DROP TABLE IF EXISTS male_middle_names;
|
||||
|
||||
DROP TABLE IF EXISTS female_last_names;
|
||||
|
||||
DROP TABLE IF EXISTS female_first_names;
|
||||
|
||||
DROP TABLE IF EXISTS female_middle_names;
|
||||
|
||||
DROP FUNCTION IF EXISTS random_date ();
|
||||
|
||||
DROP FUNCTION IF EXISTS random_address (TEXT);
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
CREATE OR REPLACE FUNCTION get_results()
|
||||
RETURNS TABLE ("Пациент" text, "Обследование" text, "Результат" varchar)
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN QUERY(SELECT pa.policy_number, et.name, COALESCE(qr.value::varchar, CONCAT(nr.value || ' ' || u.name))
|
||||
FROM patient pa
|
||||
LEFT JOIN prescription pr
|
||||
ON pa.patient_id = pr.patient_id
|
||||
LEFT JOIN examination_type_prescription etp
|
||||
ON pr.prescription_id = etp.prescription_id
|
||||
LEFT JOIN examination_type et
|
||||
ON et.examination_type_id = etp.examination_type_id
|
||||
LEFT JOIN result r
|
||||
ON etp.result_id = r.result_id
|
||||
LEFT JOIN qualitative_result qr
|
||||
ON r.result_id = qr.result_id
|
||||
LEFT JOIN numeric_result nr
|
||||
ON r.result_id = nr.result_id
|
||||
LEFT JOIN unit u
|
||||
ON nr.unit_id = u.unit_id
|
||||
WHERE et.name IS NOT NULL);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
@@ -0,0 +1,286 @@
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
-- Люди
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
--
|
||||
-- Справочник людей
|
||||
CREATE TABLE person (
|
||||
person_id serial PRIMARY KEY,
|
||||
-- фамилия
|
||||
last_name text NOT NULL CHECK (default_word_check (last_name)),
|
||||
-- имя
|
||||
first_name text NOT NULL CHECK (default_word_check (first_name)),
|
||||
-- отчество
|
||||
middle_name text CHECK (default_word_check (middle_name)),
|
||||
-- дата рождения
|
||||
birth_date date NOT NULL CHECK (birth_date <= CURRENT_DATE),
|
||||
-- пол
|
||||
gender string NOT NULL,
|
||||
-- место жительства
|
||||
address text NOT NULL CHECK (default_str_check (address))
|
||||
);
|
||||
|
||||
-- Справочник пациентов
|
||||
CREATE TABLE patient (
|
||||
patient_id int PRIMARY KEY REFERENCES person (person_id),
|
||||
policy_number text NOT NULL CHECK (policy_number ~ '^[0-9]{16}$')
|
||||
);
|
||||
|
||||
-- Справочник врачей
|
||||
CREATE TABLE doctor (
|
||||
doctor_id int PRIMARY KEY REFERENCES person (person_id)
|
||||
);
|
||||
|
||||
CREATE TABLE registrar (
|
||||
registrar_id int PRIMARY KEY REFERENCES person (person_id)
|
||||
);
|
||||
|
||||
-- Справочник единиц измерения для числовых результатов
|
||||
CREATE TABLE unit (
|
||||
unit_id serial PRIMARY KEY,
|
||||
name text NOT NULL CHECK (name ~ '^[а-яА-ЯёЁ%^/]+$' AND str_length_between (name, 1, 16)),
|
||||
UNIQUE (name)
|
||||
);
|
||||
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
-- Назначения
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
--
|
||||
-- Базовая таблица назначения (TPT)
|
||||
CREATE TABLE prescription (
|
||||
prescription_id serial PRIMARY KEY,
|
||||
-- пациент
|
||||
patient_id int NOT NULL REFERENCES patient (patient_id) ON DELETE CASCADE,
|
||||
-- врач
|
||||
doctor_id int NOT NULL REFERENCES doctor (doctor_id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
-- Обследования
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
--
|
||||
-- Справочник типов обследований (Общий анализ крови", "ЭКГ", "УЗИ печени")
|
||||
CREATE TABLE examination_type (
|
||||
examination_type_id serial PRIMARY KEY,
|
||||
-- название типа обследования (одно слово, без пробелов)
|
||||
name text NOT NULL CHECK (default_word_check (name)),
|
||||
-- категория обследования
|
||||
category text NOT NULL,
|
||||
UNIQUE (name)
|
||||
);
|
||||
|
||||
-- Общая таблица результатов обследований (TPT)
|
||||
CREATE TABLE result (
|
||||
result_id serial PRIMARY KEY,
|
||||
examination_id int NOT NULL REFERENCES examination_type (examination_type_id) ON DELETE CASCADE,
|
||||
);
|
||||
|
||||
-- Журнал фактических обследований пациентов
|
||||
CREATE TABLE examination_type_prescription (
|
||||
examination_prescription_id serial PRIMARY KEY,
|
||||
prescription_id int NOT NULL REFERENCES prescription (prescription_id) ON DELETE CASCADE,
|
||||
result_id int REFERENCES result (result_id) ON DELETE CASCADE,
|
||||
-- тип обследования
|
||||
examination_type_id int NOT NULL REFERENCES examination_type (examination_type_id) ON DELETE CASCADE,
|
||||
UNIQUE (prescription_id)
|
||||
);
|
||||
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
-- Фактические результаты обследований
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
--
|
||||
|
||||
-- Числовой результат обследования пациента (только один на обследование)
|
||||
CREATE TABLE numeric_result (
|
||||
-- обследование пациента
|
||||
result_id int PRIMARY KEY REFERENCES result (result_id) ON DELETE CASCADE,
|
||||
-- значение результата
|
||||
value numeric NOT NULL,
|
||||
-- единица измерения
|
||||
unit_id int NOT NULL REFERENCES unit (unit_id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
-- Нормальные результаты обследований
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
--
|
||||
-- Качественный результат обследования пациента (только один на обследование)
|
||||
CREATE TABLE qualitative_result (
|
||||
-- обследование пациента
|
||||
result_id int PRIMARY KEY REFERENCES result (result_id) ON DELETE CASCADE,
|
||||
-- признак отклонения
|
||||
value boolean NOT NULL
|
||||
);
|
||||
|
||||
-- Общая таблица для справочника референсных (нормальных) значений (TPT)
|
||||
CREATE TABLE reference_value (
|
||||
reference_value_id serial PRIMARY KEY,
|
||||
examination_type_id int NOT NULL REFERENCES examination_type (examination_type_id) ON DELETE CASCADE,
|
||||
gender text, -- пол (если норма специфична)
|
||||
age_min int, -- нижняя граница возраста (если есть зависимость)
|
||||
age_max int, -- верхняя граница возраста (если есть зависимость)
|
||||
CHECK (
|
||||
(age_min IS NULL OR age_max IS NULL) -- если один из параметров не задан, не сравниваем
|
||||
OR (age_min < age_max) -- если оба заданы, min < max
|
||||
),
|
||||
UNIQUE (examination_type_id, gender, age_min, age_max)
|
||||
);
|
||||
|
||||
-- Справочник референсных (нормальных) значений для числовых обследований
|
||||
CREATE TABLE reference_numeric_value (
|
||||
reference_value_id int PRIMARY KEY REFERENCES reference_value (reference_value_id) ON DELETE CASCADE,
|
||||
min_value numeric,
|
||||
max_value numeric,
|
||||
unit_id int NOT NULL REFERENCES unit (unit_id) ON DELETE RESTRICT
|
||||
);
|
||||
|
||||
-- Справочник референсных (нормальных) значений для качественных обследований
|
||||
CREATE TABLE reference_qualitative_value (
|
||||
reference_value_id int PRIMARY KEY REFERENCES reference_value (reference_value_id) ON DELETE CASCADE,
|
||||
description_true text NOT NULL CHECK (default_word_check (description_true)),
|
||||
description_false text NOT NULL CHECK (default_word_check (description_false)),
|
||||
UNIQUE (description_true, description_false)
|
||||
);
|
||||
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
-- Синдромы
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
--
|
||||
-- Справочник синдромов
|
||||
CREATE TABLE syndrome_type (
|
||||
syndrome_type_id serial PRIMARY KEY,
|
||||
name text NOT NULL CHECK (default_word_check (name)),
|
||||
UNIQUE (name)
|
||||
);
|
||||
|
||||
-- Соответствие синдромов и типов обследований (определяет, какое обследование позволяет определить синдром)
|
||||
CREATE TABLE syndrome_examination_type (
|
||||
syndrome_id int PRIMARY KEY REFERENCES syndrome_type (syndrome_type_id) ON DELETE CASCADE,
|
||||
examination_type_id int NOT NULL REFERENCES examination_type (examination_type_id) ON DELETE CASCADE,
|
||||
UNIQUE (examination_type_id)
|
||||
);
|
||||
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
-- Болезни и диагнозы
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
--
|
||||
-- Справочник заболеваний
|
||||
CREATE TABLE disease (
|
||||
disease_id serial PRIMARY KEY,
|
||||
-- русское наименование
|
||||
name text NOT NULL CHECK (default_str_check (name)),
|
||||
-- код МКБ в формате A12.3
|
||||
icd_code text NOT NULL CHECK (icd_code ~ '^[A-Z][0-9]{2}\.[0-9]$'),
|
||||
UNIQUE (name),
|
||||
UNIQUE (icd_code)
|
||||
);
|
||||
|
||||
-- Соответствие заболеваний и синдромов
|
||||
CREATE TABLE disease_syndrome (
|
||||
disease_syndrome_id SERIAL PRIMARY KEY,
|
||||
disease_id int NOT NULL REFERENCES disease (disease_id) ON DELETE CASCADE,
|
||||
syndrome_id int NOT NULL REFERENCES syndrome_type (syndrome_type_id) ON DELETE CASCADE,
|
||||
UNIQUE (disease_id, syndrome_id)
|
||||
);
|
||||
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
-- Госпитализация
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
--
|
||||
-- Госпитализация (начало)
|
||||
CREATE TABLE hospital_admission (
|
||||
hospital_admission_id serial PRIMARY KEY,
|
||||
-- пациент
|
||||
patient_id int NOT NULL REFERENCES patient (patient_id) ON DELETE CASCADE,
|
||||
-- предварительный диагноз
|
||||
preliminary_disease_id int NOT NULL REFERENCES disease (disease_id),
|
||||
-- дата и время поступления
|
||||
admission_datetime timestamp NOT NULL CHECK (admission_datetime <= CURRENT_TIMESTAMP)
|
||||
);
|
||||
|
||||
-- Завершение госпитализации
|
||||
CREATE TABLE hospital_discharge (
|
||||
hospital_discharge_id serial PRIMARY KEY,
|
||||
hospital_admission_id int NOT NULL REFERENCES hospital_admission (hospital_admission_id) ON DELETE CASCADE,
|
||||
-- дата и время выписки/смерти
|
||||
discharge_datetime timestamp NOT NULL CHECK (discharge_datetime <= CURRENT_TIMESTAMP),
|
||||
final_disease_id int NOT NULL REFERENCES disease (disease_id),
|
||||
-- причина завершения
|
||||
reason text NOT NULL
|
||||
);
|
||||
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
-- Лекарства
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
--
|
||||
-- Справочник способов приёма лекарств
|
||||
CREATE TABLE drug_intake_method (
|
||||
drug_intake_method_id serial PRIMARY KEY,
|
||||
-- наименование способа (например, "перорально", "внутримышечно", "внутривенно", "ингаляционно", "ректально")
|
||||
name text NOT NULL CHECK (default_word_check (name)),
|
||||
UNIQUE (name)
|
||||
);
|
||||
|
||||
-- Справочник лекарств
|
||||
CREATE TABLE drug_type (
|
||||
drug_type_id serial PRIMARY KEY,
|
||||
international_name_ru text NOT NULL CHECK (default_word_check (international_name_ru)),
|
||||
international_name_en text CHECK (default_word_check (international_name_en)),
|
||||
international_name_la text CHECK (default_word_check (international_name_la)),
|
||||
brand_name_ru text CHECK (default_word_check (brand_name_ru)),
|
||||
UNIQUE (international_name_ru),
|
||||
UNIQUE (international_name_en),
|
||||
UNIQUE (international_name_la),
|
||||
UNIQUE (brand_name_ru)
|
||||
);
|
||||
|
||||
-- Журнал фактических назначений лекарств пациенту
|
||||
CREATE TABLE drug_type_prescription (
|
||||
drug_prescription_id serial PRIMARY KEY,
|
||||
prescription_id int NOT NULL REFERENCES prescription (prescription_id) ON DELETE CASCADE,
|
||||
drug_type_id int NOT NULL REFERENCES drug_type (drug_type_id) ON DELETE RESTRICT,
|
||||
-- количество вещества за приём
|
||||
dose numeric NOT NULL CHECK (dose > 0),
|
||||
-- единица измерения дозы
|
||||
dose_unit_id int NOT NULL REFERENCES unit (unit_id) ON DELETE RESTRICT,
|
||||
-- способ приёма
|
||||
intake_method_id int NOT NULL REFERENCES drug_intake_method (drug_intake_method_id) ON DELETE RESTRICT,
|
||||
-- длительность приёма (в днях)
|
||||
duration_days int NOT NULL CHECK (duration_days > 0),
|
||||
UNIQUE (prescription_id)
|
||||
);
|
||||
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
-- Процедуры (для пациента)
|
||||
-- = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
|
||||
--
|
||||
-- Справочник типов процедур
|
||||
CREATE TABLE procedure_type (
|
||||
procedure_type_id serial PRIMARY KEY,
|
||||
-- наименование процедуры (например, "физиотерапия")
|
||||
name text NOT NULL CHECK (default_word_check (name)),
|
||||
UNIQUE (name)
|
||||
);
|
||||
|
||||
-- Журнал фактических назначений процедур пациенту
|
||||
CREATE TABLE procedure_type_prescription (
|
||||
procedure_prescription_id serial PRIMARY KEY,
|
||||
prescription_id int NOT NULL REFERENCES prescription (prescription_id) ON DELETE CASCADE,
|
||||
procedure_type_id int NOT NULL REFERENCES procedure_type (procedure_type_id) ON DELETE RESTRICT,
|
||||
scheduled_datetime timestamp NOT NULL,
|
||||
UNIQUE (prescription_id)
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
insert into doctor (doctor_id)
|
||||
values (1),
|
||||
(2),
|
||||
(3);
|
||||
|
||||
insert into registrar (registrar_id)
|
||||
values (4),
|
||||
(5),
|
||||
(6);
|
||||
@@ -0,0 +1,82 @@
|
||||
-- Общая функция возвращения всех госпитализаций и периодов пациента
|
||||
CREATE OR REPLACE FUNCTION get_hospitalization_periods (p_patient_id int)
|
||||
RETURNS TABLE (
|
||||
admission_datetime timestamp,
|
||||
discharge_datetime timestamp
|
||||
)
|
||||
AS $$
|
||||
BEGIN
|
||||
RETURN QUERY
|
||||
SELECT
|
||||
ha.admission_datetime,
|
||||
hd.discharge_datetime
|
||||
FROM
|
||||
hospital_admission ha
|
||||
LEFT JOIN hospital_discharge hd ON ha.hospital_admission_id = hd.hospital_admission_id
|
||||
WHERE
|
||||
ha.patient_id = p_patient_id;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
-- Триггер: запрещает новую госпитализацию при наличии незавершённой
|
||||
CREATE OR REPLACE FUNCTION check_active_hospitalization ()
|
||||
RETURNS TRIGGER
|
||||
AS $$
|
||||
DECLARE
|
||||
open_hospitalization boolean;
|
||||
BEGIN
|
||||
SELECT
|
||||
EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
get_hospitalization_periods (NEW.patient_id)
|
||||
WHERE
|
||||
discharge_datetime IS NULL) INTO open_hospitalization;
|
||||
IF open_hospitalization THEN
|
||||
RAISE EXCEPTION
|
||||
USING ERRCODE = 'P0001', MESSAGE = '10001: ACTIVE_HOSPITALIZATION_EXISTS';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
-- Триггер: запрещает перекрытие госпитализаций
|
||||
CREATE OR REPLACE FUNCTION check_hospitalization_overlap ()
|
||||
RETURNS TRIGGER
|
||||
AS $$
|
||||
DECLARE
|
||||
overlap_found boolean;
|
||||
BEGIN
|
||||
SELECT
|
||||
EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
get_hospitalization_periods (NEW.patient_id)
|
||||
WHERE
|
||||
discharge_datetime IS NOT NULL
|
||||
AND NEW.admission_datetime < discharge_datetime
|
||||
AND NEW.admission_datetime > admission_datetime) INTO overlap_found;
|
||||
IF overlap_found THEN
|
||||
RAISE EXCEPTION
|
||||
USING ERRCODE = 'P0001', MESSAGE = '10002: HOSPITALIZATION_OVERLAP';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
-- Подключение триггеров на hospital_admissions
|
||||
CREATE TRIGGER trg_check_active_hospitalization
|
||||
BEFORE INSERT ON hospital_admission
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION check_active_hospitalization ();
|
||||
|
||||
CREATE TRIGGER trg_check_hospitalization_overlap
|
||||
BEFORE INSERT ON hospital_admission
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION check_hospitalization_overlap ();
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
-- Проверка: выписка должна быть позже поступления
|
||||
CREATE OR REPLACE FUNCTION check_discharge_after_admission ()
|
||||
RETURNS TRIGGER
|
||||
AS $$
|
||||
DECLARE
|
||||
admission_time timestamp;
|
||||
BEGIN
|
||||
SELECT
|
||||
admission_datetime INTO admission_time
|
||||
FROM
|
||||
hospital_admission
|
||||
WHERE
|
||||
hospital_admission_id = NEW.hospital_admission_id;
|
||||
IF NEW.discharge_datetime <= admission_time THEN
|
||||
RAISE EXCEPTION
|
||||
USING ERRCODE = 'P0001', MESSAGE = '10003: DISCHARGE_BEFORE_ADMISSION';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_check_discharge_after_admission
|
||||
BEFORE INSERT OR UPDATE ON hospital_discharge
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION check_discharge_after_admission ();
|
||||
|
||||
-- Триггер: нельзя создать выписку, если уже есть выписка по этой госпитализации
|
||||
CREATE OR REPLACE FUNCTION check_unique_discharge ()
|
||||
RETURNS TRIGGER
|
||||
AS $$
|
||||
DECLARE
|
||||
discharge_count integer;
|
||||
BEGIN
|
||||
SELECT
|
||||
COUNT(*) INTO discharge_count
|
||||
FROM
|
||||
hospital_discharge
|
||||
WHERE
|
||||
hospital_admission_id = NEW.hospital_admission_id
|
||||
AND hospital_discharge_id <> COALESCE(NEW.hospital_discharge_id, -1);
|
||||
IF discharge_count > 0 THEN
|
||||
RAISE EXCEPTION
|
||||
USING ERRCODE = 'P0001', MESSAGE = '10004: DISCHARGE_ALREADY_EXISTS';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_check_unique_discharge
|
||||
BEFORE INSERT OR UPDATE ON hospital_discharge
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION check_unique_discharge ();
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
-- Триггер: запрет на запись числового результата, если есть качественный по тому же обследованию
|
||||
CREATE OR REPLACE FUNCTION check_numeric_result_uniqueness ()
|
||||
RETURNS TRIGGER
|
||||
AS $$
|
||||
DECLARE
|
||||
qualitative_exists boolean;
|
||||
BEGIN
|
||||
SELECT
|
||||
EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
qualitative_result
|
||||
WHERE
|
||||
result_id = NEW.result_id) INTO qualitative_exists;
|
||||
IF qualitative_exists THEN
|
||||
RAISE EXCEPTION
|
||||
USING ERRCODE = 'P0001', MESSAGE = '10005: QUALITATIVE_RESULT_ALREADY_EXISTS_FOR_THIS_EXAMINATION';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_check_numeric_result_uniqueness
|
||||
BEFORE INSERT OR UPDATE ON numeric_result
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION check_numeric_result_uniqueness ();
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
-- Триггер: запрет на запись качественного результата, если есть числовой по тому же обследованию
|
||||
CREATE OR REPLACE FUNCTION check_qualitative_result_uniqueness ()
|
||||
RETURNS TRIGGER
|
||||
AS $$
|
||||
DECLARE
|
||||
numeric_exists boolean;
|
||||
BEGIN
|
||||
SELECT
|
||||
EXISTS (
|
||||
SELECT
|
||||
1
|
||||
FROM
|
||||
numeric_result
|
||||
WHERE
|
||||
result_id = NEW.result_id) INTO numeric_exists;
|
||||
IF numeric_exists THEN
|
||||
RAISE EXCEPTION
|
||||
USING ERRCODE = 'P0001', MESSAGE = '10006: NUMERIC_RESULT_ALREADY_EXISTS_FOR_THIS_EXAMINATION';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER trg_check_qualitative_result_uniqueness
|
||||
BEFORE INSERT OR UPDATE ON qualitative_result
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION check_qualitative_result_uniqueness ();
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
-- Перечисление для пола
|
||||
CREATE TYPE gender_enum AS ENUM (
|
||||
'male', -- мужчина
|
||||
'female' -- женщина
|
||||
);
|
||||
|
||||
-- Перечисление для типа обследования
|
||||
CREATE TYPE examination_categories_enum AS ENUM (
|
||||
'physical', -- физикальное обследование
|
||||
'laboratory', -- лабораторное обследование
|
||||
'instrumental' -- инструментальное обследование
|
||||
);
|
||||
|
||||
-- Перечисление для исхода госпитализации (выписка)
|
||||
CREATE TYPE hospital_discharge_reason_enum AS ENUM (
|
||||
'death', -- смерть
|
||||
'improvement', -- улучшение состояния
|
||||
'refusal' -- отказ от госпитализации или дальнейшего лечения
|
||||
);
|
||||
|
||||
-- Перечисление для контекста использования единицы измерения
|
||||
CREATE TYPE unit_context_enum AS ENUM (
|
||||
'examination', -- используется только для обследований (лабораторные, инструментальные, физикальные)
|
||||
'medication' -- используется только для лекарственных средств (дозировка, количество)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user