merge statement when not matched by source then insert to another table - sql

I have created two tables customersrc and customertemp with the columns:
customertemp
ID name age addr cityid isactive
34 Gi 24 Chennai 1 1
customersrc
CustomerId CustomerName CustomerAge CustomerAddress
1 Gi 24 madurai
2 Pa 23 Tirupur
3 MI 27 Tirupur
Now I need to insert pa and mi data value to the temp table bcz it is not matched with the rows of customertemp. And the row gi data will be updated which was matched.
I used the following MERGE statement
DECLARE #cityid INT SET #cityid=1
MERGE Temp.dbo.customersrc as src_customer
USING ( SELECT CustomerName,CustomerAge,CustomerAddress FROM customertemp) as temp_customer
ON src_customer.name=temp_customer.CustomerName
AND
src_customer.cityid=#cityid
WHEN MATCHED THEN
UPDATE SET
src_customer.age=temp_customer.CustomerAge,
src_customer.addr=temp_customer.CustomerAddress,
src_customer.isactive=1
WHEN NOT MATCHED BY SOURCE THEN
UPDATE SET src_customer.isactive=0 ; -- here i need the insert statement to insert in another table
Questions:
is it possible to write insert statement inside the when not matched by source query?
if it is not possible then how to achieve this using merge?
in a simple set theory I need to put the customersrc(table_B)-customertemp (table_A). B-A value into the another or temp table.

