Google BigQuery

BigQuery ALTER TABLE: add, rename and drop columns, change data types

How to change an existing BigQuery table with ALTER TABLE: add one or several columns, rename a table or a column, drop a column, change a data type, and set defaults and options. Each statement comes with the limits Google documents and the workaround when ALTER cannot do it.

Google BigQueryAlyona Samovar6 min read

BigQuery ALTER TABLE: add, rename and drop columns, change data types

ALTER TABLE changes an existing BigQuery table without recreating it. This page lists the statements people look up, in the order of how often they are needed.

To do this Statement
Add a column ALTER TABLE t ADD COLUMN c TYPE
Add several columns ADD COLUMN a TYPE, ADD COLUMN b TYPE
Rename a table ALTER TABLE t RENAME TO new_name
Rename a column ALTER TABLE t RENAME COLUMN a TO b
Drop a column ALTER TABLE t DROP COLUMN c
Widen a column’s type ALTER COLUMN c SET DATA TYPE NUMERIC
Any other type change recreate with CREATE OR REPLACE TABLE
Change table options ALTER TABLE t SET OPTIONS (...)

The syntax and limits are from Google’s DDL reference, checked on 6 October 2026. The statements were written from that reference and not executed for this article, because they change tables.

Note: Originally published in June 2024. Fully updated in October 2026 against the current reference.

Add a column

Add a column

ALTER TABLE mydataset.orders
ADD COLUMN discount NUMERIC;

Existing rows get NULL in the new column. To make the statement safe to run twice, add IF NOT EXISTS. Without it, a column that already exists is an error; with it, nothing happens:

ALTER TABLE mydataset.orders
ADD COLUMN IF NOT EXISTS discount NUMERIC;

Add multiple columns

Repeat ADD COLUMN for each, separated by commas, in one statement:

ALTER TABLE mydataset.orders
ADD COLUMN IF NOT EXISTS discount NUMERIC,
ADD COLUMN IF NOT EXISTS coupon STRING,
ADD COLUMN IF NOT EXISTS tags ARRAY<STRING>;

An array column is empty, not NULL, for existing rows.

What ADD COLUMN cannot do

  • Add a REQUIRED (NOT NULL) column to an existing table.
  • Add a partitioning or clustering column.
  • Add a nested field inside an existing RECORD.
  • Work on an external table.

A new column is always added at the end. BigQuery has no AFTER clause; if the order matters for a reader, select the columns in that order in a view.

Rename a table

Rename a table

ALTER TABLE mydataset.orders
RENAME TO orders_2026;

The new name is only the table name, not a full path, and it must not already exist. Two things from the reference that are easy to miss:

  • The renamed table keeps its original creation time. If the dataset has a default table expiration, a table older than that window can be deleted right after the rename.
  • Renaming removes Data Catalog tags and Knowledge Catalog aspects attached to the table and its columns.

To move a table to another dataset, a rename does not do it. Copy the table, then drop the original.

The Turning Point

OWOX Data Marts

See your first report built in real time. 15 minutes.

  1. Connect your data warehouse
  2. Pick your metrics
  3. Get a live Google Sheets report

In the time it takes to write a ticket. Then imagine never writing that ticket again.

Book a Demo

We'll use your actual use case

Rename a column

Rename a column

ALTER TABLE mydataset.orders
RENAME COLUMN amount TO amount_usd;

Several can be renamed in one statement, separated by commas. Limits: it is not supported on external tables, it fails if the table has active row-level access policies, and primary key columns cannot be renamed.

Renaming a column breaks every query that uses the old name, and BigQuery will not tell you which ones. Search the query history for the column name first to see what reads it.

Drop a column

Drop a column

ALTER TABLE mydataset.orders
DROP COLUMN IF EXISTS coupon;

Dropping is a metadata change. The storage is not freed immediately: it is released the next time the table is rewritten, and Google gives no guaranteed window. To reclaim it at once, overwrite the table:

CREATE OR REPLACE TABLE mydataset.orders AS
SELECT * EXCEPT (coupon)
FROM mydataset.orders;

You cannot drop a partitioning column, a clustering column, or a column that is part of a primary or foreign key. A column dropped by mistake can be recovered with time travel while the window lasts.

Change a column’s data type

Change a column’s data type

ALTER COLUMN SET DATA TYPE only goes from a more restrictive type to a less restrictive one:

ALTER TABLE mydataset.orders
ALTER COLUMN amount SET DATA TYPE NUMERIC;

The conversions the reference lists:

  • INT64 to NUMERIC, BIGNUMERIC or FLOAT64
  • NUMERIC to BIGNUMERIC or FLOAT64
  • a parameterized type to a wider one, for example STRING(5) to STRING(7), or NUMERIC(10, 6) to NUMERIC(12, 8)

Everything else is not possible in place: STRING to INT64, STRING to DATE, FLOAT64 to INT64. For those, rebuild the column with a cast:

CREATE OR REPLACE TABLE mydataset.orders AS
SELECT
  * EXCEPT (order_date),
  SAFE.PARSE_DATE('%d/%m/%Y', order_date)
    AS order_date
FROM mydataset.orders;

