sql query to get deleted records
Asked Answered
S

5

13

You have a table table1 that contains id column, that is int(11), not null, auto_increment and starts from 1.

Suppose, you have 10,000 records. It is clear the id of the last record is 10,000. Once you removed 3 records, you have 9,997 records in the table, but the last record id value is still 10,000 (if the last record was not deleted).

How to display what records have been removed using 1 sql query?

Thank you.

Sherrylsherurd answered 19/6, 2012 at 14:21 Comment(3)
Create a table with 10,000 sequential values to act as a lookup table. Then select all records from your lookup that don't exist in your target table. (This will always be faster than trying to dynamically generate the missing IDs.)Selfimportant
If your DMBS has a generate_series() like function (I don't think mysql has it) you could use that, based on {min,max}, instead of a hardwired calander table.Agglutinin
+1 because I learnt something new by answering it :) A decent question :)Detrusion
L
5

I think easiest would be to have a dummy/temp table with just ids. 1-1000 then left join to that table.

But be sure to remove the "deleted" records from your dummy/temp table once you're done. Otherwise, they will show up every time.

>> EDIT << You can do self join to figure out if you're missing ids....

select a.id + 1 MissingIds
from <table> a
left join <table> b
  on a.id = b.id - 1
where b.id is null
  and a.id < 10000
Lockman answered 19/6, 2012 at 14:28 Comment(12)
all you really need is a numbers table or way to generate numbers on the flyConstringe
@AndriusNaruševičius Disagree. Sometimes it's professional to have a fail prove method, not a overcomplicated one.Antiperiodic
@AndriusNaruševičius what exactly is not professional about it? is it too fast and too easy so you can't charge many hours to your client???Boredom
Don't meant to be rude. Please define "professional." Every solution is unique to each situation. Not saying this is the best solution but dismissing it due to it not being "professional" is not right. Dismissing it because there's a better more scale-able/manageable solution... now we're cooking!Lockman
@samyi one way to improve it might be to create a real table with "check_deleted_timestamp" column and update these records when checking the deletion - or "deleted_timestamp" with a trigger when deleting the original recordsBoredom
@deathApril Yup. Soft-deleting. Good call.Lockman
@AndriusNaruševičius - I disagree too. SQL Operates on existant sets. This question is about determining a non-existant set. This is much more appropriately completed by intersecting the samplei-set with the complete-set, and taking the completement of the complete-set. Even if you want to avoid talking in sets, the approcch can be re-described as using a cache, rather than using a set-based language to re-build sets of data every query.Selfimportant
What about creating a trigger and putting deleted ids into another table? And stop picking on me, I just said what it looked for me, didn't say it was bad.Detrusion
@AndriusNaruševičius please post your own answer if thing that solution would be more appropriate than this oneBoredom
I will later today :) Not at home and sqlfiddle is down for me for some reason :/Detrusion
@AndriusNaruševičius, I'm sorry.. wasn't trying pick on you. Just trying to help the OP with some direction. Didn't mean to upset anyone with my comment.Lockman
Don't worry, it's just me that is very sensitive :) Please check my answer as I promised: #11103516Detrusion
P
2

You may find this sql useful here http://www.sqlservercurry.com/2009/06/find-missing-identity-numbers-in-sql.html

Phrenetic answered 19/6, 2012 at 14:29 Comment(0)
M
2

I used this answer as a reference.

You can use the following query to find the gaps, which in essence will give you the deleted record "ranges". For example, in the below example, you get 2 rows back in the final result, and the values are 2 and 3, and 6 and 7. So you know that rows with IDs of 2 through 3 have been deleted, and rows with IDs of 6 through 7 have been deleted (for a total of 4 deleted rows).

I believe this meets your requirement of getting the final result in "1 SQL query", and plus, no intermediate or dummy tables are used.

delimiter $$
use test
$$

create table mytable (id int not null auto_increment, name varchar(100), primary key (id));
$$

insert into mytable (name) values('a')$$
insert into mytable (name) values('b')$$
insert into mytable (name) values('c')$$
insert into mytable (name) values('d')$$
insert into mytable (name) values('e')$$
insert into mytable (name) values('f')$$
insert into mytable (name) values('g')$$
insert into mytable (name) values('h')$$


delete from mytable where id = 2$$
delete from mytable where id = 3$$
delete from mytable where id = 6$$
delete from mytable where id = 7$$


SELECT (t1.id + 1) as gap_starts_at
     , (SELECT MIN(t3.id) -1
          FROM mytable t3 
         WHERE t3.id > t1.id) as gap_ends_at
  FROM mytable t1
 WHERE NOT EXISTS (SELECT t2.id FROM mytable t2 WHERE t2.id = t1.id + 1)
HAVING gap_ends_at IS NOT NULL

Output:

gap_starts_at  gap_ends_at
2              3
6              7
Manufacturer answered 19/6, 2012 at 14:49 Comment(0)
R
1
DECLARE @myTestTable1 TABLE
(
id INT IDENTITY(1,1) NOT NULL
,testVal int
)

DECLARE @increment AS int = 1

WHILE (@increment <= 10000)
BEGIN
INSERT INTO @myTestTable1
VALUES (@increment)

SET @increment += 1
 END

DELETE FROM @myTestTable1 WHERE id IN (100,200,300)

--SELECT * FROM @myTestTable1

 ;WITH Missing (missnum, maxid)
 AS
 (
  SELECT 1 AS missnum, (select max(id) from @myTestTable1)
  UNION ALL
   SELECT missnum + 1, maxid FROM Missing
   WHERE missnum < maxid
   )
     SELECT missnum
     FROM Missing
     LEFT OUTER JOIN @myTestTable1 tt on tt.id = Missing.missnum
     WHERE tt.id is NULL
     OPTION (MAXRECURSION 0); 

But it's taking lot of time. We have to reduce the time.

Remiss answered 19/6, 2012 at 14:48 Comment(0)
C
1

So to start with, I am going to show the easiest way to generate 10.000 records. No huge-ass queries, no variables. Execution time: ~3ms. LINK

Now about that trigger that I promised. LINK

As you can see, it is really easy to create one. Keep in mind that the trigger is better not only for no need of various joins, but you can also store the date, user id, etc. etc. (very expandable example that is). And the main point of trigger over joins is: you do not care how many records there were/are/will be. You don't need to be strict about the size. That's why I called the answer of sam yi not professional enough. Sorry for misunderstanding, I am pretty sure none of us wanted to insult anyone.

By creating this example I did learn a few things. Hopefully you did too :)

Centrosymmetric answered 19/6, 2012 at 17:39 Comment(1)
sqlfiddle.com/#!2/ab17a/1 - I have built a UI alternative to the "delimiter" command (note the "|") below the schema panel. Using this, you can build your trigger-based option.Fanechka

© 2022 - 2024 — McMap. All rights reserved.