Временные таблицы в SQL — когда и зачем использовать

Что такое временная таблица

Временная таблица (TEMP TABLE) — это таблица, которая существует только в рамках текущей сессии (или транзакции). При закрытии соединения с базой она автоматически удаляется. Другие пользователи её не видят.

Аналитику временные таблицы нужны, когда промежуточные данные слишком велики или сложны, чтобы держать их в подзапросе, и к ним нужно обращаться несколько раз.

CREATE TEMP TABLE — синтаксис

CREATE TEMP TABLE active_users AS
SELECT user_id, COUNT(*) AS sessions
FROM user_sessions
WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY user_id;

После создания active_users работает как обычная таблица — можно делать SELECT, JOIN, создавать индексы:

-- Добавить индекс для ускорения JOIN
CREATE INDEX ON active_users (user_id);

-- Использовать в запросе
SELECT u.name, a.sessions
FROM users u
JOIN active_users a ON u.user_id = a.user_id
WHERE a.sessions > 10;

Можно также создать пустую временную таблицу с явной структурой:

CREATE TEMP TABLE staging_orders (
    order_id   INT,
    user_id    INT,
    amount     NUMERIC,
    created_at TIMESTAMP
);

Область видимости

Временная таблица видна только в текущей сессии. Два аналитика, подключённых к одной базе, могут создать TEMP TABLE с одинаковым именем — и они не будут мешать друг другу. При разрыве соединения таблица исчезает.

В PostgreSQL можно ограничить жизнь ещё сильнее:

CREATE TEMP TABLE tmp_data (...) ON COMMIT DROP;
-- таблица удалится при завершении транзакции

CTE как альтернатива

CTE (WITH) решает похожую задачу — даёт имя промежуточному набору данных. Но CTE существует только внутри одного запроса:

WITH active_users AS (
  SELECT user_id, COUNT(*) AS sessions
  FROM user_sessions
  WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
  GROUP BY user_id
)
SELECT u.name, a.sessions
FROM users u
JOIN active_users a ON u.user_id = a.user_id
WHERE a.sessions > 10;

Результат тот же, но CTE нельзя использовать в следующем запросе — он исчезает сразу после выполнения.

TEMP TABLE vs CTE — сравнение

TEMP TABLE CTE (WITH)
Время жизни Сессия (или транзакция) Один запрос
Повторное использование Да, в нескольких запросах Только внутри одного запроса
Индексы Можно создавать Нельзя
Хранение На диске (или в памяти) В памяти, в рамках выполнения
Производительность на больших данных Лучше (индексы, статистика) Хуже при многократном обращении
Видимость другим сессиям Нет Нет
Нужен DDL (CREATE) Да Нет

Когда использовать TEMP TABLE

Несколько запросов к одним данным. Посчитали агрегат — и нужно обратиться к нему из 3-4 разных SELECT. CTE придётся дублировать, а TEMP TABLE создаётся один раз.

Нужен индекс. Если промежуточный результат большой и вы делаете JOIN по нему — индекс на TEMP TABLE ускорит запрос в разы.

Сложный пайплайн. Когда анализ состоит из нескольких этапов: сначала выгрузка, потом фильтрация, потом агрегация — каждый шаг удобно класть в отдельную TEMP TABLE.

-- Шаг 1: собрать данные
CREATE TEMP TABLE raw_events AS
SELECT * FROM events WHERE event_date >= '2026-01-01';

-- Шаг 2: агрегировать
CREATE TEMP TABLE user_metrics AS
SELECT user_id, COUNT(*) AS events, COUNT(DISTINCT event_date) AS active_days
FROM raw_events
GROUP BY user_id;

-- Шаг 3: финальный анализ
SELECT
    CASE WHEN active_days >= 20 THEN 'power_user'
         WHEN active_days >= 5 THEN 'regular'
         ELSE 'casual'
    END AS segment,
    COUNT(*) AS users,
    AVG(events) AS avg_events
