C# - TimeStamp adattípusok összehasonlítása MS SQL Server-ben

forráskód letöltése
Ha megvizsgáljuk az MS SQL Server beépített TIMESTAMP adattípusát, arra a következtetésre juthatunk, hogy az ilyen típusú oszlopok tartalma nem igazán hasonlít arra az adatra, amire nevéből jogosan következtetni lehetne. Cikkünkben bemutatjuk, hogy tulajdonképpen mire is használható az adattípus, hol lehet jelentősége, és hogy milyen – az SQL Server Book Online dokumentációjában mindeddig nem említett – függvénnyel hasonlíthatjuk össze az ilyen adatokat.
A példához szükséges a TSDB adatbázis, melyet a mellékelt Run_script.cmd BATCH állomány lefuttatásával hozhatunk létre. Csak arra kell ügyelni, hogy a TSDB.sql parancsállomány a BATCH állománnyal azonos mappában legyen. A TSDB.sql parancsállomány 5. sorában adja meg helyesen a létrehozandó adatbázisfájlok mappájának nevét és elérési útvonalát.
TimeStamp adattípus
A TimeStamp adattípus egy különlegessége a MS SQL Server típushalmazának, mivel - nevével ellentétben nincs köze időbélyegekhez – elegendő deklarálnunk a tábla létrehozásakor, majd értékének generálása a rendszer dolga. A táblába közvetlenül nem is vihető be ilyen típusú adat, az INSERT parancs megadásakor ki kell hagynunk az oszlopot az értékadó listából, UPDATE parancs kiadásakor a rendszer hibaüzenetet ad.
Minden táblába kerülő rekord ilyen típusú oszlopában a rendszer generál egy egyedi, 8 bájtos, bináris adatot (például 0x00000000000000C4). A rekord módosításakor a rekord TimeStamp típusú oszlopába automatikusan új adat kerül, tehát használható annak megállapítására, hogy az adott rekord egy időpillanat óta megváltozott-e, vagy sem.
A csak-olvasható táblákban ennek nincs jelentősége, azonban olyan rendszerekben, ahol egy adott táblát egy adott időpillanatban több felhasználó is használhat és módosíthat, igen hasznos segítség. Egyszerűen meg kell határozni azt, hogy az új és a régi TimeStamp bináris adatok eltérnek-e, vagy nem.
Ennek módja az, hogy meg kell hívni az MS SQL Server beépített, azonban valamilyen ok miatt a dokumentációból kimaradt TSEQUAL függvényét, mely paraméterként TimeStamp típusú adatot vár. Szintaxisa:
TSEQUAL(<TimeStamp típusú adat>,< TimeStamp típusú adat >)
A függvény jellegzetessége, hogy egy IF utasításban lekérdezhetjük, van-e eltérés a két paraméterben megadott adat közt, és teljesen azonos adatok esetén TRUE értéket ad vissza. Azonban eltérő adatok esetén visszaad egy FALSE értéket, de mellé egy SqlException kivétel is keletkezik. A kivételt programból el kell kapni, ám előfordulása jelzi, hogy az adatok eltérnek, vagyis az adott rekord módosult az eredeti tartalomhoz képest.
UpdatedClient alkalmazás
A mellékelt alkalmazáscsomag két programot tartalmaz. Ezekkel modellezzük azt a helyzetet, amikor több felhasználó férhet hozzá egy táblához egy időben. Az UpdaterClient alkalmazásban egy DataGrid kontrolban folyamatosan látható az adatbázisunk Table1 táblájának tartalma.
A Table1 adattábla megadott rekordjának ProductName oszlopban található értéke módosítható a MÓDOSÍTÁS feliratú gombbal. Ehhez a ComboBox kontrolban ki kell választani egy ProductID azonosítót, valamint meg kell adni egy új nevet a TextBox kontrolban.
Az UPDATE művelet egy egyszerű SQL paranccsal végrehajtható.
Az adattábla ProductPropery-je TimeStamp típusú adat, melynek meg van az a jellegzetessége, hogy a deklaráción kívül nincs vele különösebb dolgunk, azonban a rendszer jótékony kezelése folytán mégis hasznos segítség lehet.
CREATE TABLE Table1(
  ProductID INT IDENTITY(1, 1) NOT NULL,
  ProductName VARCHAR(50) NOT NULL,
  ProductProperty TIMESTAMP,
  InsDate DateTime NOT NULL)
GO
ViewerClient alkalmazás
A másik kliens-alkalmazásunkban induláskor betöltjük egy DataTable objektumba az összes rekord TimeStamp oszlopának értékét, amely jelképezni fogja egy adott időpillanat eredeti adatállományát. A DataTable objektum TimeStamp oszlopát bájt-tömb (byte[]) típusúra kell választani, ellenkező esetben nem tudjuk kivenni az adatot a DataTable objektumból.
dt.Columns.Add(new DataColumn("ProductProperty",System.Type.GetType("System.Byte[]")));
Egy SqlDataReader objektum segítségével kiolvassuk a Table1 tábla értékeit:
d = sqlCommand1.ExecuteReader();
while (d.Read())
{
  DataRow dr = dt.NewRow();
  dr[0] = d.GetInt32(0);
  dr[1] = d.GetSqlBinary(1);
  dt.Rows.Add(dr);
} 
A LEKÉRDEZÉS gomb segítségével pedig folyamatosan megvizsgálhatjuk, hogy a rekordok TimeStamp típusú oszlopaiban található érték azonos-e a memóriabeli értékkel.
Ehhez írtunk egy tárolt eljárást, mely paraméterként megkapja az összehasonlítandó rekord azonosítóját, valamint a memóriabeli TimeStamp értéket. Az eljárásban kiolvastuk az adott érték táblabeli változatát, és ezzel az értékkel hasonlítjuk össze a kapott értéket.
CREATE PROCEDURE AreTimeStampsEqual (@id int, @ts timestamp, @result INT OUTPUT, @time datetime output)
AS
  ...   
  select @ts1 = ProductProperty, @time = InsDate from Table1 where ProductID = @id
  if TSEQUAL(@ts1,@ts)
    set @r = 0
  set @result = @r      
GO
A gomb kezelőfüggvényében egy Try-Catch blokkban helyeztük el a végrehajtó műveletet, hiszen eltérő értékek esetén kivétel generálódik. Azonos értékek esetén jelezzük a ListBox kontrolban, hogy a rekordok nem módosultak.
Kivétel esetén a hiba az adatok megváltozásáról árulkodik, így itt erről adunk tájékoztatást.
Amennyiben az SQL Server Query Analyser programjával futtatjuk a tárolt eljárásunkat, akkor megkapjuk a visszatérési értékeket minden esetben, azonban a végrehajtás után kapott kísérőüzenetben a következő sor jelenik meg, a rendszer beépített figyelőlogikájának eredményeként:
The timestamp (changed to <új TimeStamp érték>) shows that the row has been updated by another user.