HuntExams Academy logo
SQL · Chapter 31 of 42

DROP TABLE

DROP TABLE completely removes a table and all its data. Cannot be undone (except from backups).

Add `IF EXISTS` to avoid an error if the table isn't there.

Example 1 (sql)
DROP TABLE IF EXISTS old_users;
Output
Table dropped

Safe drop.

Example 2 (sql)
DROP TABLE users CASCADE;
Output
Table + dependent objects dropped

CASCADE drops dependent objects (views, foreign keys) — use with care.

Key points

  • Removes table and data.
  • IF EXISTS avoids errors.
  • CASCADE also drops dependent objects.
  • Cannot be undone.
💡 Note: TRUNCATE keeps the table but wipes rows. DROP removes the table itself.

Understanding DROP TABLE

DROP TABLE removes a table as a database object, not merely the rows stored inside it. After a successful, committed drop, its name no longer identifies that table, so queries against it fail unless another table with that name is created. Recreating the columns does not recover the original records. Use DROP when the table itself is no longer needed, rather than when its contents simply need refreshing.

In PostgreSQL, the basic form is DROP TABLE schema_name.table_name;. Schema qualification helps identify the intended object: different schemas can contain tables with the same name. An unqualified name is resolved through the session’s search path. Before changing a shared database, confirm the database, schema, and table, and consider applications that still expect the object to exist. Database dependency checks cannot discover every reference embedded in application code.

DROP automatically removes the target table’s indexes, triggers, rules, and constraints. Dependencies outside the table require particular attention. RESTRICT, the default behavior, refuses the operation when blocking dependencies exist. CASCADE authorizes PostgreSQL to remove dependent objects too, potentially following further dependencies. For example, a dependent view is removed, while a foreign key referencing the dropped table is removed from the referencing table without dropping that referencing table itself.

IF EXISTS changes only what happens when the named table is absent: PostgreSQL issues a notice instead of treating absence as an error. It does not request confirmation, create a backup, bypass permissions, or protect a table that does exist. Executing DROP also requires appropriate ownership or administrative authority; permission to read or insert rows alone is insufficient.

The claim that DROP can never be undone needs a PostgreSQL-specific correction. DROP TABLE participates in transactions: an uncommitted drop can be rolled back, restoring the table and its data. Once committed, ordinary ROLLBACK cannot recover it; recovery requires a suitable backup or other established recovery mechanism. With autocommit enabled, a standalone successful DROP is committed immediately. Finally, distinguish DELETE, which can remove selected rows, from TRUNCATE, which empties tables, and DROP, which removes the table definition too. DROP also takes an exclusive lock, so it can wait for other transactions and block concurrent access.

Practical walkthrough

Use PostgreSQL 18 and a SQL client connected to a disposable practice database. The example needs permission to create temporary tables, but no extensions or sample dataset. Run it in one fresh connection with no transaction already open. A temporary table belongs to that session and is automatically removed when the session ends; this example also explicitly removes it before finishing.

Start by opening a transaction. Create the temporary table and insert two fixed records. The table name is deliberately exercise-specific. Later references use pg_temp, PostgreSQL’s alias for the current session’s temporary schema, so the DROP statements cannot accidentally target a permanent table with the same name. A fresh connection also avoids a collision with a temporary table from an earlier exercise.

The first SELECT checks the starting row count. Next, establish a savepoint after inserting the records. A savepoint marks a position inside the current transaction to which changes can be reversed without abandoning everything done earlier. Drop the temporary table using explicit RESTRICT. There are no external dependencies in this exercise, so that request succeeds.

Instead of querying the now-missing table, inspect its name with to_regclass. This PostgreSQL function returns NULL when the specified relation cannot be found. Convert that result into the text absent for predictable display.

Roll back to the savepoint, then count the restored rows. Finally, drop the temporary table again and commit. Compare the four result sets in sequence. This demonstrates transactional reversibility without suggesting that committed drops are reversible, or that temporary-table practice reproduces shared-database locking and dependency risks.

Worked example

BEGIN;

CREATE TEMPORARY TABLE drop_table_practice_482 (
    item_id integer PRIMARY KEY,
    label text NOT NULL
);

INSERT INTO pg_temp.drop_table_practice_482 (item_id, label)
VALUES (1, 'pencil'), (2, 'notebook');

SELECT 'before drop' AS stage, COUNT(*) AS row_count
FROM pg_temp.drop_table_practice_482;

SAVEPOINT before_removal;

DROP TABLE pg_temp.drop_table_practice_482 RESTRICT;

SELECT 'after drop' AS stage,
       CASE
           WHEN to_regclass('pg_temp.drop_table_practice_482') IS NULL
           THEN 'absent'
           ELSE 'present'
       END AS table_state;

ROLLBACK TO SAVEPOINT before_removal;

SELECT 'after rollback' AS stage, COUNT(*) AS row_count
FROM pg_temp.drop_table_practice_482;

DROP TABLE pg_temp.drop_table_practice_482 RESTRICT;
COMMIT;

SELECT 'after commit' AS stage,
       CASE
           WHEN to_regclass('pg_temp.drop_table_practice_482') IS NULL
           THEN 'absent'
           ELSE 'present'
       END AS table_state;

Expected output

SELECT result sets, in execution order:

stage       | row_count
before drop | 2

stage      | table_state
after drop | absent

stage          | row_count
after rollback | 2

stage        | table_state
after commit | absent

Both inserted records exist before the savepoint, so the first count is exactly 2. The first DROP removes the table within the transaction; the catalog lookup therefore reports absent. Rolling back to the savepoint reverses that DROP, preserving the earlier creation and insertion, so the next count is again 2. The second DROP is committed, leaving the table absent at the final lookup. Each SELECT returns exactly one row, with the column names and values shown. Client spacing and transaction command messages may vary; these are query results, not a fabricated message returned by DROP. No persistent table is modified.

Common mistakes and how to avoid them

  • Treating IF EXISTS as a general safety switch.

    It only tolerates a missing table. Verify the target schema and recovery plan separately; an existing table is still removed.

  • Adding CASCADE immediately after a dependency error.

    Inspect the reported dependencies first. Keep RESTRICT unless removing the dependent objects is intentional, and account for application references that PostgreSQL cannot track.

  • Expecting ROLLBACK to undo an already committed DROP.

    Check transaction boundaries and client autocommit settings before execution. After commitment, use an established recovery process rather than issuing ROLLBACK.

  • Using DROP merely to clear old records.

    Use DELETE for selected rows or consider TRUNCATE for emptying a table. Review their permissions, locking, and constraint behavior before choosing.

Review questions

Why does recreating a dropped table with identical columns not restore its records?

CREATE TABLE makes a new object with an empty dataset. Matching the former structure does not retrieve the old data; restoring records requires a separate recovery source.

What does RESTRICT protect, and what does it miss?

It blocks drops with tracked dependencies, such as dependent views. It cannot guarantee that application queries, scripts, or reports will keep working after the table disappears.

Why does the example restore two rows rather than an empty table?

The savepoint follows creation and insertion. Rolling back to it reverses the subsequent DROP, not the earlier statements, restoring the table with both records.

Does CASCADE necessarily drop another table that references the target through a foreign key?

No. PostgreSQL removes the referencing foreign key constraint, not that referencing table merely because of the foreign key. Other dependencies can still cause additional objects to be removed.

Further reading

📝 Quick Quiz

1. DROP TABLE:

2. IF EXISTS is used to:

3. Difference: TRUNCATE vs DROP?