diff --git a/sports_store.sql b/sports_store.sql new file mode 100644 index 0000000..249cc17 --- /dev/null +++ b/sports_store.sql @@ -0,0 +1,367 @@ +CREATE TYPE person_gender AS ENUM ('M', 'F'); + +CREATE TABLE table_persons ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + first_name TEXT NOT NULL CHECK ( first_name != '' ), + last_name TEXT NOT NULL CHECK ( last_name != '' ), + patronymic TEXT CHECK ( patronymic != '' ), + gender person_gender NOT NULL CHECK ( gender != '' ), + date_of_birth DATE, + is_deleted BOOLEAN NOT NULL DEFAULT FALSE +); + +CREATE TABLE table_emails ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + person_id INT NOT NULL, + email TEXT NOT NULL CHECK ( email LIKE '%@%' ), + FOREIGN KEY (person_id) REFERENCES table_persons(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION +); + +CREATE TABLE table_phone_numbers ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + person_id INT NOT NULL, + phone_number TEXT NOT NULL CHECK ( phone_number != '' ), + FOREIGN KEY (person_id) REFERENCES table_persons(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION +); + +CREATE TABLE table_manufacturers ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name TEXT NOT NULL CHECK ( name != '' ), + address TEXT CHECK ( address != '' ), + contact_person_id INT NOT NULL, + description TEXT CHECK ( name != '' ), + FOREIGN KEY (contact_person_id) REFERENCES table_persons(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION +); + +CREATE TABLE table_product_types ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name TEXT NOT NULL CHECK ( name != '' ), + description TEXT CHECK ( name != '' ) +); + +CREATE TABLE table_products ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name TEXT NOT NULL CHECK ( name != '' ), + type_id INT NOT NULL, + amount_in_stock INT NOT NULL CHECK ( amount_in_stock >= 0 ), + cost_price NUMERIC NOT NULL CHECK ( cost_price >= 0.0 ), + manufacturer_id INT NOT NULL, + price NUMERIC NOT NULL CHECK ( price >= 0.0 ), + price_reason TEXT CHECK ( price_reason != '' ), + price_set_employee_id INT NOT NULL, + measurement_unit TEXT CHECK ( name != '' ), + measurement_value NUMERIC CHECK ( measurement_value >= 0.0 ), + is_archived BOOLEAN NOT NULL DEFAULT FALSE, + FOREIGN KEY (type_id) REFERENCES table_product_types(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + FOREIGN KEY (manufacturer_id) REFERENCES table_manufacturers(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + FOREIGN KEY (price_set_employee_id) REFERENCES table_employees(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION +); + +-- Товар должен помещаться в архив, когда его количество становится 0 +CREATE TABLE table_archived_products_log ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + product_id INT NOT NULL, + change_date DATE NOT NULL, + FOREIGN KEY (product_id) REFERENCES table_products(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION +); + +CREATE TABLE table_positions ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name TEXT NOT NULL CHECK ( name != '' ), + level TEXT NOT NULL CHECK ( level != '' ), + average_salary NUMERIC NOT NULL CHECK ( average_salary >= 0.0 ), + requirements TEXT NOT NULL CHECK ( requirements != '' ), + tasks TEXT NOT NULL CHECK ( tasks != '' ), + is_hiring BOOLEAN NOT NULL DEFAULT FALSE +); + +CREATE TABLE table_employees ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + person_id INT NOT NULL, + position_id INT NOT NULL, + hiring_date DATE NOT NULL, + salary NUMERIC NOT NULL CHECK ( salary >= 0.0 ), + supervisor_id INT NOT NULL, + position_reason TEXT CHECK ( position_reason != '' ), + is_deleted BOOLEAN NOT NULL DEFAULT FALSE, + FOREIGN KEY (person_id) REFERENCES table_persons(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + FOREIGN KEY (position_id) REFERENCES table_positions(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + FOREIGN KEY (supervisor_id) REFERENCES table_employees(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION +); + +CREATE TABLE table_employee_history ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + employee_id INT NOT NULL, + supervisor_id INT NOT NULL, + position_id INT NOT NULL, + salary NUMERIC NOT NULL CHECK ( salary >= 0.0 ), + change_date DATE NOT NULL, + change_reason TEXT CHECK ( change_reason != '' ), + FOREIGN KEY (employee_id) REFERENCES table_employees(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + FOREIGN KEY (supervisor_id) REFERENCES table_employees(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + FOREIGN KEY (position_id) REFERENCES table_positions(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION +); + +CREATE TABLE table_price_history ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + product_id INT NOT NULL, + employee_id INT NOT NULL, + cost_price NUMERIC NOT NULL CHECK ( cost_price >= 0.0 ), + price NUMERIC NOT NULL CHECK ( price >= 0.0 ), + change_date DATE NOT NULL, + change_reason TEXT CHECK ( change_reason != '' ), + FOREIGN KEY (product_id) REFERENCES table_products(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + FOREIGN KEY (employee_id) REFERENCES table_employees(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION +); + +CREATE TABLE table_clients ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + person_id INT NOT NULL, + discount_percent NUMERIC(3, 2) NOT NULL CHECK ( discount_percent < 1.00 AND discount_percent >= 0.00) DEFAULT 0.00, + is_subscribed_to_mailing_list BOOLEAN NOT NULL DEFAULT FALSE, + is_deleted BOOLEAN NOT NULL DEFAULT FALSE, + FOREIGN KEY (person_id) REFERENCES table_persons(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION +); + +CREATE TABLE table_orders ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + items_price NUMERIC NOT NULL CHECK ( items_price >= 0.0 ), + items_amount INT NOT NULL CHECK ( items_amount >= 0 ), + date DATE NOT NULL, + is_payed BOOLEAN NOT NULL DEFAULT FALSE, + is_client_registered BOOLEAN NOT NULL DEFAULT FALSE, + client_id INT, + FOREIGN KEY (client_id) REFERENCES table_clients(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION +); + +CREATE TABLE table_order_items ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + order_id INT NOT NULL, + product_id INT NOT NULL, + amount INT NOT NULL CHECK ( amount >= 0 ), + FOREIGN KEY (order_id) REFERENCES table_orders(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + FOREIGN KEY (product_id) REFERENCES table_products(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION +); + +CREATE TABLE table_sales ( + id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + order_id INT NOT NULL, + total_price NUMERIC NOT NULL CHECK ( total_price >= 0.0 ), + payment_type TEXT NOT NULL CHECK ( payment_type != '' ), + employee_id INT NOT NULL, + date DATE NOT NULL, + FOREIGN KEY (order_id) REFERENCES table_orders(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION, + FOREIGN KEY (employee_id) REFERENCES table_employees(id) + ON DELETE NO ACTION + ON UPDATE NO ACTION +); + +CREATE FUNCTION function_archive_product() + RETURNS TRIGGER AS +$$ +BEGIN + IF (tg_table_name == 'table_products' + AND tg_op = 'UPDATE' + AND NEW.amount_in_stock != OLD.amount_in_stock) THEN + IF (NEW.amount_in_stock = 0 AND NEW.is_archived = FALSE) THEN + NEW.is_archived = TRUE; + INSERT INTO table_archived_products_log(product_id, change_date) + VALUES (NEW.id, CURRENT_DATE); + ELSIF (NEW.amount_in_stock > 0 AND NEW.is_archived = TRUE) THEN + NEW.is_archived = FALSE; + END IF; + ELSIF (tg_table_name = 'table_products' AND tg_op = 'INSERT') THEN + IF (NEW.amount_in_stock = 0 AND NEW.is_archived = FALSE) THEN + NEW.is_archived = TRUE; + INSERT INTO table_archived_products_log(product_id, change_date) + VALUES (NEW.id, CURRENT_DATE); + ELSIF (NEW.amount_in_stock > 0 AND NEW.is_archived = TRUE) THEN + NEW.is_archived = FALSE; + END IF; + END IF; + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; + +CREATE TRIGGER trigger_archive_product + BEFORE UPDATE OR INSERT + ON table_products + FOR EACH ROW + EXECUTE FUNCTION function_archive_product(); + +CREATE FUNCTION function_log_price_history() +RETURNS TRIGGER AS +$$ +BEGIN + IF (tg_table_name == 'table_products' AND tg_op = 'UPDATE') THEN + IF (NEW.price != OLD.price AND NEW.cost_price != OLD.cost_price) THEN + INSERT INTO table_price_history(product_id, employee_id, + cost_price, price, + change_date, change_reason) + VALUES (NEW.id, NEW.price_set_employee_id, + NEW.cost_price, NEW.price, CURRENT_DATE, + NEW.price_reason); + ELSIF (NEW.price != OLD.price) THEN + INSERT INTO table_price_history(product_id, employee_id, + cost_price, price, + change_date, change_reason) + VALUES (NEW.id, NEW.price_set_employee_id, + NEW.cost_price, NEW.price, CURRENT_DATE, + NEW.price_reason); + ELSIF (NEW.cost_price != OLD.cost_price) THEN + INSERT INTO table_price_history(product_id, employee_id, + cost_price, price, + change_date, change_reason) + VALUES (NEW.id, NEW.price_set_employee_id, + NEW.cost_price, NEW.price, CURRENT_DATE, + NEW.price_reason); + END IF; + ELSIF (tg_table_name == 'table_products' AND tg_op = 'INSERT') THEN + INSERT INTO table_price_history(product_id, employee_id, + cost_price, price, + change_date, change_reason) + VALUES (NEW.id, NEW.price_set_employee_id, + NEW.cost_price, NEW.price, CURRENT_DATE, + NEW.price_reason); + END IF; + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; + +CREATE TRIGGER trigger_log_price_history + BEFORE UPDATE OR INSERT + ON table_products + FOR EACH ROW + EXECUTE FUNCTION function_log_price_history(); + +CREATE FUNCTION function_log_employee_history() +RETURNS TRIGGER AS +$$ +BEGIN + IF (tg_table_name == 'table_employees' AND tg_op = 'UPDATE') THEN + IF (NEW.position_id != OLD.position_id AND NEW.salary != OLD.salary) THEN + INSERT INTO table_employee_history(employee_id, supervisor_id, + position_id, salary, + change_date, change_reason) + VALUES (NEW.id, NEW.supervisor_id, + NEW.position_id, NEW.salary, CURRENT_DATE, + NEW.position_reason); + ELSIF (NEW.position_id != OLD.position_id) THEN + INSERT INTO table_employee_history(employee_id, supervisor_id, + position_id, salary, + change_date, change_reason) + VALUES (NEW.id, NEW.supervisor_id, + NEW.position_id, NEW.salary, CURRENT_DATE, + NEW.position_reason); + ELSIF (NEW.salary != OLD.salary) THEN + INSERT INTO table_employee_history(employee_id, supervisor_id, + position_id, salary, + change_date, change_reason) + VALUES (NEW.id, NEW.supervisor_id, + NEW.position_id, NEW.salary, CURRENT_DATE, + NEW.position_reason); + END IF; + ELSIF (tg_table_name == 'table_employees' AND tg_op = 'INSERT') THEN + INSERT INTO table_employee_history(employee_id, supervisor_id, + position_id, salary, + change_date, change_reason) + VALUES (NEW.id, NEW.supervisor_id, + NEW.position_id, NEW.salary, CURRENT_DATE, + NEW.position_reason); + END IF; + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; + +CREATE TRIGGER trigger_log_employee_history + BEFORE UPDATE OR INSERT + ON table_employees + FOR EACH ROW + EXECUTE FUNCTION function_log_employee_history(); + +CREATE VIEW view_product_price_history AS + SELECT table_products.id AS product_id, + table_products.name AS name, + table_product_types.name AS type, + table_product_types.description AS description, + table_products.amount_in_stock AS amount_in_stock, + table_manufacturers.name AS manufacturer, + table_persons.id AS person_id, + table_persons.first_name AS price_change_employee_first_name, + table_persons.last_name AS price_change_employee_last_name, + table_persons.patronymic AS price_change_employee_patronymic, + table_price_history.cost_price AS cost_price, + table_price_history.price AS selling_price, + table_price_history.change_date AS change_date, + table_price_history.change_reason AS change_reason, + table_products.measurement_unit AS measurement_unit, + table_products.measurement_value AS measurement_value, + table_products.is_archived AS is_archived + FROM table_price_history + JOIN table_products ON table_price_history.product_id = table_products.id + JOIN table_product_types ON table_products.type_id = table_product_types.id + JOIN table_manufacturers ON table_products.manufacturer_id = table_manufacturers.id + JOIN table_employees ON table_price_history.employee_id = table_employees.id + JOIN table_persons ON table_employees.person_id = table_persons.id; + +CREATE VIEW view_employee_history AS + SELECT table_employees.id AS employee_id, + table_persons.id AS person_id, + table_persons.first_name AS first_name, + table_persons.last_name AS last_name, + table_persons.patronymic AS patronymic, + table_persons.gender AS gender, + table_persons.date_of_birth AS date_of_birth, + table_employee_history.supervisor_id as supervisor_id, + table_positions.name AS position, + table_employee_history.salary as salary, + table_employee_history.change_date as change_date, + table_employee_history.change_reason as change_reason + FROM table_employee_history + JOIN table_employees ON table_employee_history.employee_id = table_employees.id + JOIN table_persons ON table_employees.person_id = table_persons.id + JOIN table_positions ON table_employee_history.position_id = table_positions.id +