One of the main usages of the MERGE statement is to perform so called "UPSERTS" (Update matching records, insert new records), so it is definitely possible to do what you want. Just add the following to the last part of your MERGE statement:
WHEN NOT MATCHED BY TARGET THEN
INSERT (name, age, addr, cityid, isactive)
VALUES (CustomerName, CustomerAge, CustomerAddress, #cityid, 1)
If you also need to insert data into a 3rd table, depending on whether rows are updated or inserted, you can use the OUTPUT clause of the merge statement. Check out the documentation: http://technet.microsoft.com/en-us/library/ms177564.aspx

Me: Why do you want to insert to another table?
You: To show the user who are not in the customertemp table.
So your requirement is not to insert into another table. Your requirement is to get the missing users.
You could do that with a dummy UPDATE (SET SomeCol = SomeCol) and OUTPUT. But that is a hack that I would try to avoid.
It is probably easier to do this in two statements. Here's how you'd get the missing rows:
SELECT temp_customer.*
FROM (SELECT CustomerName,CustomerAge,CustomerAddress FROM customertemp) as temp_customer
LEFT JOIN customersrc ON src_customer.name=temp_customer.CustomerName AND src_customer.cityid=#cityid
WHERE customersrc.cityid IS NULL

Related

How to copy parent and child data from one database to another while assigning the newly created parent ids in the foreign key column of child tables?

Suppose,
I have a table tblClasses and tblStudents
Now each class have multiple student.
tblClass
ID Name
1 ClassA
2 ClassB
3 ClassC
tblStudents
ID Name Class_ID
1. john 1
2. Mathew 1
3. Imran 2
4. Jenny 3
now, I have another server having exact same db and tables and I am copying data from server 1 to server 2 from same tables using the Select and Insert e.g.
insert into server2.dbo.tblClass (Name)
select Name from server1.dbo.tblClass
and for tblStudents
insert into server2.dbo.tblStudents (Name, Class_ID)
select Name, Class_ID from server1.dbo.tblStudents
now this is ok but the real problem is that in server2 after copying the data, how to populate the tblStudents fk Class_ID with the actual IDs of tblClass which are generated after inserting the data into tblStudents in server2 since PKs are Identity and autoincremented and cannot change the design.
What to do in this case?
In simple words, when a parent and child data are copied then in the child table the foreign key field needs to be populated with the actual IDs of the parent not the one from where it is copied which would be obviously different.
I am not allowed to change the table design or properties and have to do it using the queries.
Any suggestions?
The way is to create a ClassId mapping table on class records insertion and use this mapping table to translate OldClassId to NewClassId for the new Student table:
declare #ClassIds table (OldClassId int, NewClassId int);
merge into newDB.dbo.tblClasses as target
using
(
select
Id = Id * (-1),
[Name]
from
oldDB.dbo.tblClasses
)
as source on source.Id = target.Id
when not matched by target then
insert ([Name])
values (source.[Name])
output source.Id * (-1), inserted.Id -- ← the trick is here
into #ClassIds (OldClassId, NewClassId);
insert into newDB.dbo.tblStudents
select
s.Id,
s.[Name],
ClassId = ids.NewClassId
from
oldDB.dbo.tblStudents s
inner join #ClassIds ids on ids.OldClassId = s.ClassId;
The major trick is that the MERGE statement may work with not not only inserted and deleted columns (as INSERT, UPDATE, DELETE statements do) but with the source columns as well.
There are two ways to go that I can think:
The first is to use SET IDENTITY_INSERT tblClass ON. This is not a design change, so you should be able to do it. After that, you can insert your own values in tblClass.ID (although you will need the select list parenthesis):
insert tblClass(ID,Name) values (1, 'ClassA')....
Alternatively, you could make a query that connects student ids to class names, and then use that back to create the respective connections:
-- export/save this in temp table
select s.Name,c.Name as className
into #a
from tblStudents s
left join tblClass c on s.Class_ID=c.ID
--now use this to fill db2 tblStudents
insert tblStudents(Name,Class_ID)
select #a.Name,c.ID
from
#a
inner join tblClass c on #a.className=c.Name

Suggestion to make a massive insert

I have the tables below:
tb_profile tb_mbx tb_profile_mbx tb_profile_cd
id id id_profile id_perfil
cod_mat mbx id_mbx id_cd (matches id_mbx)
concil bp
masc
I need to create a query that when validating that the id_cd 1,2,4,5
and 6 exists in tb_profile_cd, perform an insert in the
tb_profile_mbx table with the cod_matrix parameters of the tb_profile
table.
Remembering that each concil has its ID in the tb_mbx table and a
concil has many cod_mat.
Another point is that the concil id_mbx represents the id_cd of the
tb_profile_cd table.
One more point is that as I said above, that a concil has many
cod_mat. I have around 20 thousand records for each concil.
For my need, try to consult the query below, but Oracle returned an error:
insert into tb_profile_mbx values (seq_profile_mbx.nextval,
(select id from tb_profile where concil like '%NEXXERA%')
,(select id from tb_mbx where mbx like '%NEXXERA%')
,null
,null);
ORA-01427: single line subquery returns more than one line
Would there be another way to do this query?
Thanks in advance!
You can insert all matching combinations of matches using:
insert into tb_profile_mbx (id_profile, id_mbx)
select p.id, m.id
from tb_profile p join
tb_mbx m
on p.concil like '%NEXXERA%' and m.mbx like '%NEXXERA%';
I would recommend running the select to see if it returns the values you want.
You only show two columns for tb_profile_mbx, so I've only included two columns in the insert.

Insert columnA values of Table 1 into another table if match occur

I have two tables.Table A has 4 columns. And table B has two columns.I want to insert value of one column of tabel A from one column of table B based on condtion if id matches.
how i can do this ? For example if [Movieid] in 1st table =[IMDBid] in second table then insert [count] of table 1=[CB] in table 2.
i want to do it once for full table.
[column] -> these are colums
i m using sql server.
Tabel 1 : Movieid,count,
Tabel 2: IMDBid, CB
Results which i want: i want to insert values of CB column in count where Movieid=IMDBid
Tabel 1 :
(Nick Id,MovieId,Rating,MovId)-> (1,4972,6.25,?)(1,24216,7.25,?)
Tabel 2 :
(Imdbid,Title,ImdbPyId,Id)-> (4972,hello,32450,1)(24216,hi,62450,2)
Insert /fill value of MovId(tabel1) using values Id(tabel2)where MovieId(tabel1)==Imdbid(tabel2)
You can do it like,
UPDATE tabl1
SET count = (SELECT CB FROM tabl2 WHERE IMDBid = Movieid)
But it is gonna blow up if you have multiple values returning from the subquery.
So make sure to use the appropriate function to get the single value from that subquery whichever meets your requirements.
If your tables have 1:1 relationship then it should be fine. But if it is 1:n then you need to use either aggragate functions or the TOP 1 clause in that subqyery.
solution is UPDATE tabl1
SET count = (SELECT CB FROM tabl2 WHERE IMDBid = Movieid)

How to insert, update, delete when import data from table to table?

I have a query that I need to run more than once a day. This query is importing data from a database to another.
The target table structure is:
Id Date Department Location PersonId Starttime EndTime State
1 2012-01-01 2 5 200 12:00:00.000 15:00:00.000 2
An application can also insert data to the target table. The records that are inserted by the application may not be updated also when this record exists in the source(temp table) table with another state.
To make this possible I have an solution created. I will create an new column in the target table with a second state so that I can check.
Id Date Department Location PersonId Starttime EndTime State StateSource
1 2012-01-01 2 5 200 12:00:00.000 15:00:00.000 2 2
Some Requirements:
If a record is added by the application than StateSource will be NULL. Means that this record may not be deleted, updated or inserted again from the source table.
If a Record is updated by the application than the value State and StateSource will be different. In this case I do not update this record.
I will update if the state from the sourcetable and targettable are not same and the values from target table State = StateSource.
I will INSERT a record when this is not exists in the target table. When records already exists do not insert (no matter if this is added by the application or my query on the first run).
I will delete the records from the target when they are no more exists in my sourcetable and State=StateSource.
I already have the following queries. I have decided to make 3 statements.
--Delete Statement first
Delete from t
from TargetTable t LEFT JOIN SourceTable s ON t.Id=s.Id
and t.Date=s.Date
and t.departments=s.Department
and t.PersonId=s.PersonId
and t.State=t.StateSource
--Just delete if a date is no more exists from the source table and this records is NOT
--changed by the application (t.State=t.StateSource)
--Update statement second
Update t
set t.State = s.State
From Targettable t INNER JOIN SourceTable s ON t.Id=s.Id
and t.Date=s.Date
and t.departments=s.Department
and t.PersonId=s.PersonId
The problem here is:
--when I have State 2 already in the targettable and in my sourcetable i have
--another state then the state in the targettable changes. This would not be the case.
--Insert Statement thirth
insert into TargetTable (Id, Date, Department, Location, PersonId, Starttime, EndTime,State, StateSource)
select Id, Date, Department, Location, PersonId, Starttime, EndTime,State, StateSource
from SourceTable s
WHERE Date not in (select Date
from TargetTable t
where t.id=s.id
and t.PersonId=s.PersonId
and t.date=s.date
and t.department=s.department)
--I have no idea about how the insert should be because the application also can
--insert records. When a record exists then no insert. What to do with the State?
Remember that the states that are changed by the application are leading.
Can anyone help me with the desired result?
you may use a merge statement.. something like this...
with target_T as (select * from UR_TARGET_TABLE
where statesource is not null) -- to dont change the data inserted from application...
merge target_T as TARGET
using UR_SOURCE_TABLE as SOURCE
on SOURCE.id = TARGET.id -- id is unique? anyway, put your primary key here...
when matched and TARGET.state = TARGET.statesource then --if inserted/updated from application, will not change data
update set TARGET.state = SOURCE.state
,TARGET.statesource = SOURCE.state --important update it together to be different from an application update
--, other collumns that you have to set...
--should use another when matched then update if need to change something on inserted/updated from application data
when not matched by TARGET then
insert (Id, Date, Department, Location, PersonId, Starttime, EndTime,State, StateSource)
values(SOURCE.Id, SOURCE.Date, SOURCE.Department, SOURCE.Location, SOURCE.PersonId, SOURCE.Starttime, SOURCE.EndTime,SOURCE.State, SOURCE.StateSource);
if you set an sample with declaring your tables and inserting some data...
I should help more, with a code that really works.. not just a sample...

Oracle SQL: merge with more conditions than matched/not matched

I'm needing some help with a MERGE command in Oracle. Basically I have this command but I want to optimize it a little more:
MERGE INTO swap USING dual ON (SELECT id FROM student WHERE number = '123')
WHEN MATCHED THEN
UPDATE SET swapped = 1, last_swap = sysdate
WHEN NOT MATCHED THEN
INSERT (student_id, swapped, last_swap) VALUES ((SELECT id FROM student WHERE number= '123'), 1, sysdate)
Right now this will insert or update a register on SWAP table. However, I would like to protect it from inserting NULL on the student_id if there's no student with that number on STUDENT table (I don't want to simply not allow NULL values on sudent_id of SWAP table).
Other thing, I'm repeating SELECT id FROM student WHERE number = '123' two times, how can I change this to execute it only once (store the result in an alias or something)?
Thanks a lot in advance!
Your merge statement should use the "Select from student" as the table you are merging from.
Can you post some sample data as well?
The below query is assuming that ID is the column on which you are merging and it cannot be null.
MERGE INTO swap
USING (SELECT id FROM student WHERE number = '123' and id is not null) stu
on (stu.id = swap.id)
WHEN MATCHED THEN
UPDATE SET swapped = 1, last_swap = sysdate
WHEN NOT MATCHED THEN
INSERT (student_id, swapped, last_swap) VALUES (stu.id , 1, sysdate);