145

I have a table in SQL Server. I would like to delete the top 1000 rows from it. However, I tried this, but I instead of just deleting the top 1000 rows it deleted all the rows in the table.

Here is the code:

delete from [mytab] 
select top 1000 
a1,a2,a3
from [mytab]
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
edgarmtze
  • 24,683
  • 80
  • 235
  • 386
  • 8
    You need an ORDER BY to make TOP meaningful: see @Martin Smith's answer which is the *only* one of five to have this. I despair sometimes – gbn Jan 21 '12 at 20:24
  • 2
    Do you want to delete *any* 1000 rows? Just randomly selected? Or, for example, the top 1000 oldest rows? – Nick Chammas Jan 21 '12 at 20:55
  • 15
    You deleted all the table because `delete from [mytab]` is one statement, and `select top ...` is another. – Nick Chammas Jan 21 '12 at 21:11
  • 2
    You don't need ordering for top, depends why you are doing TOP. If you need to remove 10 million rows and have 1 GB of log space available use Delete TOP(10000) From dbo.myTable (with your select clause) and keep running it till there are no more rows to delete. Who cares if its arbitrary. Sorting only slows the query. – tvanharp Jun 06 '16 at 16:04
  • 1
    I realize that this is an ancient question (in SO years) but I do think that **it is important that people consider the comments of @gbn**. While his comments do not apply to my given situation (trying to delete blocks of records without causing LOCK issues but not really caring about the order in which they are deleted) they may very likely apply to YOUR situation. Make sure you consider them before blindly utilizing answers below that do not include an ORDER BY clause. – Andrew Steitz Aug 22 '16 at 15:45
  • You should also read up on SQL lock escalation as there are reasons not to delete more than 5000 rows at a time in a production system. https://technet.microsoft.com/en-us/library/ms184286(v=sql.105).aspx – Chris Ward Dec 28 '16 at 20:04

9 Answers9

247

The code you tried is in fact two statements. A DELETE followed by a SELECT.

You don't define TOP as ordered by what.

For a specific ordering criteria deleting from a CTE or similar table expression is the most efficient way.

;WITH CTE AS
(
SELECT TOP 1000 *
FROM [mytab]
ORDER BY a1
)
DELETE FROM CTE
Liam
  • 27,717
  • 28
  • 128
  • 190
