| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- -- ============================================================
- -- БАЗА ДАННЫХ: Продажа компьютерной техники
- -- СУБД: PostgreSQL
- -- ============================================================
- DROP TABLE IF EXISTS order_items CASCADE;
- DROP TABLE IF EXISTS orders CASCADE;
- DROP TABLE IF EXISTS app_users CASCADE;
- DROP TABLE IF EXISTS products CASCADE;
- DROP TABLE IF EXISTS suppliers CASCADE;
- DROP TABLE IF EXISTS clients CASCADE;
- DROP TABLE IF EXISTS employees CASCADE;
- DROP VIEW IF EXISTS v_orders_full_info CASCADE;
- DROP VIEW IF EXISTS v_late_orders CASCADE;
- DROP VIEW IF EXISTS v_order_items_full CASCADE;
- DROP VIEW IF EXISTS v_products_dictionary CASCADE;
- DROP FUNCTION IF EXISTS fn_get_late_orders_by_client(INTEGER);
- DROP FUNCTION IF EXISTS fn_calculate_order_total(INTEGER);
- DROP FUNCTION IF EXISTS fn_update_order_total();
- DROP FUNCTION IF EXISTS fn_reduce_product_stock();
- CREATE TABLE suppliers (
- supplier_id SERIAL PRIMARY KEY,
- company_name VARCHAR(150) NOT NULL,
- contact_person VARCHAR(100) NOT NULL,
- phone VARCHAR(20) NOT NULL,
- address VARCHAR(250) NOT NULL
- );
- CREATE TABLE products (
- product_id SERIAL PRIMARY KEY,
- product_name VARCHAR(200) NOT NULL,
- price NUMERIC(12,2) NOT NULL CHECK (price >= 0),
- stock_quantity INTEGER NOT NULL DEFAULT 0 CHECK (stock_quantity >= 0),
- category VARCHAR(100) NOT NULL,
- supplier_id INTEGER NOT NULL REFERENCES suppliers(supplier_id) ON UPDATE CASCADE
- );
- CREATE TABLE clients (
- client_id SERIAL PRIMARY KEY,
- first_name VARCHAR(80) NOT NULL,
- last_name VARCHAR(80) NOT NULL,
- address VARCHAR(250) NOT NULL,
- phone VARCHAR(20),
- email VARCHAR(150) UNIQUE
- );
- CREATE TABLE employees (
- employee_id SERIAL PRIMARY KEY,
- last_name VARCHAR(80) NOT NULL,
- first_name VARCHAR(80) NOT NULL,
- phone VARCHAR(20),
- email VARCHAR(150) UNIQUE,
- position VARCHAR(100) NOT NULL
- );
- CREATE TABLE orders (
- order_id SERIAL PRIMARY KEY,
- order_date TIMESTAMP NOT NULL DEFAULT NOW(),
- status VARCHAR(50) NOT NULL DEFAULT 'новый'
- CHECK (status IN ('новый','в обработке','отправлен','выполнен','отменён')),
- total_cost NUMERIC(14,2) NOT NULL DEFAULT 0 CHECK (total_cost >= 0),
- client_id INTEGER NOT NULL REFERENCES clients(client_id),
- employee_id INTEGER NOT NULL REFERENCES employees(employee_id)
- );
- CREATE TABLE order_items (
- order_item_id SERIAL PRIMARY KEY,
- order_id INTEGER NOT NULL REFERENCES orders(order_id) ON DELETE CASCADE,
- product_id INTEGER NOT NULL REFERENCES products(product_id),
- unit_price NUMERIC(12,2) NOT NULL CHECK (unit_price >= 0),
- quantity INTEGER NOT NULL CHECK (quantity > 0)
- );
- CREATE TABLE app_users (
- user_id SERIAL PRIMARY KEY,
- login VARCHAR(50) NOT NULL UNIQUE,
- password_hash TEXT NOT NULL,
- role_name VARCHAR(30) NOT NULL CHECK (role_name IN ('admin','manager','viewer'))
- );
- CREATE OR REPLACE VIEW v_orders_full_info AS
- SELECT
- o.order_id,
- o.order_date,
- o.status,
- o.total_cost,
- c.client_id,
- c.last_name || ' ' || c.first_name AS client_name,
- c.phone AS client_phone,
- c.email AS client_email,
- e.employee_id,
- e.last_name || ' ' || e.first_name AS employee_name,
- e.position AS employee_position
- FROM orders o
- JOIN clients c ON o.client_id = c.client_id
- JOIN employees e ON o.employee_id = e.employee_id;
- CREATE OR REPLACE VIEW v_late_orders AS
- SELECT *
- FROM v_orders_full_info
- WHERE status NOT IN ('выполнен', 'отменён')
- AND order_date < NOW() - INTERVAL '7 days';
- CREATE OR REPLACE VIEW v_order_items_full AS
- SELECT
- oi.order_item_id,
- oi.order_id,
- p.product_id,
- p.product_name,
- p.category,
- oi.unit_price,
- oi.quantity,
- oi.unit_price * oi.quantity AS item_sum
- FROM order_items oi
- JOIN products p ON oi.product_id = p.product_id;
- CREATE OR REPLACE VIEW v_products_dictionary AS
- SELECT
- p.product_id,
- p.product_name,
- p.category,
- p.price,
- p.stock_quantity,
- s.supplier_id,
- s.company_name AS supplier_name,
- s.contact_person,
- s.phone AS supplier_phone,
- s.address AS supplier_address
- FROM products p
- JOIN suppliers s ON p.supplier_id = s.supplier_id;
- CREATE OR REPLACE FUNCTION fn_get_late_orders_by_client(p_client_id INTEGER)
- RETURNS TABLE (
- order_id INTEGER,
- order_date TIMESTAMP,
- status VARCHAR,
- total_cost NUMERIC,
- client_id INTEGER,
- client_name TEXT,
- client_phone VARCHAR,
- client_email VARCHAR,
- employee_id INTEGER,
- employee_name TEXT
- )
- LANGUAGE plpgsql
- AS $$
- BEGIN
- RETURN QUERY
- SELECT
- v.order_id,
- v.order_date,
- v.status,
- v.total_cost,
- v.client_id,
- v.client_name,
- v.client_phone,
- v.client_email,
- v.employee_id,
- v.employee_name
- FROM v_late_orders v
- WHERE v.client_id = p_client_id
- ORDER BY v.order_date DESC;
- END;
- $$;
- CREATE OR REPLACE FUNCTION fn_calculate_order_total(p_order_id INTEGER)
- RETURNS NUMERIC
- LANGUAGE plpgsql
- AS $$
- DECLARE
- v_total NUMERIC(14,2);
- BEGIN
- SELECT COALESCE(SUM(unit_price * quantity), 0)
- INTO v_total
- FROM order_items
- WHERE order_id = p_order_id;
- RETURN v_total;
- END;
- $$;
- CREATE OR REPLACE FUNCTION fn_update_order_total()
- RETURNS TRIGGER
- LANGUAGE plpgsql
- AS $$
- DECLARE
- v_order_id INTEGER;
- BEGIN
- v_order_id := COALESCE(NEW.order_id, OLD.order_id);
- UPDATE orders
- SET total_cost = fn_calculate_order_total(v_order_id)
- WHERE order_id = v_order_id;
- RETURN COALESCE(NEW, OLD);
- END;
- $$;
- CREATE OR REPLACE FUNCTION fn_reduce_product_stock()
- RETURNS TRIGGER
- LANGUAGE plpgsql
- AS $$
- DECLARE
- v_stock INTEGER;
- BEGIN
- SELECT stock_quantity INTO v_stock
- FROM products
- WHERE product_id = NEW.product_id;
- IF v_stock IS NULL THEN
- RAISE EXCEPTION 'Товар не найден';
- END IF;
- IF v_stock < NEW.quantity THEN
- RAISE EXCEPTION 'Недостаточно товара на складе. Доступно: %, требуется: %', v_stock, NEW.quantity;
- END IF;
- UPDATE products
- SET stock_quantity = stock_quantity - NEW.quantity
- WHERE product_id = NEW.product_id;
- RETURN NEW;
- END;
- $$;
- CREATE TRIGGER trg_update_order_total_insert
- AFTER INSERT ON order_items
- FOR EACH ROW
- EXECUTE FUNCTION fn_update_order_total();
- CREATE TRIGGER trg_update_order_total_update
- AFTER UPDATE ON order_items
- FOR EACH ROW
- EXECUTE FUNCTION fn_update_order_total();
- CREATE TRIGGER trg_update_order_total_delete
- AFTER DELETE ON order_items
- FOR EACH ROW
- EXECUTE FUNCTION fn_update_order_total();
- CREATE TRIGGER trg_reduce_product_stock
- BEFORE INSERT ON order_items
- FOR EACH ROW
- EXECUTE FUNCTION fn_reduce_product_stock();
- CREATE INDEX idx_orders_client_id ON orders(client_id);
- CREATE INDEX idx_orders_employee_id ON orders(employee_id);
- CREATE INDEX idx_orders_status ON orders(status);
- CREATE INDEX idx_orders_order_date ON orders(order_date);
- CREATE INDEX idx_products_supplier_id ON products(supplier_id);
- CREATE INDEX idx_products_category ON products(category);
- CREATE INDEX idx_order_items_order_id ON order_items(order_id);
- CREATE INDEX idx_order_items_product_id ON order_items(product_id);
- INSERT INTO suppliers(company_name, contact_person, phone, address) VALUES
- ('TechImport', 'Иванов Сергей', '+79001112233', 'Москва, ул. Ленина, д. 10'),
- ('CompMarket', 'Петров Алексей', '+79004445566', 'Санкт-Петербург, Невский пр., д. 20'),
- ('DigitalTrade', 'Соколова Анна', '+79007778899', 'Великий Новгород, ул. Большая Санкт-Петербургская, д. 15');
- INSERT INTO products(product_name, price, stock_quantity, category, supplier_id) VALUES
- ('Ноутбук Lenovo IdeaPad 3', 65000.00, 15, 'Ноутбуки', 1),
- ('Ноутбук ASUS VivoBook', 72000.00, 10, 'Ноутбуки', 1),
- ('Монитор Samsung 24', 18000.00, 30, 'Мониторы', 2),
- ('Монитор LG UltraGear', 26000.00, 12, 'Мониторы', 2),
- ('Клавиатура Logitech K120', 1500.00, 50, 'Периферия', 3),
- ('Мышь Logitech M185', 1200.00, 60, 'Периферия', 3),
- ('SSD Kingston 1TB', 7200.00, 40, 'Комплектующие', 2),
- ('Видеокарта MSI RTX 4060', 42000.00, 8, 'Комплектующие', 1);
- INSERT INTO clients(first_name, last_name, address, phone, email) VALUES
- ('Андрей', 'Смирнов', 'Великий Новгород, ул. Мира, д. 5', '+79110000001', 'smirnov@mail.ru'),
- ('Ольга', 'Кузнецова', 'Великий Новгород, ул. Парковая, д. 8', '+79110000002', 'kuznetsova@mail.ru'),
- ('Иван', 'Павлов', 'Великий Новгород, ул. Ломоносова, д. 3', '+79110000003', 'pavlov@mail.ru'),
- ('Мария', 'Васильева', 'Великий Новгород, ул. Попова, д. 12', '+79110000004', 'vasilieva@mail.ru');
- INSERT INTO employees(last_name, first_name, phone, email, position) VALUES
- ('Иванова', 'Мария', '+79220000001', 'ivanova@shop.ru', 'Менеджер'),
- ('Соколов', 'Дмитрий', '+79220000002', 'sokolov@shop.ru', 'Администратор'),
- ('Фёдорова', 'Елена', '+79220000003', 'fedorova@shop.ru', 'Менеджер');
- INSERT INTO orders(order_date, status, client_id, employee_id) VALUES
- (NOW() - INTERVAL '12 days', 'в обработке', 1, 1),
- (NOW() - INTERVAL '10 days', 'отправлен', 1, 1),
- (NOW() - INTERVAL '3 days', 'новый', 2, 3),
- (NOW() - INTERVAL '15 days', 'выполнен', 2, 1),
- (NOW() - INTERVAL '20 days', 'в обработке', 3, 3),
- (NOW() - INTERVAL '4 days', 'отменён', 4, 1);
- INSERT INTO order_items(order_id, product_id, unit_price, quantity) VALUES
- (1, 1, 65000.00, 1),
- (1, 5, 1500.00, 2),
- (2, 3, 18000.00, 2),
- (3, 6, 1200.00, 1),
- (3, 7, 7200.00, 1),
- (4, 2, 72000.00, 1),
- (5, 8, 42000.00, 1),
- (5, 4, 26000.00, 1),
- (6, 5, 1500.00, 1);
- -- ============================================================
- -- ПОЛЬЗОВАТЕЛИ ПРИЛОЖЕНИЯ
- -- Роли PostgreSQL не создаются, так как в учебной БД это запрещено.
- -- Разграничение прав выполняется на уровне приложения через app_users.role_name.
- -- Пароли для проверки: admin123, manager123, viewer123.
- -- ============================================================
- DELETE FROM app_users;
- INSERT INTO app_users(login, password_hash, role_name) VALUES
- ('admin', '240be518fabd2724ddb6f04eeb1da5967448d7e831c08c8fa822809f74c720a9', 'admin'),
- ('manager', '866485796cfa8d7c0cf7111640205b83076433547577511d81f8030ae99ecea5', 'manager'),
- ('viewer', '65375049b9e4d7cad6c9ba286fdeb9394b28135a3e84136404cfccfdcc438894', 'viewer');
- -- Проверочные запросы
- SELECT * FROM app_users;
- SELECT * FROM v_orders_full_info;
- SELECT * FROM v_late_orders;
- SELECT * FROM v_products_dictionary;
- SELECT * FROM fn_get_late_orders_by_client(1);
|