Temporary Tables in PostgreSQL

Learn how to create and use PostgreSQL temporary tables with CREATE TEMP TABLE, including session scope, ON COMMIT DROP, name shadowing, and when to use them for ETL and staging.

4 min read · Back to overview

Quick Answer

A PostgreSQL temporary table is created with CREATE TEMP TABLE and exists only for the duration of the session that created it. It is private to that session — other connections cannot see it. Use ON COMMIT DROP to scope the table to a single transaction instead.

Spin up a Postgres database in 20 seconds with Vela.

Try Vela Sandbox

A temporary table in PostgreSQL is a session-scoped table that is automatically dropped when the connection that created it closes. Temporary tables are private to their session — no other connection can see or access them — making them ideal for storing intermediate results in complex data transformations without polluting the permanent schema.

Syntax

CREATE TEMP TABLE table_name (
  column_name data_type [constraint],
  ...
) [ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP }];

TEMP and TEMPORARY are interchangeable. Key options:

  • ON COMMIT PRESERVE ROWS — the default; rows survive the transaction, table survives the session.
  • ON COMMIT DELETE ROWS — rows are truncated at each COMMIT, but the table structure persists.
  • ON COMMIT DROP — the table is dropped when the transaction commits.

Practical Example

Create a staging table for an ETL pipeline within a session:

CREATE TEMP TABLE staging_events (
  raw_id       SERIAL,
  event_type   VARCHAR(50),
  payload      JSONB,
  received_at  TIMESTAMPTZ DEFAULT now()
);

INSERT INTO staging_events (event_type, payload)
VALUES
  ('purchase',  '{"product_id": 12, "amount": 49.99}'),
  ('view',      '{"product_id": 7}'),
  ('purchase',  '{"product_id": 3,  "amount": 12.50}');

-- Transform and load into the permanent table
INSERT INTO purchase_events (product_id, amount, recorded_at)
SELECT
  (payload->>'product_id')::INT,
  (payload->>'amount')::NUMERIC,
  received_at
FROM staging_events
WHERE event_type = 'purchase';

When the session ends, staging_events is dropped automatically. To drop it explicitly within the session:

DROP TABLE staging_events;

Use ON COMMIT DROP to scope the table to a single transaction:

BEGIN;

CREATE TEMP TABLE order_totals (
  order_id INT,
  total    NUMERIC(12,2)
) ON COMMIT DROP;

INSERT INTO order_totals
SELECT order_id, SUM(line_total)
FROM order_lines
GROUP BY order_id;

-- Use the data within this transaction
SELECT * FROM order_totals WHERE total > 1000;

COMMIT;
-- order_totals is automatically dropped here

Name Shadowing

A temporary table can share a name with a permanent table, but this is not recommended. The temporary table takes precedence for the duration of the session:

CREATE TABLE products (product_id SERIAL PRIMARY KEY, name VARCHAR(200));

CREATE TEMP TABLE products (product_id INT);  -- shadows the permanent table

-- This hits the temp table, not the permanent one:
SELECT * FROM products;

-- Access the permanent table explicitly:
SELECT * FROM public.products;

Testing with Vela

When building ETL pipelines that rely on temporary tables for staging, Vela database branching lets you test the full pipeline against a production-sized copy of the database — including the permanent tables that your temp tables feed into — without risking production data. Validate row counts, data quality, and transaction behavior before scheduling the pipeline against the live system.

Production Tips

  • In connection-pool environments (PgBouncer, RDS Proxy), connections are reused between application requests — a temporary table created in one request may persist into the next if the session is not reset. Use explicit DROP TABLE or ON COMMIT DROP to control the lifetime precisely.
  • Add indexes to temporary tables just like permanent ones — they can dramatically speed up multi-step queries within a session.
  • Avoid giving temporary tables the same name as permanent tables; the shadowing behavior is a common source of subtle bugs.
  • PostgreSQL stores temporary tables in a session-specific schema (e.g., pg_temp_3). You cannot explicitly specify this schema in CREATE TEMP TABLE.
  • Use ON COMMIT DELETE ROWS when you want to keep the table structure across transactions but clear the data between operations (e.g., a reusable staging buffer).

Continue in Managing Tables: Truncate Table.

Related in this section: PostgreSQL Data Types · Create Table · Select Into

Frequently Asked Questions

What does CREATE TEMP TABLE do in PostgreSQL?
CREATE TEMP TABLE creates a table that exists only for the duration of the current database session. The table is stored in a temporary schema (e.g., pg_temp_3) and is automatically dropped when the session ends. Other concurrent sessions cannot see it.
Does CREATE TEMP TABLE lock other tables and block queries?
No. Creating a temporary table does not affect other tables. It acquires a lock only on the new temporary table itself, which no other session can access anyway.
What happens to a temporary table when a transaction is rolled back?
By default, a rolled-back transaction does not drop the temporary table — the table structure persists until the session ends, but rows inserted in the rolled-back transaction are removed. If you create the table with ON COMMIT DROP, it is dropped when the transaction ends (commit or rollback).
Can I use IF NOT EXISTS with CREATE TEMP TABLE?
Yes. CREATE TEMP TABLE IF NOT EXISTS t (...) suppresses the error when a temporary table with that name already exists in the session, issuing a NOTICE instead.
What is the safest way to use temporary tables in production workflows?
Create them inside a transaction with ON COMMIT DROP when they are only needed for a single operation. Use explicit DROP TABLE when you are done to avoid accumulating invisible tables across a long-lived connection-pool session. Avoid giving temp tables the same name as permanent tables to prevent accidental shadowing.