Delete new record if same data exists - sql

I want to delete new record if the same record created before.
My columns are date, time and MsgLog. If date and time are same, I want to delete new one.
I need help .

You can check in the table whether that value exists or not in the column using a query. If it exists, you can show message that a record already exists.

To prevent such kind of erroneous additions you can add restriction to your table to ensure unique #Date #Time pairs; if you don't want to change data structure (e.g. you want to add records with such restrictions once or twice) you can exploit insert select counstruction.
-- MS SQL version, check your DBMS
insert into MyTable(
Date,
Time,
MsgLog)
select #Date,
#Time,
#MsgLog
where not exists(
select 1
from MyTable
where (#Date = Date) and
(#Time = Time)
)
P.S. want to delete new one equals to do not insert new one

You should create a unique constraint in the DB level to avoid invalid data no matter who writes to your DB.
It's always important to have your schema well defined. That way you're safe that no matter how many apps are using your DB or even in case someone just writes some inserts manually.
I don't know which DB are you using but in MySQL can use to following DDL
alter table MY_TABLE add unique index(date, time);
And in Oracle you can :
alter table MY_TABLE ADD CONSTRAINT constaint_name UNIQUE (date, time);
That said, you can also (not instead of) do some checks before inserting new values to avoid dealing with exceptions or to improve performance by avoiding making unnecessary access to your DB (length \ nulls for example could easily be dealt with in the application level).

You can avoid deleting by checking for duplicate while inserting.
Just modify your insert procedure like this, so no duplicates will entered.
declare #intCount as int;
select #intCount =count(MsgLog) where (date=#date) and (time =#time )
if #intCount=0
begin
'insert procedure
end
> Edited
since what you wanted is you need to delete the duplicate entries after your bulk insert. Think about this logic,
create a temporary table
Insert LogId,date,time from your table to the temp table order by date,time
now declare four variables, #preTime,#PreDate,#CurrTime,#CurrDate
Loop for each items in temp table, like this
while
begin
#pkLogID= ' Get LogID for the current row
select #currTime=time,#currDate=date from tblTemp where pkLogId=#pkLogID 'Assign Current values
'Delete condition check
if (#currDate=#preDate) and (#currTime=#preTime)
begin
delete from MAINTABLE where pkLogId=#pkLogID
end
select #preDate=#currDate,#preTime=#currTime 'Assign current values as preValues for next entries
end
The above strategy is we sorted all entries according to date and time, so duplicates will come closer, and we started to compare each entry with its previous, when match found we deleting the duplicate entry.

Related

Edit inserted table sql

When insert I need edit a value if it is null. I create a trigger but I don't know how to edit inserted table.
ALTER TRIGGER [trigger1] on [dbo].[table]
instead of insert
as
declare #secuencia bigint, #ID_PERSONA VARCHAR;
select #secuencia = SECUENCIA from inserted
select #ID_PERSONA = ID_PERSONA from inserted
if #secuencia is null begin
set inserted.SECUENCIA = NEXT VALUE FOR SEQ_BIOINTEG --(Sequence)
end
i dont know how to edit inserted table.
You do not. That table is read only.
Note how your trigger also says:
instead of insert
There is no way to edit the inserted table.
What you do instead, is setting up an INSERT command for the original table, using the data from the inserted table to filter to the ROWS of inserted - mostly by a join.
Changing inserted makes no sense, logically - because triggers in SQL are one of two things:
INSTEAD OF - then there is no actual insert happening for inserted to start with. Instead of doing the insert, the trigger is called. As such, changing inserted - makes no sense.
AFTER - then the insert already happened (and you UPDATE the rows). As the trigger runs after the update, changing inserting makes no sense.
Note that I say ROWS - your trigger has one very basic error: it assumes inerted contains ONE row. It is a table - it is possible the changes come from an insert statement that inserts multiple rows (which is trivial, i.e. select into, or simply an insert with values for multiple rows). Handle those.
select #ID_PERSONA = ID_PERSONA from inserted
Makes NO sense - inserted is a table, so ID_PERSONA from inserted contains what value, if 2 rows are inserted? You must treat inserted like any other table.
Apart from all the varied issues with your trigger code, as mentioned by others, the easiest way to use a SEQUENCE value in a table is to just put it in a DEFAULT constraint:
ALTER TABLE dbo.[table]
ADD CONSTRAINT DF_table_seq
DEFAULT (NEXT VALUE FOR dbo.SEQ_BIOINTEG)
FOR SECUENCIA;

which delete statement is better for deleting millions of rows

I have table which contains millions of rows.
I want to delete all the data which is over a week old based on the value of column last_updated.
so here are my two queries,
Approach 1:
Delete from A where to_date(last_updated,''yyyy-mm-dd'')< sysdate-7;
Approach 2:
l_lastupdated varchar2(255) := to_char(sysdate-nvl(p_days,7),'YYYY-MM-DD');
insert into B(ID) select ID from A where LASTUPDATED < l_lastupdated;
delete from A where id in (select id from B);
which one is better considering performance, safety and locking?
Assuming the delete removes a significant fraction of the data & millions of rows, approach three:
create table tmp
Delete from A where to_date(last_updated,''yyyy-mm-dd'')< sysdate-7;
drop table a;
rename tmp to a;
https://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:2345591157689
Obviously you'll need to copy over all the indexes, grants, etc. But online redefinition can help with this https://oracle-base.com/articles/11g/online-table-redefinition-enhancements-11gr1
When you get to 12.2, there's another simpler option: a filtered move.
This is an alter table move operation, with an extra clause stating which rows you want to keep:
create table t (
c1 int
);
insert into t values ( 1 );
insert into t values ( 2 );
commit;
alter table t
move including rows where c1 > 1;
select * from t;
C1
2
While you're waiting to upgrade to 12.2+ and if you don't want to use the create-as-select method for some reason then approach 1 is superior:
Both methods delete the same rows from A* => it's the same amount of work to do the delete
Option 1 has one statement; Option 2 has two statements; 2 > 1 => option 2 is more work
*Statement level consistency means you might get different results running the processes. Say another session tries to update an old row that your process will remove.
With just the delete, the update will be blocked until the delete finishes. At which point the row's gone, so the update does nothing.
Whereas if you do the insert first, the other session can update & commit the row before the insert completes. So the update "succeeds". But the delete will then remove it! Which can lead to some unhappy customers...
Your stored dateformat seems suitable for proper sorting, so you could go the other way round and convert sysdate to string:
--this is false today
select * from dual where '2019-06-05' < to_char(sysdate-7, 'YYYY-MM-DD');
--this is true today
select * from dual where '2019-05-05' < to_char(sysdate-7, 'YYYY-MM-DD');
So it would be:
Delete from A where last_updated < to_char(sysdate-7, ''yyyy-mm-dd'');
It has the benefit that your default index (if there is any) will be used.
It has the disadvantage on relying on the String/Varchar ordering which might be changed i.e. bei NLS changes (if i remember right), so in any case you should do a little testing before...
In the long term, you should of cource alter the colum to a proper date-datatype, but I guess that doesn't help you right now ;)
If you are trying to delete most of the rows in the table, I would advise you go with a different approach, namely:
create <new table name> as
select *
from <old table name>
where <predicates for the data you want to keep>;
then
drop table <old table name>;
and finally you can rename the new table back to the old table.
You could always partition the new table (i.e. create the new table with a separate statement containing the partitioning clauses, and then have an insert as select into the new table from the old table).
That way, when you need to delete rows, it's a simple matter of dropping the relevant partition(s).

Concat specific string to every inserted row

This is an hypothetical case..
I'm trying to find a good approach to make sure that every value inserted in an specific column col1 of my table mytable has a specific string http:// at the begining of the value.
Example:
I want to insert myprofile into mytable so (after my check condition..) the final value would be http://myprofile
I guess that a good approach could be using a trigger on insert but I didn't find anything concrete yet..
Any ideas?
Thank you.
You can try something like this as a starting point - this is for SQL Server (don't know MySQL well enough to provide that trigger code for you):
-- create the trigger, give it a meaningful name
CREATE TRIGGER PrependHttpPrefix
ON dbo.YourTableName -- it's on a specific table
AFTER INSERT, UPDATE -- it's for a specific operation, or several
AS
BEGIN
-- the newly inserted rows are stored in the "Inserted" pseudo table.
-- It has the exact same structure as your table that this trigger is
-- attached to.
-- SQL Server works in such a way that if the INSERT affected multiple
-- rows, the trigger is called *once* and "Inserted" contains those
-- multiple rows - you need to work with "Inserted" as a multi-row data set
--
-- You need to join the "Inserted" rows to your table (based on the
-- primary key for the table); for those rows newly inserted that
-- **do not** start with "http://" in "YourColumn", you need to set
-- that column value to the fixed text "http:/" plus whatever has been inserted
UPDATE tbl
SET YourColumn = 'http://' + i.YourColumn
FROM dbo.YourTableName tbl
INNER JOIN Inserted i ON tbl.PKColumn = i.PKColumn
WHERE LEFT(i.YourColumn, 7) <> 'http://'
END

Most efficient way to sustain a maximum number of entries in SQL Server database table

I was wondering, what is the most efficient way of doing the following?
I'm trying to implement some sort of an auditing system where each logon to my page will be stored in a database. I use SQL Server 2005 database. The table that stores the auditing data obviously cannot grow without an upper limit. So, say, it should have a maximum of 1000 entries and then any older entries must be deleted when new ones are inserted. The question is how do you do this in a most efficient way -- do I need to add any special columns, like, say an ordinal entry number for easier clean-up?
EDIT:
Say, if the structure of my table is (pseudo code):
`id` BIGINT autoincrement
`date` DATETIME
`data1` NVARCHAR(256)
`data2` INT
How would you write this cleanup procedure?
As Tony mentioned, use dates to identify the inserts. In addition, use a clustered index on the date field, so that inserts are always at the end of the table and it is easy and efficient to scan through and delete the old rows.
If you use a number, something like this should work:
DELETE FROM myTable WHERE someField < (SELECT MAX(someField) - 1000 FROM myTable)
For a date, deleting everything older than one day would be something like:
DELETE FROM myTable WHERE someField < DateAdd('d', -1, getdate())
Do it by date not number. Have a look at your stats, see how many days 1000 is / will be. Delete anything older than that. Auditing is never particularly efficient, but if you have loads of data that doesn't help you that's very inefficient....
If I understand your needs, this should work. I've tested it on SQL 2008R2, but I can not see any reason why it would not work on SQL Server 2005.
Use a logon trigger to insert a row into your audit table.
Create an AFTER INSERT trigger on your audit table that deletes the row with MIN(ID).
Here's some code to play with:
/* Create audit table */
CREATE TABLE ServerLogonHistory
(SystemUser VARCHAR(512),
ID BIGINT,
DBUser VARCHAR(512),
SPID INT,
LogonTime DATETIME)
GO
/* Create Logon Trigger */
CREATE TRIGGER Tr_ServerLogon
ON ALL SERVER FOR LOGON
AS
BEGIN
INSERT INTO TestDB.dbo.ServerLogonHistory
SELECT SYSTEM_USER, MAX(ID)+1 , USER,##SPID,GETDATE()
FROM TestDB.dbo.ServerLogonHistory;
END
GO
/* Create the cleanup trigger */
CREATE TRIGGER AfterLogin
ON TestDB.dbo.ServerLogonHistory
AFTER INSERT
AS
DELETE
FROM TestDB.dbo.ServerLogonHistory
WHERE ID =
(SELECT MIN(ID) FROM TestDB.dbo.ServerLogonHistory);
GO;
A word of warning. If you create an invalid logon trigger, you'll not be able to logon to the database. But don't panic! It's all part of learning. You'll be able to use 'sqlcmd' to drop the bad trigger.
I did try to delete the row with the min ID in the logon trigger, but I was not able to get that to work.
Is the "ID" column a Identity column with step 1?
after you insert one row
delete column where id<IDENTITY_CURRENT(YOUR_TABLE)-1000

Prevent inserting overlapping date ranges using a SQL trigger

I have a table that simplified looks like this:
create table Test
(
ValidFrom date not null,
ValidTo date not null,
check (ValidTo > ValidFrom)
)
I would like to write a trigger that prevents inserting values that overlap an existing date range. I've written a trigger that looks like this:
create trigger Trigger_Test
on Test
for insert
as
begin
if exists(
select *
from Test t
join inserted i
on ((i.ValidTo >= t.ValidFrom) and (i.ValidFrom <= t.ValidTo))
)
begin
raiserror (N'Overlapping range.', 16, 1);
rollback transaction;
return
end;
end
But it doesn't work, since my newly inserted record is part of both tables Test and inserted while inside a trigger. So the new record in inserted table is always joined to itself in the Test table. Trigger will always revert transation.
I can't distinguish new records from existing ones. So if I'd exclude same date ranges I would be able to insert multiple exactly-same ranges in the table.
The main question is
Is it possible to write a trigger that would work as expected without adding an additional identity column to my Test table that I could use to exclude newly inserted records from my exists() statement like:
create trigger Trigger_Test
on Test
for insert
as
begin
if exists(
select *
from Test t
join inserted i
on (
i.ID <> t.ID and /* exclude myself out */
i.ValidTo >= t.ValidFrom and i.ValidFrom <=t.ValidTo
)
)
begin
raiserror (N'Overlapping range.', 16, 1);
rollback transaction;
return
end;
end
Important: If impossible without identity is the only answer, I welcome you to present it along with a reasonable explanation why.
I know this is already answered, but I tackled this problem recently and came up with something that works (and performs well doing a singleton seek for each inserted row). See the example in this article:
http://michaeljswart.com/2011/06/enforcing-business-rules-vs-avoiding-triggers-which-is-better/
(and it doesn't make use of an identity column)
Two minor changes and everything should work just fine.
First, add a where clause to your trigger to exclude the duplicate records from the join. Then you won't be comparing the inserted records to themselves:
select *
from testdatetrigger t
join inserted i
on ((i.ValidTo >= t.ValidFrom) and (i.ValidFrom <= t.ValidTo))
Where not (i.ValidTo=t.Validto and i.ValidFrom=t.ValidFrom)
Except, this would allow for exact duplicate ranges, so you will have to add a unique constraint across the two columns. Actually, you may want a unique constraint on each column, since any two ranges that start (or finish) on the same day are by default overlapping.