A következő példát annak illusztrálására raktam össze (nem, ezt már nem loptam, mint a tegnapit :), hogy hogyan lehet a kliensen történt módosításokat, törléseket és beszúrásokat egy füst alatt betolni a szervernek.
Az elképzelt alkalmazás kb. úgy néz ki, mint amit megszoktunk disconnected datadatable-ök esetén. Mivel a datatable ma már nem menő, lehet üzleti ojjektumra is gondolni, ami az adatbázisból jött adatokat fieldekben vagy tömbökben tárolja.
Az adatok egy részhalmazát lehúzzuk a szerverről közönséges, szűrt select segítségével. A kliens ezeket a sorokat módosíthatja, beszúrhat újakat és törölheti is őket. A törlést esetünkben azzal szimulálom, hogy az egyik oszlop értékét !torlendo!-re állítom (itt persze egy olyan string kell, amit garantáltan nem vihet be Mancika). Ha az összes sort levinnénk a kliensre nem kellene ez utóbbi trükk, hisz egy outer join (meg a merge is) meg tudná mondani, mely sorok hiányoznak a kliens halmazából.
No, a kliens tehát összeállítja a módosított adathalmazt, amiben mindhárom művelet szerepel, majd betolja a szervernek. Ha van egy kis esze, a nem változott sorokat nem küldi be, de ezt is lekezeli a példa.
A beköldött adatokat az új MERGE utasítással aprítjuk be.
Kezdjünk neki! Előállítok egy kis táblácskát, ez szimbolizálja a szerver oldali táblánkat.
create schema MergeDemo go select EmployeeID, Title, VacationHours into MergeDemo.EmpServerSide from HumanResources.Employee
Kliens oldalon valahogyan letároljuk az adatokat és módosítjuk, a módosításokat pl. tábla típusú paraméteren keresztül küldhetjük be egyszerre. Ezt a paramétert jelképezi most a következő tábla:
create table MergeDemo.EmpClientSide ( EmployeeID int not null identity(0, -1), Title nvarchar(50) not null, VacationHours smallint not null )
Látható, hogy az identity 0-tól megy visszafelé, így a kliens oldalon az új sorokhoz generált id-k nem ütköznek majd a szerveroldaliakkal. Klasszikus ado.net hack. Rakjunk bele némi adatot, ez menne le a kliens apphoz:
set identity_insert MergeDemo.EmpClientSide on
insert into
MergeDemo.EmpClientSide
(
EmployeeID,
Title,
VacationHours
)
select
EmployeeID,
Title,
VacationHours
from
HumanResources.Employee
where
EmployeeID < 20
set identity_insert MergeDemo.EmpClientSide off
[/source]
Ha most megnézzük a táblát, ezt találjuk benne:
[source='c']
EmployeeID Title VacationHours
----------- -------------------------------------------------- -------------
1 Production Technician - WC60 21
2 Marketing Assistant 42
3 Engineering Manager 2
4 Senior Tool Designer 48
5 Tool Designer 9
6 Marketing Manager 40
7 Production Supervisor - WC60 82
8 Production Technician - WC10 83
9 Design Engineer 5
10 Production Technician - WC10 88
11 Design Engineer 6
12 Vice President of Engineering 1
13 Production Technician - WC10 84
14 Production Supervisor - WC50 79
15 Production Technician - WC10 85
16 Production Supervisor - WC60 80
17 Production Technician - WC10 86
18 Production Supervisor - WC60 81
19 Production Technician - WC10 87
[/source]
Most a kliens fogja az über gridjét, és belebabrál az adatokba. Mivel mi sql táblával szimuláljuk a kliens oldali konténert, sql műveletekkel dolgozok.
(Érdemes jól megnézni azt az insertet, aztán csapkodni a homlokunkat, oracle-ösök csöndben maradnak :).
[source='sql']
--Beszúrás szimuláció
insert into MergeDemo.EmpClientSide (Title, VacationHours)
values
('Hasvakaró', 0),
('Hátvakaró', 0)
--Módosítás szimuláció
update MergeDemo.EmpClientSide
set Title = Title + ' alma'
where EmployeeID > 15
–Törlés szimuláció
update MergeDemo.EmpClientSide
set Title = ‘!torlendo!’
where EmployeeID > 0 and EmployeeID < 4
[/source]
A kliens adatai ekkor így néznek ki:
[source='c']
EmployeeID Title VacationHours
----------- -------------------------------------------------- -------------
1 !torlendo! 21
2 !torlendo! 42
3 !torlendo! 2
4 Senior Tool Designer 48
5 Tool Designer 9
6 Marketing Manager 40
7 Production Supervisor - WC60 82
8 Production Technician - WC10 83
9 Design Engineer 5
10 Production Technician - WC10 88
11 Design Engineer 6
12 Vice President of Engineering 1
13 Production Technician - WC10 84
14 Production Supervisor - WC50 79
15 Production Technician - WC10 85
16 Production Supervisor - WC60 alma 80
17 Production Technician - WC10 alma 86
18 Production Supervisor - WC60 alma 81
19 Production Technician - WC10 alma 87
-1 Hasvakaró 0
-2 Hátvakaró 0
[/source]
Látható, hogy az első három tételt kell majd kiradírozni a szerveren, a 16-19 közöttiek módosultak és a két új sort hátul látjuk, kliens oldalon generált fake id-kkel.
No, most jön a főszereplő, a merge, ami minden módosítást bevisz egy szuszra:
[source='sql']
merge into MergeDemo.EmpServerSide s
using MergeDemo.EmpClientSide c
on s.EmployeeID = c.EmployeeID
when matched and c.Title = '!torlendo!' then
delete
when matched and c.Title != '!torlendo!'
and (c.Title != s.Title or
c.VacationHours != s.VacationHours) then
update set
s.Title = c.Title,
s.VacationHours = c.VacationHours
when target not matched then
insert (Title, VacationHours)
values (c.Title, c.VacationHours);
[/source]
Aki tud angolul, az majdhogynem szövegként olvashatja az utasítást.
Hogy lássuk mi történik a háttérben, az output utasítással (2005 újdonság volt) generáljuk egy táblát az elvégzett műveletekről:
[source='sql']
create table #Results
(
ClientEmployeeID int,
ClientTitle nvarchar(50),
ClientVacationHours smallint,
ServerEmployeeID int,
OriginalServerTitle nvarchar(50),
UpdatedOrInsertedServerTitle nvarchar(50),
ServerVacationHours smallint,
Action nvarchar(10)
)
go
merge into MergeDemo.EmpServerSide s
using MergeDemo.EmpClientSide c
on s.EmployeeID = c.EmployeeID
when matched and c.Title = '!torlendo!' then
delete
when matched and c.Title != '!torlendo!'
and (c.Title != s.Title or
c.VacationHours != s.VacationHours) then
update set
s.Title = c.Title,
s.VacationHours = c.VacationHours
when target not matched then
insert (Title, VacationHours)
values (c.Title, c.VacationHours)
output c.EmployeeID, c.Title, c.VacationHours,
coalesce(inserted.EmployeeID, deleted.EmployeeID),
deleted.Title, inserted.Title, inserted.VacationHours,
$ACTION into #Results
;
[/source]
A $ACTION a merge ajándéka, segít megmutatni, mit művelt a háttérben.
Lássuk hát (csúnya, széles, elnézést érte, view plain-nel, és megfelelően nagy felbontásnál tűrhető):
[source='c']
(9 row(s) affected)
ClientEmployeeID ClientTitle ClientVacationHours ServerEmployeeID OriginalServerTitle UpdatedOrInsertedServerTitle ServerVacationHours Action
---------------- -------------------------------------------------- ------------------- ---------------- -------------------------------------------------- -------------------------------------------------- ------------------- ----------
-1 Hasvakaró 0 291 NULL Hasvakaró 0 INSERT
-2 Hátvakaró 0 292 NULL Hátvakaró 0 INSERT
1 !torlendo! 21 1 Production Technician - WC60 NULL NULL DELETE
2 !torlendo! 42 2 Marketing Assistant NULL NULL DELETE
3 !torlendo! 2 3 Engineering Manager NULL NULL DELETE
16 Production Supervisor - WC60 alma 80 16 Production Supervisor - WC60 Production Supervisor - WC60 alma 80 UPDATE
17 Production Technician - WC10 alma 86 17 Production Technician - WC10 Production Technician - WC10 alma 86 UPDATE
18 Production Supervisor - WC60 alma 81 18 Production Supervisor - WC60 Production Supervisor - WC60 alma 81 UPDATE
19 Production Technician - WC10 alma 87 19 Production Technician - WC10 Production Technician - WC10 alma 87 UPDATE
[/source]
Szépen, a terveknek megfelően futottak le a parancsok, örülünk. :)
Could you hire me? Contact me if you like what I’ve done in this article and think I can create value for your company with my skills.