Martin Smith
  • 438,706
  • 87
  • 741
  • 845
  • 20
    For those wondering why you can't do `DELETE TOP (1000) FROM table ORDER BY column`, [read this](http://msdn.microsoft.com/en-us/library/ms189463.aspx): "The rows referenced in the TOP expression used with INSERT, UPDATE, MERGE, or DELETE are not arranged in any order." – Nick Chammas Jan 21 '12 at 21:06
  • 3
    @Magnus yes. Not 2000 though. It might be possible to use a derived table in 2000. I haven't an instance around to test with. – Martin Smith Jan 30 '14 at 16:02
  • All you need to do is: SET ROWCOUNT 1000; DELETE FROM [MyTable] WHERE ..... thats what the ROWCOUNT function is for! – Joe Bourne Mar 14 '14 at 18:01
  • 6
    @Joe [Using SET ROWCOUNT will not affect DELETE, INSERT, and UPDATE statements in a future release of SQL Server. Avoid using SET ROWCOUNT with DELETE, INSERT, and UPDATE statements in new development work, and plan to modify applications that currently use it.](http://technet.microsoft.com/en-us/library/ms188774.aspx) – Martin Smith Mar 14 '14 at 18:07
  • 2
    I had done a slightly different way (though I think the CTE might be nicer to look at): DELETE T1 FROM (SELECT TOP 1000 * FROM [MYTAB] ORDER BY A1) T1; – Abacus Aug 08 '14 at 14:48
  • why the leading `;`? – Liam Jun 15 '17 at 14:57
  • 5
    @Liam - just because if there is any preceding statement before the CTE this needs to be terminated with a semicolon so appending it to the front of the `WITH` pre-empts complaints from people that haven't done that. – Martin Smith Jun 15 '17 at 18:22
  • If i have 2 million rows that and the query times out for even 200k, is there any way to make this query loop and perform the delete in chunks? – rollsch Jan 23 '20 at 00:06
  • This solution is also faster than similar `delete from table where id in (select top 1000 id from table order by a1)`. In my case with 50 million rows difference was 20% to 80%. – endo64 Mar 21 '20 at 09:11
126

May be better for sql2005+ to use:

DELETE TOP (1000)
FROM [MyTab]
WHERE YourConditions

For Sql2000:

DELETE FROM [MyTab]
WHERE YourIdField IN 
(
  SELECT TOP 1000 
    YourIdField 
  FROM [MyTab]
  WHERE YourConditions
)

BUT

If you want to delete specific subset of rows instead of arbitrary subset, you should explicitly specify order to subquery:

DELETE FROM [MyTab]
WHERE YourIdField IN 
(
  SELECT TOP 1000 
    YourIdField 
  FROM [MyTab]
  WHERE YourConditions
  ORDER BY ExplicitSortOrder
)

Thanks tp @gbn for mentioning and demanding the more clear and exact answer.

Oleg Dok
  • 21,109
  • 4
  • 45
  • 54
  • 3
    @gbn Maybe useless for you, but still that is exactly what the question is asking for. – Joachim Isaksson Jan 21 '12 at 20:28
  • 1
    @Joachim Isaksson: go and read up about TOP then come back. There is no such thing as TOP without an ORDER BY in sets. Alternatively, go and find me a canonical reference that proves me wrong... To save you searching, http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/05/20/without-order-by-there-is-no-default-sort-order.aspx and http://blogs.technet.com/b/wardpond/archive/2007/07/19/database-programming-top-without-order-by.aspx – gbn Jan 21 '12 at 20:30
  • 1
    @gbn No any conditions about WHICH rows to delete, so ORDER BY in subquery is useless – Oleg Dok Jan 22 '12 at 07:43
  • 1
    @gbn Did you mention WHERE in subquery - I filter 1000 arbitrary rows inside choosen criteria and delete then. Valid scenario? Yes. If I add ORDER BY NEWID() or whatever it changes nothing - I still delete 1000 rows filtered by choosen criteria – Oleg Dok Jan 22 '12 at 08:11
  • 1
    @gbn The question is: **I have a table in SQL I would like to delete 1000 rows from it I tried this, But I deleted all table**. Where do you see **top** word in the question. So, If it not stated than we need to delete top X from smth. so we assume that we should delete 1000 arbitrary rows. Sounds weird - agree, but valid scenario? - agree too – Oleg Dok Jan 22 '12 at 08:17
  • @OLegDok: I see it in the question title: ***"delete top 1000 rows ..."*** – ypercubeᵀᴹ Mar 14 '12 at 16:51
  • 11
    @gbn In case you're looking for a valid use of TOP without ORDER BY: what brought me here is I need to delete all rows matching some criteria but, for performance reasons, I don't want it to delete more than 10,000 rows at a time. I don't care which rows it deletes, as I will run the command again at some interval until all such rows are gone. – Richiban Oct 13 '14 at 16:09
  • 1
    important: paranthesis after top is important – Iman Dec 06 '14 at 04:15
  • Using TOP (X) with delete is absolutely a valid scenario and anyone who manages large databases should be aware of it for managing performance while deleting massive amounts of data with concurrent load. It is also the simplest way to script unordered deletes that can easily be changed from batched to non-batched form. Unordered deletes are valuable when you want to create the least amount of work on the database. – Anonymike Dec 29 '21 at 16:48
32

As defined in the link below, you can delete in a straight forward manner

USE AdventureWorks2008R2;
GO
DELETE TOP (20) 
FROM Purchasing.PurchaseOrderDetail
WHERE DueDate < '20020701';
GO

http://technet.microsoft.com/en-us/library/ms175486(v=sql.105).aspx

Sabyasachi Mishra
  • 1,677
  • 2
  • 31
  • 49
Rishi Bhatnagar
  • 493
  • 4
  • 6
8
delete from [mytab]
where [mytab].primarykeyid in
(
select top 1000 primarykeyid
from [mytab]
)
Bassam Mehanni
  • 14,796
  • 2
  • 33
  • 41
Jason Dam
  • 390
  • 1
  • 8
  • 4
    Useless: TOP without an ORDER BY gives arbitrary rows – gbn Jan 21 '12 at 20:22
  • 4
    @gbn Maybe useless for you, but still that is exactly what the question is asking for. – Joachim Isaksson Jan 21 '12 at 20:26
  • @Joachim Isaksson: I'd downvote more if I could for arguing. Go and read up about TOP then come back. There is no such thing as TOP without an ORDER BY in sets. Alternatively, go and find me a canonical reference that proves me wrong... To save you searching, http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/05/20/without-order-by-there-is-no-default-sort-order.aspx and http://blogs.technet.com/b/wardpond/archive/2007/07/19/database-programming-top-without-order-by.aspx – gbn Jan 21 '12 at 20:28
  • 3
    @gbn I did not claim that there is any default sort order or that the query is even in any way useful, I just reminded you that the question did not ask for one so what would you suggest ordering on? – Joachim Isaksson Jan 21 '12 at 20:40
  • 2
    @gbn I don't know why you're so hostile to everyone over something that is a starting point. I do not claim that my answer is the end all, it is merely a suggestion to help someone out. I think the importance is the keys that are coming back from the sub query here. – Jason Dam Jan 21 '12 at 21:01
  • 2
    This may be all that the asker is looking for. I would just add a note for others reading to stress that the rows deleted by such a statement are not guaranteed to be in any order. – Nick Chammas Jan 21 '12 at 21:14
  • @Jason Dam: because of too many upvotes for incorrect answers. OP said "TOP". Top *what*? Quite simple. – gbn Jan 21 '12 at 21:33
  • @gbn in my case, I am trying to purge a large subset of records from a bloated table and don't care WHAT ORDER they are deleted in, just as long as they meet the criteria, but I don't want to delete ALL of them at once to prevent LOCK problems. And NO, I cannot do the "copy records I want to keep to new table, then drop current table and finally rename new table to old name" routine. Try to be a little more open minded and a little less hostile. – Andrew Steitz Aug 22 '16 at 15:10
  • @gbn, HOWEVER, your point is a valid one that people should take into consideration. (Credit where credit is due, even if I disagree with delivery!) – Andrew Steitz Aug 22 '16 at 15:21
4
SET ROWCOUNT 1000;

DELETE FROM [MyTable] WHERE .....
Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
Joe Bourne
  • 1,144
  • 10
  • 18
  • 2
    When dealing with just 1000 rows, does it really matter?? If it was 100,000,000 rows then your points might be valid, but for just 1000 rows, this is by far the simplest solution proposed so far for SQL 2008. – Joe Bourne Mar 17 '14 at 09:46
3

It is fast. Try it:

DELETE FROM YourTABLE
FROM (SELECT TOP XX PK FROM YourTABLE) tbl
WHERE YourTABLE.PK = tbl.PK

Replace YourTABLE by table name, XX by a number, for example 1000, pk is the name of the primary key field of your table.

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
  • You are effectively creating two tables from one, and then deleting where joined. It works well when you want to delete the oldest (or newest) records from a table, since you can sort them ascending first. This t-sql is accepted by Microsoft (and it's fast). – Tequila Jan 21 '16 at 18:37
1

I agree with the Hamed elahi and Glorfindel.

My suggestion to add is you can delete and update using aliases

/* 
  given a table bi_customer_actions
  with a field bca_delete_flag of tinyint or bit
    and a field bca_add_date of datetime

  note: the *if 1=1* structure allows me to fold them and turn them on and off
 */
declare
        @Nrows int = 1000

if 1=1 /* testing the inner select */
begin
  select top (@Nrows) * 
    from bi_customer_actions
    where bca_delete_flag = 1
    order by bca_add_date
end

if 1=1 /* delete or update or select */
begin
  --select bca.*
  --update bca  set bca_delete_flag = 0
  delete bca
    from (
      select top (@Nrows) * 
        from bi_customer_actions
        where bca_delete_flag = 1
        order by bca_add_date
    ) as bca
end 
0

To enhance the accepted answer code, check out mine. The code will start by deleting 1000 rows from the table at a time, and then increase the batch size by 10% after each iteration. This way, the code will delete more rows as the table gets smaller and reduce the number of loops needed. The variable @count will still store the number of rows affected by each delete statement and the loop will stop when it becomes zero.

DECLARE @batch_size INT = 10000
DECLARE @count INT = 1
WHILE @count > 0
BEGIN
    ;WITH CTE AS
    (
    SELECT TOP (@batch_size) *
    FROM [mytab]
    ORDER BY a1
    )
    DELETE FROM CTE

    SET @count = @@ROWCOUNT

    -- Increase the batch size by 10% after each iteration
    SET @batch_size = @batch_size * 1.1
END
Joseph Wambura
  • 2,732
  • 2
  • 21
  • 24
0

No need to create CTE to fetch and delete as it will load the records in memory first and then perform the delete operation which might impact the performance.

All you need is simple delete top(n) based on your condition to remove the records from your table which is fast and efficient for non-partitioned table.

delete top(1000)
from [mytab] 
where 
 condition[1]
 condition[2]
 .
 .
 condition[n]