Basic SQL DELETE Statement with Examples

By:   |   Updated: 2022-07-29   |   Comments (3)   |   Related: More > Triggers


Please do not scroll away - stay informed.
Dear Database Professional,

Did you know that MSSQLTips.com publishes new SQL Server content on a daily basis as well as offers free webinars and tutorials?

We know your day is hectic and you don't necessarily have time to research new topics and solutions every day, but we can keep you informed.

Take 30 seconds to register for our newsletter and look for free educational content to help you grow your career. >> REGISTER HERE <<

Thank you,
Greg Robidoux and Jeremy Kadlec (MSSQLTips.com Co-Founders)
Solution

In this SQL tutorial, I demonstrate the use of the SQL DELETE statement to remove 1 row, a group of rows, or all the existing records from a Microsoft SQL Server table.

Before diving in too deep, understand that a SQL DELETE operation is dangerous. Once you run the DELETE, there is no recycle bin like in the Windows operating system where you can retrieve deleted records! To retrieve records, you must restore a database from a backup that occurred before the DELETE. To avoid having to restore the database, I included a best practice approach using the following basic transaction control statements in conjunction with the DELETE statement: BEGIN TRANSACTION, COMMIT, and ROLLBACK. These are more advanced topics, but putting these into practice as you learn to use DELETE can save your day!

SQL DELETE Statement

The syntax for a basic DELETE query includes the DELETE keyword and the table name with the schema name, which is sometimes optional depending on the database design. Also, it is important to consider including a WHERE clause. The WHERE clause is important to control the rows being deleted. Note: if a WHERE clause is not included, all rows of the entire table will be purged! Microsoft sample database, Adventureworks, will be used for this demonstration.

Delete Basic Syntax:

DELETE schema.TableName
WHERE Col1 = 1; -- WHERE Condition

First - Setup the Delete Command Examples with Test Tables

First, I will set up a test table called dbo.MySalesPerson to use for my delete examples, as I do not want to mess up my sample database's integrity. Data will be loaded from existing tables to this table so we can issue DELETE statements throughout this demo.

Run the code below in the AdventureWorks database to set up the sample table for this demo.

USE [AdventureWorks]
GO
--A) CREATE TABLE statement to create our test table
CREATE TABLE [dbo].[MySalesPerson](
   [BusinessEntityID] [int] NOT NULL,
   [TerritoryID] [int] NULL,
   [SalesQuota] [money] NULL,
   [Bonus] [money]  NULL,
   [CommissionPct] [smallmoney]  NULL,
   [SalesYTD] [money]  NULL,
   [SalesLastYear] [money]  NULL,
   [rowguid] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
   [ModifiedDate] [datetime] NOT NULL,
);
GO
 
--B) Load Test Data from [Sales].[SalesPerson]
INSERT [dbo].[MySalesPerson]
SELECT *
FROM [Sales].[SalesPerson];
GO
 
--C) Insert Additional Rows for Non- Sales Persons.
INSERT [dbo].[MySalesPerson]
SELECT e.BusinessEntityID 
      ,1 as [TerritoryID]
      ,NULL as [SalesQuota]
      ,0 as [Bonus]
      ,1 as [CommissionPct]
      ,0 as [SalesYTD]
      ,0 as [SalesLastYear]
      ,e.[rowguid]
      ,Getdate() as [ModifiedDate]
FROM [HumanResources].[Employee] e
WHERE JobTitle = 'Database Administrator';
 
--D) Explore the Test Data.
SELECT * from [dbo].[MySalesPerson];
GO

Example 1 – Basic DELETE Statement

The following query shows a basic DELETE statement with a WHERE clause to control what records should be deleted i.e., only one record. A check query can show the record with BusinessEntityID = 271 no longer exists.

USE [AdventureWorks]
GO
 
--1) Basic Delete Statement
DELETE [dbo].[MySalesPerson]
WHERE BusinessEntityID = 271;
 
--Check Query
SELECT * FROM [dbo].[MySalesPerson]
WHERE BusinessEntityID = 271;
GO

Example 2 – DELETE Command with Join Criteria

The following query uses a Join Query to delete rows that are in another table (WHERE JobTitle = 'Pacific Sales Manager').

--2) Delete With a Query
DELETE [dbo].[MySalesPerson]
FROM [dbo].[MySalesPerson] m
   INNER JOIN [HumanResources].[Employee] e 
      ON e.BusinessEntityID = m.BusinessEntityID
