Ответ
Временная таблица в реляционных базах данных — это специальная таблица, которая создается на время сеанса пользователя или транзакции и автоматически удаляется при их завершении. Она не является частью постоянной схемы данных.
Основные характеристики
- Жизненный цикл: Существует только в рамках текущего соединения (сеанса) или транзакции.
- Видимость: Видна только тому соединению, которое её создало. Другие сеансы не могут получить к ней доступ, что исключает конфликты имен с таблицами в других сеансах.
- Производительность: Операции с временными таблицами могут быть быстрее, так как для них часто используется менее строгое журналирование (logging).
- Хранение: Обычно хранятся в специальной области (temporary tablespace) или в оперативной памяти.
Сценарии использования
Временные таблицы незаменимы для хранения промежуточных результатов в сложных многоэтапных запросах:
- Декомпозиция сложных
JOIN'ов: Вместо одного огромного запроса с множеством соединений можно выбрать данные в несколько временных таблиц и затем соединить их. - Генерация отчетов: Собрать данные из разных источников в одну временную таблицу для последующей агрегации и анализа.
- Массовые операции: Сохранить список идентификаторов (
ID) во временную таблицу для последующего использования вUPDATEилиDELETEзапросах.
Пример на SQL (PostgreSQL/MySQL)
Синтаксис может незначительно отличаться в разных СУБД (например, в MS SQL Server используются таблицы с префиксом #).
-- Создание временной таблицы, которая будет удалена в конце сессии
CREATE TEMPORARY TABLE temp_users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
is_active BOOLEAN
);
-- Наполнение и использование таблицы
INSERT INTO temp_users (name, is_active) VALUES ('Alice', true), ('Bob', false);
-- Выборка активных пользователей из временной таблицы
SELECT * FROM temp_users WHERE is_active = true;
-- После закрытия соединения с БД таблица 'temp_users' будет автоматически удалена.
Работа с временными таблицами в Go
В Go нет специального API для временных таблиц. Работа с ними ведется через стандартные SQL-запросы с помощью пакета database/sql. Однако есть важный нюанс, связанный с пулом соединений.
Ключевой момент: Временная таблица привязана к конкретному соединению. Пакет database/sql управляет пулом соединений, и каждый вызов db.Exec() или db.Query() может использовать разное соединение из пула. Поэтому, чтобы гарантированно работать с временной таблицей, все операции (создание, наполнение, чтение) нужно выполнять в рамках одной транзакции или одного соединения.
Правильный подход — использование транзакций:
package main
import (
"database/sql"
"log"
_ "github.com/lib/pq" // Драйвер для PostgreSQL
)
func main() {
db, err := sql.Open("postgres", "user=... password=... dbname=... sslmode=disable")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Начинаем транзакцию. Все последующие команды до Commit/Rollback
// будут выполнены в рамках одного соединения.
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
// Гарантируем откат транзакции в случае ошибки
defer tx.Rollback()
// 1. Создаем временную таблицу
_, err = tx.Exec(`CREATE TEMPORARY TABLE temp_products (id INT, name TEXT)`)
if err != nil {
log.Fatal("Failed to create temp table: ", err)
}
// 2. Наполняем ее данными
_, err = tx.Exec(`INSERT INTO temp_products (id, name) VALUES ($1, $2)`, 1, "Laptop")
if err != nil {
log.Fatal("Failed to insert into temp table: ", err)
}
// 3. Читаем данные из нее
var productName string
err = tx.QueryRow(`SELECT name FROM temp_products WHERE id = $1`, 1).Scan(&productName)
if err != nil {
log.Fatal("Failed to query temp table: ", err)
}
log.Printf("Product from temp table: %s", productName)
// Завершаем транзакцию. После этого временная таблица может быть удалена
// (зависит от настроек СУБД, но в конце сессии она исчезнет точно).
if err := tx.Commit(); err != nil {
log.Fatal(err)
}
} Ответ 18+ 🔞
А, ну это ж классика, блядь! Временные таблицы в базах данных — это как одноразовый стаканчик на вечеринке: налил, выпил, выкинул в пизду, и никто не помнит, что ты там бухал. Суть в том, что эта штука живёт ровно до тех пор, пока ты не отключишься от базы или не завершишь транзакцию. Потом — хуй, испарилась, как твои надежды на повышение зарплаты.
Что это за зверь такой:
- Живёт недолго: Создал в рамках своей сессии или транзакции — и всё, блядь. Закрыл коннект — таблица накрылась медным тазом. Не лезет в постоянную схему, не мусорит.
- Видит только ты: Другие подключения к этой же базе её не увидят. Идеально, чтобы не пересекаться именами с каким-нибудь коллегой-распиздяем, который тоже назвал свою таблицу
temp_shit. - Быстрая, потому что похуистичная: Часто под неё журналирование (это когда всё логируют) слабее, поэтому операции могут летать. Может храниться в оперативке или в специальном временном пространстве — не в постоянных файлах, которые жрут место.
Зачем это надо, ёпта?
Ну, например, когда у тебя запрос такой ебученно сложный, что проще разбить его на части. Вместо одного монстра с десятью JOIN, который даже СУБД плачет, можно:
- Выгрести первую пачку данных во временную таблицу.
- Присобачить к ней вторую.
- Всё это проагрегировать и получить отчёт.
Или, допустим, нужно сделать массовое обновление — выбрал IDшники во временную табличку, а потом по ним
UPDATEилиDELETEзапулил. Удобно, блядь!
Смотри, как в SQL это выглядит (например, в PostgreSQL):
-- Создаём таблицу-призрак. Умрёт, когда сессия кончится.
CREATE TEMPORARY TABLE temp_orders (
id SERIAL PRIMARY KEY,
user_id INT,
total DECIMAL
);
-- Пихаем в неё данные
INSERT INTO temp_orders (user_id, total) VALUES (42, 999.99), (777, 0.01);
-- Читаем их, как будто так и надо
SELECT * FROM temp_orders WHERE total > 100;
-- И всё... Закрыл коннект — и таблицы нет, будто и не было. Магия, сука!
А теперь главное, про Go, где всё всегда весело!
В Go нет волшебной палочки для временных таблиц. Работаешь через обычные SQL-запросы. Но есть одна хитрая жопа, о которую все спотыкаются: пул соединений.
Библиотека database/sql держит кучу коннектов к базе. И когда ты делаешь db.Exec(), она может каждый раз выдавать тебе разное соединение из пула. А временная таблица привязана к конкретному соединению. Представь: ты создал таблицу в одном соединении, а в следующем запросе пытаешься в неё записать — а тебе выдают другое соединение, где этой таблицы, блядь, НЕТУ! И получаешь ошибку «relation does not exist». Удивление пиздец!
Как правильно? Держать всё в одной транзакции! Транзакция гарантированно использует одно соединение.
package main
import (
"database/sql"
"log"
_ "github.com/lib/pq"
)
func main() {
db, err := sql.Open("postgres", "user=... password=... dbname=...")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Начинаем транзакцию — это наш личный, непередаваемый коннект.
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
defer tx.Rollback() // На всякий пожарный, если что-то пойдёт не так.
// 1. Создаём временную таблицу В ЭТОЙ транзакции.
_, err = tx.Exec(`CREATE TEMPORARY TABLE temp_cart (item_id INT, qty INT)`)
if err != nil {
log.Fatal("Не создалась таблица, блядь: ", err)
}
// 2. Пихаем в неё что-нибудь.
_, err = tx.Exec(`INSERT INTO temp_cart VALUES ($1, $2)`, 555, 3)
if err != nil {
log.Fatal("Не вставилось, ёпта: ", err)
}
// 3. Читаем обратно. Всё в рамках той же транзакции — значит, та же таблица.
var quantity int
err = tx.QueryRow(`SELECT qty FROM temp_cart WHERE item_id = $1`, 555).Scan(&quantity)
if err != nil {
log.Fatal("Не прочиталось, сука: ", err)
}
log.Printf("Достали из временной таблицы: %d штук", quantity)
// Фиксируем. После коммита транзакция завершена, но сессия-то ещё жива.
// Таблица будет висеть, пока это соединение не вернётся в пул и не закроется.
// Но обычно на это забивают — главное, что в рамках логики всё работало.
if err := tx.Commit(); err != nil {
log.Fatal(err)
}
// А вот когда эта функция завершится и db.Close() закроет все коннекты,
// тогда временная таблица точно отправится в цифровое небытие.
}
Вот и весь секрет, блядь. Создал в транзакции — используй в транзакции — удалится сама. Красота, ёперный театр!