Changing and removing rows: UPDATE and DELETE
Change values with UPDATE and remove rows with DELETE, always with a WHERE clause. Preview with SELECT, use a transaction as a safety net, and see what foreign keys protect.
UPDATE changes values in existing rows. SET says which columns get which new values, and WHERE says which rows. The WHERE clause is exactly the one you know from SELECT:
UPDATE dbo.ProductsSET Price = 19.99, Stock = Stock + 10WHERE ProductId = 1; SELECT @@ROWCOUNT AS RowsUpdated; SELECT ProductId, Name, Price, Stock FROM dbo.Products WHERE ProductId = 1;RowsUpdated
-----------
1
ProductId Name Price Stock
--------- ----------- ----- -----
1 Claw hammer 19.99 50Stock = Stock + 10 reads the old value and writes a new one. Every expression in SET sees the row as it was before the update, so the order of the assignments does not matter. @@ROWCOUNT tells you how many rows the last statement touched; read it straight away, because the next statement replaces it.
The missing WHERE
Write the SELECT first. Write SELECT * FROM dbo.Products WHERE …, check the rows, then change only the first line intoUPDATE … SET …orDELETE.Check the row count. SSMS prints “(n rows affected)”. If you expected 3 and it says 12, stop. Use a transaction when the change matters, so you can look before you commit.
A transaction as a safety net
BEGIN TRANSACTION starts a unit of work. Nothing is final until COMMIT; ROLLBACK undoes everything since BEGIN. Here a price rise is checked, found to be wrong, and undone:
BEGIN TRANSACTION; UPDATE dbo.Products SET Price = Price * 1.10; -- oops: no WHERE SELECT TOP (3) Name, Price FROM dbo.Products ORDER BY ProductId; ROLLBACK; -- undo it SELECT TOP (3) Name, Price FROM dbo.Products ORDER BY ProductId;Name Price ---------------- ----- Claw hammer 20.35 Cordless drill 97.90 Tape measure 8 m 10.73 Name Price ---------------- ----- Claw hammer 18.50 Cordless drill 89.00 Tape measure 8 m 9.75
OUTPUT: old and new values
OUTPUT can return each row’s value before (deleted.) and after (inserted.) the change, a built-in audit of what you did:
UPDATE dbo.ProductsSET Price = Price * 1.10OUTPUT inserted.ProductId, deleted.Price AS OldPrice, inserted.Price AS NewPriceWHERE CategoryId = 1;ProductId OldPrice NewPrice
--------- -------- --------
1 18.50 20.35
2 89.00 97.90
3 9.75 10.73decimal(10,2) column: 9.75 × 1.10 = 10.725 becomes 10.73.
DELETE
DELETE FROM table WHERE … removes whole rows. Ella (customer 5) has never placed an order, so she can be removed:
DELETE FROM dbo.CustomersWHERE CustomerId = 5; SELECT @@ROWCOUNT AS RowsDeleted;RowsDeleted
-----------
1DELETE FROM dbo.CustomersWHERE CustomerId = 1;Msg 547, Level 16, State 0, Line 1 The DELETE statement conflicted with the REFERENCE constraint "FK__Orders__Customer__3E52440B". The conflict occurred in database "BuildMart", table "dbo.Orders", column 'CustomerId'.
DELETE or TRUNCATE?
| `DELETE FROM t` | `TRUNCATE TABLE t` | |
|---|---|---|
Status to Cancelled, report how many rows changed, then list every order’s id, date and status.
Show a hint
Show the solution
UPDATE dbo.OrdersSET Status = 'Cancelled'WHERE Status = 'Pending' AND OrderDate < '2024-06-01'; SELECT @@ROWCOUNT AS RowsUpdated; SELECT OrderId, OrderDate, Status FROM dbo.Orders;RowsUpdated
-----------
1
OrderId OrderDate Status
------- ---------- ---------
1 2024-02-03 Shipped
2 2024-02-10 Shipped
3 2024-03-01 Shipped
4 2024-03-15 Shipped
5 2024-04-02 Shipped
6 2024-04-20 Cancelled
7 2024-05-05 Shipped
8 2024-05-18 Cancelled
9 2024-06-01 Pending
10 2024-06-12 ShippedRemember
UPDATE t SET col = expr, … WHERE …changes values; every SET expression sees the old row.DELETE FROM t WHERE …removes rows; without WHERE, both statements hit every row.Preview with SELECT, check the row count, and wrap important changes in BEGIN TRANSACTION … COMMIT / ROLLBACK. OUTPUT deleted.x, inserted.xshows values before and after.Foreign keys stop deletes that would leave rows pointing at nothing; TRUNCATE is all-or-nothing and resets IDENTITY.