WHERE JobTitle = 'Pacific Sales Manager';
 
--Check Query
SELECT JobTitle, m.*
FROM [dbo].[MySalesPerson] m
   INNER JOIN [HumanResources].[Employee] e 
      ON e.BusinessEntityID = m.BusinessEntityID
WHERE JobTitle = 'Pacific Sales Manager';
GO

Example 3 – Remove All Rows from a Table

For this example, I will demonstrate two ways to clear all the data from a table. The first option will use a DELETE statement without a WHERE clause. The second option uses TRUNCATE, which requires ALTER TABLE permissions or higher. Also, I am including a transaction control statement to show the results of DELETE and TRUNCATE statements to undo the transaction, so I do not need to reset and reload the demo data. Please see the link at the end of this article to learn more about Transaction Control Statements.

It is important to mention that once a "BEGIN TRAN" statement is issued, you must run a COMMIT or ROLLBACK statement. If not, a perpetual opened transaction will be left, potentially causing contention or blocking in the database. Note: if you run either DELETE or TRUNCATE without the "BEGIN TRAN", you will need to rerun the "Load Test Data" previously provided in this article, because the data will be gone.

--3a) Option a: DELETE ALL Rows
BEGIN TRAN;
DELETE [dbo].[MySalesPerson];
 
--Check Query
SELECT * FROM [dbo].[MySalesPerson];
 
--Now Rollback as I do not want to lose my data.
ROLLBACK;

--Show that no records where removed!
SELECT * FROM [dbo].[MySalesPerson];
 
----------------------------------------------
--3b) Option b:  TRUNCATE Data
 
--faster and uses fewer system and transaction log resources. per Microsoft Documents.
BEGIN TRAN;
TRUNCATE TABLE [dbo].[MySalesPerson];  --requires ALTER TABLE permissions or higher
 
SELECT * FROM [dbo].[MySalesPerson];
 
--Now Rollback as I do not want to lose my data.
ROLLBACK;
 
SELECT * FROM [dbo].[MySalesPerson];
GO

Example 4 – Delete Foreign Key Error

Occasionally, the database design may have defined constraints that prevent deleting data. This example shows what happens if you attempt to delete records from table Person.BusinessEntity that is referenced by a Foreign Key. This is a way to protect data to prevent orphaned records in the database.

--4) Delete Error because of Foreign Key
DELETE [Person].[BusinessEntity]
WHERE BusinessEntityID = 1;

GO

Note that the result of the above query is an error message (below) notifying that this DELETE cannot happen. To correct this issue, you must first delete the referenced records from the table Person.BusinessEntityAddress.

Msg 547, Level 16, State 0, Line 146 The DELETE statement conflicted with the REFERENCE constraint "FK_BusinessEntityAddress_BusinessEntity_BusinessEntityID". The conflict occurred in database "AdventureWorks", table "Person.BusinessEntityAddress", column 'BusinessEntityID'.

Example 5 – Using Best Practices When Deleting Data

When deleting records, it is helpful to show the rows that will be affected. To do this, run SET NOCOUNT OFF. This command may be off by default. Next, perform a SELECT query that identifies the rows that could be deleted. Then you are ready for the DELETE. Again, Transaction Control Statements will be used to verify the delete results and to decide to COMMIT or ROLLBACK the DELETE transaction should the validation fail. This recommendation introduces the OUTPUT clause, which allows for an integrated way to see the deleted records to help with validation.

I recommend using the delete template below, which includes the pre-check query, transaction control statements, and the OUTPUT clause, which will return the deleted rows. Keep in mind these extra actions are included due to the dangers of a DELETE. Note that the OUTPUT clause does not work with TRUNCATE.

SET NOCOUNT OFF;
--1) Check Query shows rows to be deleted
SELECT * FROM schema.TableName
WHERE Col1 = 1;
 
--2) Run the DELETE in a Transaction
BEGIN TRAN ;
DELETE schema.TableName
OUTPUT deleted.*
FROM schema.TableName
WHERE Col1 = 1;
 
--3) Run ROLLBACK if the count does not match
ROLLBACK;
--Or Run COMMIT if row count matches
COMMIT;

Option 5A – Best Practice Using OUTPUT Clause