Two cautions. CREATE OR REPLACE ... AS SELECT does not carry over partitioning, clustering or table options unless you write them again in the statement. And with SAFE., values that fail to convert become NULL, so count them before you replace the table. The conversion functions are in CAST and SAFE_CAST and conversion functions.

If data is streaming into the table, the reference says to stop the stream first; a type change is usually possible about five hours after the last streaming operation.

NOT NULL and defaults

NOT NULL and defaults

Remove a NOT NULL constraint:

ALTER TABLE mydataset.orders
ALTER COLUMN customer DROP NOT NULL;

There is no statement to add NOT NULL to an existing column. That again means recreating the table.

Set or remove a default for rows inserted from now on:

ALTER TABLE mydataset.orders
ALTER COLUMN created_at
SET DEFAULT CURRENT_TIMESTAMP();

ALTER TABLE mydataset.orders
ALTER COLUMN created_at DROP DEFAULT;

A default applies to new rows only. Existing rows keep what they have.

Change table and column options

Change table and column options

Descriptions, expiration and labels are options:

ALTER TABLE mydataset.orders
SET OPTIONS (
  description = 'One row per order, paid and refunded',
  expiration_timestamp =
    TIMESTAMP '2027-01-01 00:00:00 UTC',
  labels = [('team', 'analytics')]
);

ALTER TABLE mydataset.orders
ALTER COLUMN amount_usd
SET OPTIONS (description = 'Order total in USD, tax included');

Column descriptions are the cheapest documentation a table can have, and they show up in the schema tab for everyone who opens it. For partition options such as expiration and required filters, see partitioned tables.

When ALTER TABLE is not enough

When ALTER TABLE is not enough

Recreate the table when you need to:

  • change a type in a way SET DATA TYPE does not allow
  • add NOT NULL to a column
  • add or change partitioning or clustering
  • reorder columns

The pattern is always CREATE OR REPLACE TABLE ... AS SELECT ..., with the partitioning and options restated. It rewrites the whole table and is billed as a full scan. Creating and dropping tables is covered in creating and deleting BigQuery tables.

Before you change a table other people read

Before you change a table other people read

A schema change is quick to run and slow to recover from. The statement succeeds, and the failures show up later in someone else’s dashboard, as an error or, worse, as a column of NULL.

Three checks cost a few minutes: search the query history for the column or table name, look at which scheduled queries and reports reference it, and tell the owners. The third is hard when nobody knows who they are.

That is one of the things a governed layer is for. In OWOX Data Marts each Data Mart is your SQL with an owner and a description, and the reports built on it are listed with it. So when a table changes, the place to check is a short list of queries with named owners, not every dashboard in the company. OWOX does not alter your tables or migrate schemas. It makes it clear whose query sits between the table and the report.

Destinations tab of a Data Mart in OWOX Data Marts listing the reports built on it, each with its last run date and status – what depends on the query is visible next to it

Your New Normal

Turn your data into decisions.

Governed data marts give you the clean foundation ML needs to actually work.

  • No AI hallucinations
  • Analyst-governed definitions
  • Every number traces to SQL
Get started free

FAQ

Frequently Asked Questions

How do I change the data type of a column in BigQuery?

ALTER TABLE t ALTER COLUMN c SET DATA TYPE works only towards a less restrictive type: INT64 to NUMERIC, BIGNUMERIC or FLOAT64, NUMERIC to BIGNUMERIC or FLOAT64, or a wider parameterized type. For any other change, recreate the table with CREATE OR REPLACE TABLE ... AS SELECT and a cast.

When do I have to recreate a BigQuery table instead of altering it?

To change a type in a way SET DATA TYPE does not allow, to add NOT NULL to a column, to add or change partitioning or clustering, or to reorder columns. Restate the partitioning and options in the CREATE OR REPLACE statement, because they are not carried over.

How do I rename a table in BigQuery?

ALTER TABLE mydataset.orders RENAME TO orders_2026. The new name is the table name only, not a full path. The renamed table keeps its original creation time, so a dataset-level table expiration can delete it immediately if it is older than the window.

How do I add a column to an existing BigQuery table?

ALTER TABLE mydataset.orders ADD COLUMN IF NOT EXISTS discount NUMERIC. Existing rows get NULL. To add several, repeat ADD COLUMN separated by commas in one statement. You cannot add a REQUIRED column, a partitioning column or a clustering column this way.

Does dropping a column in BigQuery free storage?

Not immediately. DROP COLUMN is a metadata change, and the storage is released the next time the table is rewritten, with no guaranteed window. To reclaim it at once, overwrite the table with CREATE OR REPLACE TABLE ... AS SELECT * EXCEPT (column).

How do I rename a column in BigQuery?

ALTER TABLE mydataset.orders RENAME COLUMN amount TO amount_usd. It is not supported on external tables or tables with active row-level access policies, and primary key columns cannot be renamed. Queries that use the old name will fail afterwards.

Who wrote this

Alyona Samovar

Alyona Samovar · Senior Digital Analyst

Alyona Samovar is a Senior Digital Analyst who spent years at OWOX helping enterprise clients design analytics systems, build BigQuery reporting pipelines, and optimize marketing measurement. She specializes in SQL-based analytics, data modeling, and turning raw marketing data into actionable insights. Alyona writes about practical analytics workflows, BigQuery best practices, and data-driven decision-making.