Временные таблицы в 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.