← Назад к вопросам

Какой сложности запросы писал на PostgreSQL?

3.0 Senior🔥 121 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Ответ

В своей практике я работал с запросами различной сложности в PostgreSQL, от простых CRUD операций до сложных аналитических запросов. Давайте разберем примеры.

Уровень 1: Базовые CRUD операции

-- SELECT с WHERE
SELECT * FROM users WHERE email = 'user@example.com';

-- INSERT
INSERT INTO users (name, email, created_at) 
VALUES ('John Doe', 'john@example.com', NOW());

-- UPDATE
UPDATE users SET name = 'Jane Doe' WHERE id = 1;

-- DELETE
DELETE FROM users WHERE id = 1;

Уровень 2: Джойны и агрегация

-- INNER JOIN с GROUP BY
SELECT 
    d.name,
    COUNT(u.id) as employee_count,
    AVG(u.salary) as avg_salary
FROM departments d
INNER JOIN users u ON d.id = u.department_id
GROUP BY d.id, d.name
HAVING COUNT(u.id) > 5
ORDER BY avg_salary DESC;

-- LEFT JOIN с фильтром
SELECT 
    u.name,
    COUNT(p.id) as post_count
FROM users u
LEFT JOIN posts p ON u.id = p.user_id AND p.status = 'published'
GROUP BY u.id, u.name;

Уровень 3: Оконные функции (Window Functions)

Оконные функции очень мощны для аналитики данных.

-- ROW_NUMBER для нумерации
SELECT 
    id,
    name,
    salary,
    ROW_NUMBER() OVER (ORDER BY salary DESC) as salary_rank
FROM users
WHERE department_id = 1;

-- RANK vs DENSE_RANK (разница при одинаковых значениях)
SELECT 
    id,
    name,
    salary,
    RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) as rank,
    DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) as dense_rank
FROM users;

-- LAG/LEAD для доступа к предыдущим/следующим строкам
SELECT 
    id,
    name,
    salary,
    LAG(salary) OVER (ORDER BY hire_date) as prev_salary,
    LEAD(salary) OVER (ORDER BY hire_date) as next_salary,
    salary - LAG(salary) OVER (ORDER BY hire_date) as salary_change
FROM users;

-- SUM OVER для скользящей суммы
SELECT 
    id,
    created_at,
    amount,
    SUM(amount) OVER (
        ORDER BY created_at 
        ROWS BETWEEN 7 PRECEDING AND CURRENT ROW
    ) as rolling_7day_sum
FROM transactions;

Уровень 4: Рекурсивные CTE (Common Table Expressions)

Используется для иерархических данных.

-- Получить всех подчиненных и их подчиненных
WITH RECURSIVE employee_hierarchy AS (
    -- Базовый случай: босс
    SELECT id, name, manager_id, 1 as level
    FROM employees
    WHERE manager_id IS NULL
    
    UNION ALL
    
    -- Рекурсивный случай: подчиненные
    SELECT e.id, e.name, e.manager_id, eh.level + 1
    FROM employees e
    INNER JOIN employee_hierarchy eh ON e.manager_id = eh.id
    WHERE eh.level < 10  -- ограничение глубины
)
SELECT * FROM employee_hierarchy ORDER BY level, name;

-- Поиск пути в графе
WITH RECURSIVE graph AS (
    SELECT id, parent_id, 1 as depth, ARRAY[id] as path
    FROM categories
    WHERE id = 1
    
    UNION ALL
    
    SELECT c.id, c.parent_id, g.depth + 1, g.path || c.id
    FROM categories c
    INNER JOIN graph g ON c.parent_id = g.id
    WHERE g.depth < 10
)
SELECT * FROM graph;

Уровень 5: JSON операции и поиск

PostgreSQL имеет встроенную поддержку JSON.

-- Работа с JSON колонками
SELECT 
    id,
    data->>'name' as name,
    data->'settings'->>'theme' as theme,
    (data->>'age')::integer as age
FROM users
WHERE data->>'role' = 'admin';

-- JSONB с операторами
SELECT *
FROM users
WHERE data @> '{"status": "active"}';

-- JSON агрегация
SELECT 
    department_id,
    json_agg(
        json_build_object(
            'id', id,
            'name', name,
            'salary', salary
        )
    ) as employees
FROM users
GROUP BY department_id;

Уровень 6: Полнотекстовый поиск (Full-Text Search)

-- Индекс FTS
CREATE INDEX idx_articles_fts ON articles 
    USING GIN (to_tsvector('russian', content));

-- Поиск
SELECT 
    id,
    title,
    ts_rank(to_tsvector('russian', content), query) as rank
FROM articles,
to_tsquery('russian', 'базы & данные') query
WHERE to_tsvector('russian', content) @@ query
ORDER BY rank DESC;

-- Поиск с фонетикой (требует расширение)
SELECT *
FROM articles
WHERE soundex(title) = soundex('PostgreSQL');

Уровень 7: Сложные аналитические запросы

-- Когортный анализ
WITH user_cohorts AS (
    SELECT 
        id,
        DATE_TRUNC('month', created_at) as cohort_month
    FROM users
),
user_activity AS (
    SELECT 
        u.id,
        u.cohort_month,
        DATE_TRUNC('month', o.created_at) as activity_month,
        EXTRACT(MONTH FROM AGE(DATE_TRUNC('month', o.created_at), u.cohort_month)) as months_since_signup
    FROM user_cohorts u
    LEFT JOIN orders o ON u.id = o.user_id
)
SELECT 
    cohort_month,
    months_since_signup,
    COUNT(DISTINCT id) as users
FROM user_activity
GROUP BY cohort_month, months_since_signup
ORDER BY cohort_month, months_since_signup;

-- Анализ воронки
WITH funnel AS (
    SELECT 
        user_id,
        'visit' as event,
        created_at,
        LAG(created_at) OVER (PARTITION BY user_id ORDER BY created_at) as prev_event_time
    FROM events
    WHERE event_type IN ('visit', 'signup', 'purchase')
)
SELECT 
    user_id,
    COUNT(*) as total_events,
    COUNT(*) FILTER (WHERE event = 'visit') as visits,
    COUNT(*) FILTER (WHERE event = 'signup') as signups,
    COUNT(*) FILTER (WHERE event = 'purchase') as purchases
FROM funnel
GROUP BY user_id;

Уровень 8: Оптимизация сложных запросов

-- EXPLAIN ANALYZE для анализа производительности
EXPLAIN ANALYZE
SELECT u.id, u.name, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2024-01-01'
GROUP BY u.id
HAVING COUNT(o.id) > 10;

-- Использование MATERIALIZED VIEW для кеша сложных запросов
CREATE MATERIALIZED VIEW user_order_stats AS
SELECT 
    u.id,
    u.name,
    COUNT(o.id) as order_count,
    SUM(o.amount) as total_spent,
    MAX(o.created_at) as last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name;

CREATE INDEX idx_user_order_stats ON user_order_stats(order_count);

Мой опыт

В реальных проектах я работал с:

  • E-commerce платформы: запросы для аналитики продаж, когортного анализа, рекомендаций
  • CRM системы: иерархические запросы для организационных структур, поиск контактов с FTS
  • Аналитические дашборды: сложные агрегации, оконные функции, MATERIALIZED VIEW'ы
  • Системы реал-тайм: оптимизация запросов через индексы и правильное использование EXPLAIN

Главное: всегда профилируй запросы через EXPLAIN ANALYZE перед production, используй индексы правильно, и не пиши запросы, которые нельзя оптимизировать.