An advantage of using the OUTPUT clause option is that the rows that have been deleted will be returned, allowing for positive verification.

----------------------------------------------------------------------
--5 OPTION A: Best Practice Output Query w/ ROLLBACK or COMMIT
----------------------------------------------------------------------
--Run SET NOCOUNT OFF to show row count results.
SET NOCOUNT OFF;
 
--1) Check Query shows rows to be deleted
SELECT m.*
FROM [dbo].[MySalesPerson] m
   INNER Join [HumanResources].[Employee] e 
      ON e.BusinessEntityID = m.BusinessEntityID
WHERE JobTitle = 'Database Administrator';
 
--2) Run the DELETE in a Transaction
BEGIN TRAN;
DELETE [dbo].[MySalesPerson]
OUTPUT Deleted.*
FROM [dbo].[MySalesPerson] m
   INNER Join [HumanResources].[Employee] e 
      ON e.BusinessEntityID = m.BusinessEntityID
WHERE JobTitle = 'Database Administrator'
--Stop Here to Validate.
 
--Now either Commit or Rollback the transaction based on the Check Query results! 
COMMIT;
--OR 
ROLLBACK;

Option 5B – Best Practice Using Check Queries

This option may be preferred as the concept to validate the results is the same, but it uses queries instead of the OUTPUT clause.

----------------------------------------------------------------------
--5 Option B: Best Practice Check Table w/ ROLLBACK or COMMIT
----------------------------------------------------------------------
 
--Run SET NOCOUNT OFF to show row count results.
SET NOCOUNT OFF;
 
--1) Check Query shows rows to be deleted
SELECT m.*
FROM [dbo].[MySalesPerson] m
   INNER Join [HumanResources].[Employee] e
      ON e.BusinessEntityID = m.BusinessEntityID
WHERE JobTitle = 'Database Administrator';
 
--2) Run the DELETE in a Transaction
BEGIN TRAN;
DELETE [dbo].[MySalesPerson]
FROM [dbo].[MySalesPerson] m
   INNER Join [HumanResources].[Employee] e
      ON e.BusinessEntityID = m.BusinessEntityID
WHERE JobTitle = 'Database Administrator'
 
--3) Again Run the Check Query to verify Results
SELECT m.*
FROM [dbo].[MySalesPerson] m
   INNER Join [HumanResources].[Employee] e
      ON e.BusinessEntityID = m.BusinessEntityID
WHERE JobTitle = 'Database Administrator';
--RUN THE Query Above Up to Here to review the output of the Check Query.
 
--Now either Commit or Rollback the transaction based on the Check Query results!
COMMIT;
--OR
ROLLBACK;
GO

Wrap Up

Through this demo, you have an opportunity to see how to DELETE data from a SQL table and to understand the dangers of unintended deletes and the importance to validate the delete results. See the links below to learn more about basic SQL statements and new topics presented throughout this article.

Next Steps



Get Started Now - Click here to get your free 14 day trial of SolarWinds Database Insights




get scripts

next tip button



About the author
MSSQLTips author Jim Evans Jim Evans is an IT Manager currently with Harsco who has managed DBAs, Application and BI Developers and Data Management teams for over 20 years.

View all my tips


Article Last Updated: 2022-07-29

Comments For This Article




Friday, July 29, 2022 - 5:22:39 PM - Jared Westover Back To Top (90323)
Nice article Jim! You gave some great examples.

Friday, July 29, 2022 - 12:11:54 PM - Greg Robidoux Back To Top (90321)
Hi Henn,

here are other articles related to deleting duplicate records:

https://www.mssqltips.com/sqlservertip/4486/find-and-remove-duplicate-rows-from-a-sql-server-table/

https://www.mssqltips.com/sqlservertip/1103/delete-duplicate-rows-with-no-primary-key-on-a-sql-server-table/

Thanks
Greg


Friday, July 29, 2022 - 11:34:08 AM - Henn Sarv Back To Top (90320)
Nice artice - explain major delete cases. One interesting case might be removing accidential duplicates from table. this is when there is NO unique or other promary indexes on that table.

One example I've used is following:

with dups1 as
(
select
*, ROW_NUMBER() over (partition by nimetus order by nimetus) nr
FROM tablewithdups
)
, dups2 as
(
select * from dups1
where nr > 1
)
delete
--select *
from dups2
go


download














get free sql tips
agree to terms