{
  "id": 11074472,
  "title": "Adding a NOT NULL Column to a Large PostgreSQL Table: Constant Default, Backfill or NOT VALID",
  "url": "https://urgent.news/2026/10/01/adding-a-not-null-column-to-a-large-postgresql-table-constant-default",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-10-01T01:22:37.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/tbson87/adding-a-not-null-column-to-a-large-postgresql-table-constant-default-backfill-or-not-valid-1ha3"
  },
  "original_language": "en",
  "account": "Adding a NOT NULL column to a large PostgreSQL table involves careful consideration of the default value and the migration approach. On PostgreSQL 11 and later, adding a column with a constant non-null default, such as NOW(), can be done quickly, often within milliseconds, as it only changes the table's metadata. However, using a volatile default like gen_random_uuid() requires rewriting the entire table, which can take around 10-12 seconds for a 5 million row table.\n\nWhen the column value must be computed per row, a better approach is to add the column as nullable, backfill it in batches, and then prove it is NOT NULL. This method ensures that the full table scan does not block reads, as each batch of updates holds a short lock. Setting lock_timeout for all these statements is recommended, as they can be affected by open transactions.\n\nThe choice of migration method depends on the nature of the new column's values for existing rows. If the values are constant, the ALTER TABLE command with the NOT NULL DEFAULT clause is sufficient. However, if each row requires a unique value, splitting the process into steps is advised. First, add the column as nullable, backfill it in manageable batches, and then make the column NOT NULL. This approach avoids the full table scan that occurs when trying to make a column NOT NULL without first ensuring there are no NULL values.\n\nFor backfilling, updating the column with a value that depends on another table's data can be done in batches to minimize locks. Finally, after backfilling, set the column to NOT NULL. On PostgreSQL versions 12 and later, adding a NOT VALID constraint check can skip the full table scan, making the process more efficient. VALIDATE CONSTRAINT, while necessary for confirming no NULL values exist, incurs a significant scan time. Once validated, the check constraint can be dropped to improve performance.",
  "summary": "Disclosure: I build Schemity , a desktop ERD tool - this post is from our blog and uses it for the examples. TL;DR: On PostgreSQL 11 and later, ADD COLUMN ... NOT NULL DEFAULT 'new' is a catalogue change that took 10 ms on 5,000,000 rows, but a volatile default such as gen_random_uuid() rewrote the whole table in 10.4 seconds. When the value has to be computed per row, add the column as nullable,…",
  "key_points": [
    "Adding a NOT NULL column with constant default (e.g., NOW()) is fast in PostgreSQL 11+.",
    "For per-row computed values, add column as nullable, backfill in batches, then set NOT NULL.",
    "PostgreSQL 12+ allows NOT VALID constraint to skip full table scan during backfill."
  ],
  "editors_take": "Adding a NOT NULL column to a large PostgreSQL table requires a strategic approach to avoid performance hits, with the optimal method depending on whether the new column's values are constant or row-specific.",
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}