best way to delete and reinsert data to database - sql

so i have a catalog inside a sql server table, i need to be refreshing every 12 hours this catalog, what's the correct way into doing this?
i have been doing a simple delete and afterwards a bulkcopy of the updated data, but im afraid that another process is using this table and might break something.
Cheers,

You could use MERGE feature. It is available in MS SQL Server 2008+, and also add a try/catch - transaction handling. Try following example:
CREATE TABLE #Temp1
(
Id INT IDENTITY PRIMARY KEY
,Description NVARCHAR(20)
)
CREATE TABLE #Temp2
(
Id INT IDENTITY PRIMARY KEY
,Description NVARCHAR(20)
)
INSERT INTO #Temp1 VALUES ('Example1'), ('Example2'), ('');
INSERT INTO #Temp2 VALUES ('Example1'), ('Example4'), ('Example5');
--First values:
SELECT Id, Description FROM dbo.#Temp1;
SELECT Id, Description FROM dbo.#Temp2;
BEGIN TRY
BEGIN TRANSACTION;
--MERGE statement:
--#Temp1 will be our Target and #Temp2 our Source
MERGE dbo.#Temp1 AS target
USING (SELECT Id, Description FROM #Temp2) AS source
ON (target.Id = source.Id)
--When rows matched and description from Target will be '', then the delete will happen
WHEN MATCHED AND target.Description = ''
THEN DELETE
--When rows matched (by their Id, seen previously), then the update will happen:
WHEN MATCHED
THEN UPDATE SET target.Description = source.Description
OUTPUT $action;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
RAISERROR('<<Transaction uncommited. Rolling back transaction',10,1);
ROLLBACK TRANSACTION;
END CATCH
--Final values:
SELECT Id, Description FROM dbo.#Temp1;
SELECT Id, Description FROM dbo.#Temp2;
DROP TABLE #Temp1;
DROP TABLE #Temp2;

Related

For each inserted row create row in other table with foreign key constrain

I have 2 tables with foreign key constraint:
Table A:
[id] int identity(1, 1) PK,
[b_id] INT
and
Table B:
[id] int identity(1, 1) PK
where [b_id] refers to [id] column of Table B.
The task is:
On each insert into table A, and new record into table B and update [b_id].
Sql Server 2008 r2 is used.
Any help is appreciated.
Having misread this the first time, I am posting a totally different answer.
First if table B is the parent table, you insert into it first. Then you grab the id value and insert into table A.
It is best to do this is one transaction. Depending on what the other fields are, you can populate table A with a trigger from table B or you might need to write straight SQL code or a stored procedure to do the work.
It would be easier to describe what to do if you have a table schema for both tables. However, assuming table B only has one column and table A only has ID and B_id, this is the way the code could work (you would want to add explicit transactions for production code). The example is for a single record insert which would not happen from a trigger. Triggers should always handle multiple record inserts and it would have to be written differently then. But without knowing what the columns in the tables are it is hard to provide a good example of this.
create table #temp (id int identity)
create table #temp2 (Id int identity, b_id int)
declare #b_id int
insert into #temp default values
select #B_id = scope_identity()
insert into #temp2 (B_id)
values(#B_id)
select * from #temp2
Now the problem gets more complex if there are other columns, as you would have to provide values for them as well.
Without removing identity specification you can use the following option:
SET IDENTITY_INSERT B ON
Try this:
CREATE TRIGGER trgAfterInsert ON [dbo].[A]
FOR INSERT
AS
IF ##ROWCOUNT = 0 RETURN;
SET NOCOUNT ON;
SET IDENTITY_INSERT B ON
DECLARE #B_Id INT
SELECT #B_Id = ISNULL(MAX(Id), 0) FROM B;
WITH RES (ID, BIDS)
AS
(SELECT Id, #B_Id + ROW_NUMBER() OVER (ORDER BY Id) FROM INSERTED)
UPDATE A SET [b_Id] = BIDS
FROM A
INNER JOIN RES ON A.ID = RES.ID
INSERT INTO B (Id)
SELECT #B_Id + ROW_NUMBER() OVER (ORDER BY Id) FROM INSERTED
SET IDENTITY_INSERT B OFF
GO
Though Nadeem's answer is on the right track, his trigger for some reason takes max.id instead of NEW.id and doesn't update A accordingly.
For what you ask to be usable by trigger, you need the FK in table A to be nulleable, else you have a race condition between the tables.
EDIT: As SpectralGhost pointed out, my original code didn't support multiple rows, this one will do:
CREATE TRIGGER trgAfterInsertA ON TableA
FOR INSERT
AS
DECLARE db_cursor CURSOR FOR
SELECT id FROM INSERTED
DECLARE #an_id int
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #an_id
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO TableB VALUES(VALUE_PLACEHOLDER)
UPDATE TableA
SET b_id = SCOPE_IDENTITY()
WHERE id = #an_id
FETCH NEXT FROM db_cursor INTO #an_id
END
GO
The VALUE_PLACEHOLDER are the values you initialize TableB with.

Adding multiple values to a Table in SQL using a trigger and audit table

The issue I am having is when I insert more than one value into a table or delete a value that exists more than once in a table. I am unsure how to work around this issue.
`CREATE TRIGGER [dbo].[Q5Trigger]
ON [dbo].[WF]
AFTER INSERT, DELETE
AS
IF NOT EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'AuditTable')
BEGIN
CREATE TABLE [dbo].[AuditTable](
Word VARCHAR(100),
Frequency INT,
Date DATETIME,
Type VARCHAR(100)
)
END
IF EXISTS (SELECT * FROM inserted)
BEGIN
INSERT INTO AuditTable VALUES((SELECT Word FROM inserted),(SELECT Frequency FROM inserted), CURRENT_TIMESTAMP, 'Inserted')
END
IF EXISTS (SELECT * FROM deleted)
BEGIN
INSERT INTO AuditTable VALUES((SELECT Word FROM deleted),(SELECT Frequency FROM deleted), CURRENT_TIMESTAMP, 'Deleted')
END`
You misunderstand how triggers and INSERTED and DELETED work. When you insert 50 records into a table with a trigger, the trigger gets called once and the INSERTED table has 50 records in it. To do what you are doing, you must insert into the 1 table ALL the records of the other table.
INSERT INTO AuditTable (Word,Frequency,LogWhen,LogType)
SELECT Word,Frequency, CURRENT_TIMESTAMP, 'Inserted' FROM inserted
Your delete would be very similar to this.

sql server - trigger to copy rows from 2 tables

I have a trigger that is called when there's an Update, Insert or Delete on a table that is joined to another table by a PK/FK 1 to 1 relationship.
Currently I'm copying rowX from TableA when a U, I or D occurs. I want it to also copy rowX from TableB at the same time.
How to I do this?
USE [Database]
GO
/****** Object: Trigger [dbo].[archiveTable] Script Date: 14/01/2014 3:48:08 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[archiveTable] ON [dbo].[TableA]
AFTER INSERT,UPDATE,DELETE
AS
DECLARE #HistoryType char(1) --"I"=insert, "U"=update, "D"=delete
DECLARE #Id INT
SET #HistoryType=NULL
SET #Id=NULL
IF EXISTS (SELECT * FROM INSERTED)
BEGIN
IF EXISTS (SELECT * FROM DELETED)
BEGIN
--UPDATE
SET #HistoryType='U'
END
ELSE
BEGIN
--INSERT
SET #HistoryType='I'
END
--handle insert or update data
INSERT INTO [database2].[dbo].[tableA]
(column1, column2, ...)
SET #Id=INSERTED.column5
SELECT
GETDATE(), #HistoryType,
column1, column2, ...
FROM INSERTED
JOIN tableB ON INSERTED.column5 = table5.column1
INSERT INTO [database2].[dbo].[tableB]
(column1, column2, column3, ...)
SELECT
GETDATE(), #HistoryType,
column1, column2, column3, ....
FROM jobdtl WHERE column1 = INSERTED.column5
END
ELSE IF EXISTS(SELECT * FROM DELETED)
BEGIN
--DELETE
SET #HistoryType='D'
same as above except for delete
END
I'm guessing I need an inner join somewhere or do I need a variable to get the #job_id so it knows to copy the relevant info from the second table?
edit - from the looks of it I need to somehow use SCOPE_IDENTITY() however it's not taking the job_id of the transaction it's taking the actual ID of the transaction (ie. 1, 2, 3, etc. whereas I need it to be dynamic as job_id may be 54, 634, 325, etc.)
To run multiple statements in a trigger, enclose the statements in a BEGIN END block.
You may define multiple triggers for a particular event.
To refer to inserted/updated data, use "inserted" pseudo table. For deleted rows, use "deleted" pseudo table.
It's not clear from your question whether you want to insert into TableA/TableB or if these are the tables for which the triggers are defined. (The SELECT statement lacks a from clause)
I think, you can use the UNION clause to insert two rows from different table at one time.
I have made a demo query you just need to change it to your query.
create table #a
(
id int,
name varchar(10)
)
insert into #a
select 1,'ax'
union
select 1,'bx'
select * from #a
drop table #a
This is just the sample query with the logic, and i hope you understand it.

inserting into A errors because of a foreign key contraint issue

Can someone help explain this to me and resolve it?
http://sqlfiddle.com/#!6/2adc7/9
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_tblMobileForms_tblForms". The conflict occurred in database "db_6_2adc7", table "dbo.tblForms", column 'fm_id'.: insert into tblMobileForms(fm_name) values ('lol')
My schema has the ID from tblMobileForms be a foreign key to tblForms.fm_id
To do what you are trying to do you cannot set up the FK on tblMobileForms as an identity. See my fiddle below for more information.
http://sqlfiddle.com/#!6/be6f7/2
Alternatively what you could do is to have tblMobileForms have it's own separate surrogate key and have a different FK column to the tblForms table.
The PK on the tblMobileForms table has the same name as the FK on the same table. Seeing the PK is an IDENTITY column, you can end up with non-matching values.
In my fiddle, the tblForms table contained IDs in the upper 60s. Running the INSERT in the child table would add a record with id 1, which does not exist in the parent table.
I'd create a new row in the tblMobileForms table, and reference that to the parent table.
You could use an INSTEAD OF trigger to apply a random ID to each mobile form as it is inserted:
CREATE TRIGGER dbo.tblMobileForms_Insert
ON dbo.tblMobileForms
INSTEAD OF INSERT
AS
BEGIN
DECLARE #Inserted TABLE (fm_ID INT, fm_html_file VARBINARY(MAX), fm_name NVARCHAR(50));
INSERT #Inserted (fm_ID, fm_html_File, fm_Name)
SELECT fm_ID, fm_html_File, fm_Name
FROM inserted;
IF EXISTS (SELECT 1 FROM #Inserted WHERE fm_ID IS NULL)
BEGIN
WITH NewRows AS
( SELECT fm_ID, fm_html_File, fm_Name, RowNumber = ROW_NUMBER() OVER (ORDER BY fm_name)
FROM #Inserted
WHERE fm_ID IS NULL
), AvailableIDs AS
( SELECT fm_ID, RowNumber = ROW_NUMBER() OVER (ORDER BY fm_ID)
FROM tblForms f
WHERE NOT EXISTS
( SELECT 1
FROM tblMobileForms m
WHERE f.Fm_ID = m.fm_ID
)
AND NOT EXISTS
( SELECT 1
FROM inserted i
WHERE f.fm_ID = i.fm_ID
)
)
UPDATE NewRows
SET fm_ID = a.fm_ID
FROM NewRows n
INNER JOIN AvailableIDs a
ON a.RowNumber = n.RowNumber
IF EXISTS (SELECT 1 FROM #Inserted WHERE fm_ID IS NULL)
BEGIN
RAISERROR ('Not enough free Form IDs to allocate an ID to the inserted rows', 16, 1);
RETURN;
END
END
INSERT dbo.tblMobileForms (fm_ID, fm_html_File, fm_Name)
SELECT fm_ID, fm_html_file, fm_name
FROM #Inserted
END
When each row is inserted the trigger will check for the next available ID in tblForms and apply it sequentially to the inserted rows where fm_id is not specified. If there are no free ID's in tblForms then the trigger will throw an error so a 1 to 1 relationship is maintained (The error would be thrown anyway since tblMobileForms.fm_id is also a PK).
N.b. this requires tblForms.fm_ID to just be an int column, and not identity.

issue with trigger in ms sql server?

IF EXISTS (SELECT name FROM sysobjects WHERE name = 'myTrigger' AND type = 'TR')
BEGIN
DROP TRIGGER myTrigger
END
GO
go
create trigger myTrigger
on mytable_backup
instead of insert
as
begin
declare #seq int
select #seq = seq from inserted
if exists (select * from mytable_backup where seq= #seq) begin
delete from mytable_backup where seq=#seq
end
insert into mytable_backup
select * from inserted
end
go
I've written this trigger to check while inserting if seq column is repeated then update the previous row with same seq if seq doesn't exits insert it with new seq.
In ssis package I'm using OLEDB table(Mytable) as a source which contains.
Name,Age,Seq
Gauraw,30,1
Gauraw,31,1
Kiran,28,3
Kiran,29,3
kiran,28,3
Venkatesh,,4
Venkatesh,28,4
Now I'm loading this table to OLEDB destination(Mytable_backup) as destination.
I suppose to get output as.
Gauraw,31,1
kiran,28,3
Venkatesh,28,4
But I'm getting all the records from Mytable into Mytable_backup.
is anything wrong with my trigger?
I think that this trigger will just take the first row and compare it with the existing. If I understand what you want to do you can quit easy do this:
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'myTrigger' AND type = 'TR')
BEGIN
DROP TRIGGER myTrigger
END
GO
go
create trigger myTrigger
on mytable_backup
instead of insert
as
begin
insert into mytable_backup
select
*
from
inserted
WHERE NOT EXISTS
(
SELECT
NULL
FROM
mytable_backup AS mytable
WHERE
inserted.seq=mytable.seq
)
end
go
EDIT
So I found out what was going on. If you insert all of the rows in one go the inserted contains all the rows.. Sorry my mistake. If there are duplicates in your data your example do not show which to choose. I have chosen the one with the maximum of age (don't know what your requirements is). Here is a update with the full example
Table structure
CREATE TABLE mytable_backup
(
Name VARCHAR(100),
Age INT,
Seq INT
)
GO
Trigger
create trigger myTrigger
on mytable_backup
instead of insert
as
begin
;WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER(PARTITION BY inserted.Seq ORDER BY Age) AS RowNbr,
inserted.*
FROM
inserted
WHERE NOT EXISTS
(
SELECT
NULL
FROM
mytable_backup
WHERE
mytable_backup.Seq=inserted.Seq
)
)
insert into mytable_backup(Age,Name,Seq)
SELECT
CTE.Age,
CTE.Name,
cte.Seq
FROM
CTE
WHERE
CTE.RowNbr=1
end
GO
Insert of test data
INSERT INTO mytable_backup
VALUES
('Gauraw',30,1),
('Gauraw',31,1),
('Kiran',28,3),
('Kiran',29,3),
('kiran',28,3),
('Venkatesh',20,4),
('Venkatesh',28,4)
SELECT * FROM mytable_backup
Drop of the database objects
DROP TRIGGER myTrigger
DROP TABLE mytable_backup
Your original code has two flaws:
It assumes that only one record is inserted at a time.
Your insert into mytable_backup happens outside of the if condition. That insert will execute every time.