Reputation: 5918
Is there any way to delete all the rows in a table except one (random) row, without specifying any column names in the DELETE
statement?
I'm trying to do something like this:
CREATE TABLE [dbo].[DeleteExceptTop1]([Id] INT)
INSERT [dbo].[DeleteExceptTop1] SELECT 1
INSERT [dbo].[DeleteExceptTop1] SELECT 2
INSERT [dbo].[DeleteExceptTop1] SELECT 3
SELECT * FROM [dbo].[DeleteExceptTop1]
DELETE
FROM [dbo].[DeleteExceptTop1]
EXCEPT
SELECT TOP 1 * FROM [dbo].[DeleteExceptTop1]
SELECT * FROM [dbo].[DeleteExceptTop1]
The final SELECT
should yield one row (could be any of the three).
Upvotes: 4
Views: 6532
Reputation: 561
I know it has been answered but what about?
DELETE
FROM [dbo].[DeleteExceptTop1]
Where Id not in (
SELECT TOP 1 * FROM [dbo].[DeleteExceptTop1])
Upvotes: 1
Reputation: 2626
It seems like the simplest answer may be the best. The following should work:
Declare @count int
Set @count=(Select count(*) from DeleteExceptTop1)-1
Delete top (@count) from DeleteExceptTop1
Upvotes: 1
Reputation: 453308
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT newid())) AS RN
FROM [dbo].[DeleteExceptTop1]
)
DELETE FROM CTE
WHERE RN > 1
Or similar to @abatishchev's answer but with more variety in the ordering and avoiding deprecated constructs.
DECLARE @C INT
SELECT @C = COUNT(*) - 1
FROM [dbo].[DeleteExceptTop1]
IF @c > 0
BEGIN
WITH CTE AS
(
SELECT TOP(@C) *
FROM [dbo].[DeleteExceptTop1]
ORDER BY NEWID()
)
DELETE FROM CTE;
END
Or a final way that uses EXCEPT
and assumes no duplicate rows and that all columns are of datatypes compatible with the EXCEPT
operator
/*Materialise TOP 1 to ensure only evaluated once*/
SELECT TOP(1) *
INTO #T
FROM [dbo].[DeleteExceptTop1]
ORDER BY NEWID()
;WITH CTE AS
(
SELECT *
FROM [dbo].[DeleteExceptTop1] T1
WHERE EXISTS(
SELECT *
FROM #T
EXCEPT
SELECT T1.*)
)
DELETE FROM CTE;
DROP TABLE #T
Upvotes: 6
Reputation: 100288
Try:
declare @c int
select @c = count(*) - 1 from [dbo].[DeleteExceptTop1]
IF @c > 0
BEGIN
set RowCount @c
delete from [dbo].[DeleteExceptTop1]
END
Upvotes: 3
Reputation: 10444
No.
You need to use a column name (such as that of the primary key) to identify which rows you want to remove.
"random row" has no meaning in SQL except its data. If you want to delete everything except some row, you must differentiate that row from the others you with to DELETE
EXCEPT
works by comparing the DISTINCT
values in the row.
EDIT: If you can specify the primary key then this is a trivial matter. You can simply DELETE
where the PK <>
your "random" selection or NOT IN
your "random" selection(s).
EDIT: Apparently I'm wrong about the need to specify any column name, you can do it using the assigned ROW_NUMBER
.. But I'm not going to delete my answer because it references your use of EXCEPT
which was discussed in the comments. You cannot do it without deriving some column name like that from ROW_NUMBER
Upvotes: 2
Reputation: 500
You could do something like this (SQL 2008)
DECLARE @Original TABLE ([Id] INT)
INSERT INTO @Original(ID) VALUES(1)
INSERT INTO @Original(ID) VALUES(2)
INSERT INTO @Original(ID) VALUES(3)
SELECT * FROM @Original;
WITH CTE AS
(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS ROW, ID FROM @Original)
DELETE @Original
FROM @Original O
INNER JOIN CTE ON O.ID = CTE.ROW
WHERE ROW > 1
SELECT * FROM @Original
Upvotes: 1