<style> .reveal-up, .reveal-left, .reveal-right, .reveal-stagger > * { opacity: 1 !important; transform: none !important; } </style>
  1. Learn
  2. SQL
  3. Creating and changing data
All lessons in this course

Lesson 12 of 54 14 minBeginner

Changing tables safely: ALTER and DROP

Add, change, rename and remove columns on tables that already hold data. See which changes are instant, which fail, and which silently lose data, and how to make schema changes you can undo.

Tables change over a system’s life: a new column here, a longer name there. ALTER TABLE changes a table in place, keeping its data. Some changes are harmless, some are refused, and a few quietly destroy data. Try them on BuildMart’s products:

Interactive

Change a table that already has data

Each statement runs on a fresh BuildMart, where dbo.Products holds 12 rows. The grid shows the first three rows afterwards.

Statement.sql
ALTER TABLE dbo.Products ADD Barcode varchar(13) NULL;GOSELECT TOP (3) * FROM dbo.Products ORDER BY ProductId;
ProductIdNameCategoryIdPriceStockUnitAddedOnBarcode
11Claw hammer118.5040each2023-01-15NULL
22Cordless drill189.0012each2023-02-01NULL
33Tape measure 8 m19.7565each2023-01-15NULL
Instant, even on a huge table: existing rows simply have no barcode yet.

Adding columns

A new column that allows NULL is added instantly, with NULL in every existing row. A NOT NULL column needs a DEFAULT so the existing rows have a value:

Query.sql
ALTER TABLE dbo.Customers ADD Newsletter bit NOT NULL    CONSTRAINT DF_Customers_Newsletter DEFAULT 0;GOSELECT TOP (3) CustomerId, FirstName, Newsletter FROM dbo.Customers;
Output
CustomerId  FirstName  Newsletter
----------  ---------  ----------
         1  Asha       0
         2  Ben        0
         3  Chloe      0

Changing a column

ALTER COLUMN changes a column’s type, length or NULL rule. Widening (nvarchar(60) to nvarchar(100), int to bigint) is always safe. Narrowing is checked against the data and refused if anything would be cut. Changing to a different kind of type converts every value with the normal conversion rules, and those can lose data without an error, as the decimal-to-int example in the widget shows.

Renaming

Renaming uses the system procedure sp_rename, not ALTER TABLE. SQL Server warns you, because every query, view and procedure that uses the old name breaks:

Query.sql
EXEC sp_rename 'dbo.Customers.Phone', 'PhoneNumber', 'COLUMN';GOSELECT TOP (2) CustomerId, FirstName, PhoneNumber FROM dbo.Customers;
Output
Caution: Changing any part of an object name could break scripts and stored procedures.

CustomerId  FirstName  PhoneNumber
----------  ---------  -------------
         1  Asha       0113 496 0001
         2  Ben        01904 555 010

Dropping columns, constraints and tables

ALTER TABLE … DROP COLUMN removes a column and its data. A column that a constraint uses cannot be dropped until the constraint is, which is where named constraints pay off: you can drop them by name. Tables are dropped with DROP TABLE, children before parents, because a table referenced by a foreign key cannot be dropped:

Query.sql
ALTER TABLE dbo.Customers ADD Region nvarchar(20) NULL    CONSTRAINT DF_Customers_Region DEFAULT N'North';GOALTER TABLE dbo.Customers DROP COLUMN Region;   -- fails: the default uses itGOALTER TABLE dbo.Customers DROP CONSTRAINT DF_Customers_Region;ALTER TABLE dbo.Customers DROP COLUMN Region;SELECT COUNT(*) AS RegionColumns FROM sys.columnsWHERE object_id = OBJECT_ID('dbo.Customers') AND name = 'Region';
Output
Msg 5074, Level 16, State 1, Line 1
The object 'DF_Customers_Region' is dependent on column 'Region'.

Msg 4922, Level 16, State 9, Line 1
ALTER TABLE DROP COLUMN Region failed because one or more objects access this column.

RegionColumns
-------------
            0

Schema changes you can undo

In SQL Server, CREATE, ALTER and DROP take part in transactions like any INSERT or UPDATE. Wrap a risky change in a transaction, look at the result, and roll it back if it is wrong, data included:

Query.sql
BEGIN TRANSACTION; ALTER TABLE dbo.Products DROP COLUMN Unit;SELECT COUNT(*) AS UnitColumns FROM sys.columnsWHERE object_id = OBJECT_ID('dbo.Products') AND name = 'Unit'; ROLLBACK; SELECT TOP (2) Name, Unit FROM dbo.Products ORDER BY ProductId;
Output
UnitColumns
-----------
          0

Name            Unit
--------------  ----
Claw hammer     each
Cordless drill  each

For changes to a live system, the professional routine is: script the change, run it on a copy of the database first, take a backup, run it in a transaction, check, then commit. Tools such as SQL Server Data Tools and migration frameworks (EF Core migrations, DbUp, Flyway) turn those scripts into versioned, repeatable steps.

Quick check

Which change can lose data without any error?

Quick check

Why add GO between an ALTER TABLE that adds a column and a SELECT that reads it?

Practice

BuildMart starts recording barcodes. Add a column Barcode varchar(13) NULL to Products, set it to 5012345678900 for the claw hammer (product 1), and make sure no two products can ever share a barcode while still allowing many products without one. Show the first two products with their barcodes.

Show a hint

ALTER TABLE … ADD, then GO, an UPDATE, and a filtered unique index (WHERE Barcode IS NOT NULL), because a UNIQUE constraint would allow only one NULL.

Show the solution
Solution.sql
ALTER TABLE dbo.Products ADD Barcode varchar(13) NULL;GOUPDATE dbo.Products SET Barcode = '5012345678900' WHERE ProductId = 1; CREATE UNIQUE INDEX UX_Products_Barcode ON dbo.Products (Barcode) WHERE Barcode IS NOT NULL; SELECT TOP (2) ProductId, Name, Barcode FROM dbo.Products ORDER BY ProductId;
Output
ProductId  Name            Barcode
---------  --------------  -------------
        1  Claw hammer     5012345678900
        2  Cordless drill  NULL

Remember

  • Adding a NULL column is instant; a NOT NULL column needs a DEFAULT on a table with rows.
  • Widening a column is safe; narrowing is checked; changing the kind of type can silently lose data.
  • Restate NULL / NOT NULL in ALTER COLUMN, and put GO before using a new column in the same script.
  • Name constraints so they can be dropped; drop child tables before their parents.
  • DDL is transactional in SQL Server: BEGIN TRANSACTION, change, check, then COMMIT or ROLLBACK.
0 of 54 lessons completed