FROM user_metrics
GROUP BY 1;

Когда CTE достаточно

Один запрос, несколько шагов. Если промежуточные данные нужны только здесь и сейчас — CTE чище и проще. Не нужно создавать и потом думать, удалилась ли таблица.

Читаемость. CTE читается сверху вниз, каждый блок с понятным именем. Для код-ревью и для собеседования — идеальный формат.

Подробнее о CTE и подзапросах — в отдельном разборе.

Типичные ошибки

1. Забыть, что TEMP TABLE живёт до конца сессии

В инструментах вроде DBeaver или DataGrip сессия может висеть часами. Если создали TEMP TABLE утром, а днём создаёте с тем же именем — получите ошибку relation already exists. Решение:

DROP TABLE IF EXISTS active_users;
CREATE TEMP TABLE active_users AS ...;

2. Конфликт имён с постоянными таблицами

Если в базе есть таблица users и вы создаёте TEMP TABLE users — временная таблица «перекроет» постоянную в текущей сессии. Все запросы к users будут идти в TEMP TABLE. Это трудно поймать, и на собеседовании про это спрашивают. Совет: добавляйте префикс tmp_ или temp_.

3. Не чистить за собой

В длинных скриптах с десятком TEMP TABLE можно забить временное хранилище. Хорошая практика — делать DROP TABLE IF EXISTS в начале скрипта или использовать ON COMMIT DROP.

Вопросы с собеседований

  • «Что такое временная таблица?» — Таблица, которая существует только в текущей сессии и автоматически удаляется при отключении. Другие сессии её не видят.

  • «Чем TEMP TABLE отличается от CTE?» — TEMP TABLE хранится на диске и живёт до конца сессии. Можно создавать индексы, использовать в нескольких запросах. CTE существует только внутри одного запроса.

  • «Чем TEMP TABLE отличается от VIEW?» — VIEW — это сохранённый запрос, который живёт в базе постоянно и доступен всем. TEMP TABLE — физическая таблица с данными, видимая только текущей сессии.

  • «Когда вы используете TEMP TABLE вместо CTE?» — Когда нужно обращаться к промежуточным данным из нескольких запросов, когда нужен индекс или когда данные слишком большие для эффективного CTE.

  • «TEMP TABLE видна другим пользователям?» — Нет. Каждая сессия видит только свои временные таблицы.

FAQ

Временная таблица хранится в памяти или на диске?

Зависит от СУБД и объёма данных. В PostgreSQL TEMP TABLE хранится в буферном кеше, но может сбрасываться на диск. В MySQL CREATE TEMPORARY TABLE работает аналогично. Для аналитика важно знать: поведение похоже на обычную таблицу, просто с ограниченным временем жизни.

Можно ли создать индекс на TEMP TABLE?

Да, и это одно из главных преимуществ перед CTE. CREATE INDEX ON tmp_users (user_id) — и JOIN по временной таблице работает быстро. CTE не поддерживает индексы.

Как удалить временную таблицу до конца сессии?

DROP TABLE IF EXISTS tmp_name; — удалит таблицу сразу. Или используйте ON COMMIT DROP при создании, чтобы таблица удалялась автоматически при завершении транзакции.

TEMP TABLE есть во всех СУБД?

Да, но синтаксис немного отличается. PostgreSQL: CREATE TEMP TABLE. MySQL: CREATE TEMPORARY TABLE. SQL Server: #table_name (локальная) или ##table_name (глобальная). ClickHouse: CREATE TEMPORARY TABLE. Концепция одинаковая — таблица живёт в рамках сессии.


Хотите потренировать SQL на реальных задачах с собеседований? Попробуйте SQL-тренажёр или откройте тренажёр прямо в Telegram. Больше вопросов — в разделе примеры вопросов. А если хотите разобраться в SQL для аналитика с нуля — читайте гайд по SQL.