Загрузка данных
В листинге 35 представлен простой запрос для вывода запчастей с остатками на складе.
Листинг 35
```sql
SELECT
part_name,
manufacturer,
quantity,
sale_price,
purchase_price,
(sale_price - purchase_price) AS markup
FROM spare_parts
WHERE quantity > 0
ORDER BY quantity ASC, part_name;
```
В листинге 36 представлен простой запрос для вывода платежей по заказам.
Листинг 36
```sql
SELECT
p."ID" AS payment_id,
p."order_ID" AS order_id,
p.payment_type,
p.payment_method,
p.amount,
p.date_payment
FROM payments p
JOIN work_orders wo ON p."order_ID" = wo.id
ORDER BY p.date_payment DESC;
```
В листинге 37 представлен простой запрос для вывода заказов поставщикам с их статусом.
Листинг 37
```sql
SELECT
so.id AS supply_order_id,
s.supplier_name,
sp.part_name,
so.order_date,
so.expected_date,
so.status,
so.quantity
FROM supply_orders so
JOIN suppliers s ON so.supplier_id = s.id
JOIN spare_parts sp ON so.part_id = sp.id
ORDER BY so.status, so.order_date DESC;
```
5.5 Сложные запросы
В листинге 38 представлен сложный запрос для вывода топ-5 самых дорогих выполненных заказов с перечнем оказанных услуг.
Листинг 38
```sql
SELECT
wo.id AS order_id,
c.brand || ' ' || c.model AS car,
cl.surname || ' ' || LEFT(cl.first_name, 1) || '.' AS client,
wo.total_cost,
wo.reception_date,
wo.completion_date,
(SELECT STRING_AGG(s.service_name, ', ')
FROM order_services os
JOIN services s ON os.service_id = s.id
WHERE os.order_id = wo.id) AS services_performed
FROM work_orders wo
JOIN cars c ON wo.car_id = c.id
JOIN clients cl ON c.client_id = cl.id
WHERE wo.status = 'Выполнен'
ORDER BY wo.total_cost DESC
LIMIT 5;
```
В листинге 39 представлен сложный запрос для вывода общей прибыли по месяцам за последний год.
Листинг 39
```sql
SELECT
EXTRACT(YEAR FROM wo.reception_date) AS year,
EXTRACT(MONTH FROM wo.reception_date) AS month,
COUNT(*) AS orders_count,
SUM(wo.total_cost) AS total_revenue,
SUM(wo.total_cost) - COALESCE(SUM(op.quantity * sp.purchase_price), 0) AS total_profit
FROM work_orders wo
LEFT JOIN order_parts op ON wo.id = op.order_id
LEFT JOIN spare_parts sp ON op.part_id = sp.id
WHERE wo.reception_date >= CURRENT_DATE - INTERVAL '1 year'
AND wo.status = 'Выполнен'
GROUP BY EXTRACT(YEAR FROM wo.reception_date), EXTRACT(MONTH FROM wo.reception_date)
ORDER BY year DESC, month DESC;
```
В листинге 40 представлен сложный запрос для вывода клиентов, потративших больше среднего, с процентным отклонением.
Листинг 40
```sql
WITH client_spending AS (
SELECT
cl.id,
cl.surname || ' ' || cl.first_name AS full_name,
COUNT(DISTINCT wo.id) AS orders_count,
SUM(wo.total_cost) AS total_spent
FROM clients cl
JOIN cars c ON cl.id = c.client_id
JOIN work_orders wo ON c.id = wo.car_id
WHERE wo.status = 'Выполнен'
GROUP BY cl.id, cl.surname, cl.first_name
),
avg_spending AS (
SELECT AVG(total_spent) AS avg_total_spent
FROM client_spending
)
SELECT
cs.*,
ROUND((cs.total_spent - a.avg_total_spent) / a.avg_total_spent * 100, 2) AS percent_above_avg
FROM client_spending cs
CROSS JOIN avg_spending a
WHERE cs.total_spent > a.avg_total_spent
ORDER BY cs.total_spent DESC;
```
В листинге 41 представлен сложный запрос для выбора наиболее часто используемых запчастей с рекомендацией по пополнению.
Листинг 41
```sql
SELECT
sp.part_name,
sp.manufacturer,
SUM(op.quantity) AS total_used,
sp.quantity AS current_stock,
CASE
WHEN sp.quantity = 0 THEN 'Нет в наличии'
WHEN sp.quantity < 10 THEN 'Мало'
ELSE 'Достаточно'
END AS stock_status,
CASE
WHEN sp.quantity < SUM(op.quantity) / 3.0 THEN 'Требуется пополнение'
ELSE 'Запас достаточный'
END AS reorder_recommendation
FROM spare_parts sp
JOIN order_parts op ON sp.id = op.part_id
GROUP BY sp.id, sp.part_name, sp.manufacturer, sp.quantity
ORDER BY total_used DESC;
```
В листинге 42 представлен сложный запрос для вывода заказов с просроченной датой выполнения.
Листинг 42
```sql
SELECT
wo.id AS order_id,
c.brand || ' ' || c.model AS car,
wo.reception_date,
wo.status,
CURRENT_DATE - wo.expected_completion_date AS days_overdue
FROM work_orders wo
JOIN cars c ON wo.car_id = c.id
WHERE wo.status NOT IN ('Выполнен', 'Отменен')
AND CURRENT_DATE > wo.expected_completion_date
ORDER BY days_overdue DESC;