Archive for December, 2007

Ingyenes LINQ könyv

Saturday, December 22nd, 2007

Aki ezek után azt mondja, hogy nem volt honnan tanulnia, nem hiszek neki. :)

Van itt még Ajax meg SilverLight könyv is.

Békés, boldog Karácsonyt kívánok!

Friday, December 21st, 2007

Minden kedves olvasómnak.

Az SQL Serveres dolgokkal és a világ nagy kérdéseivel a két ünnep között jelentkezek újra.

SQL Server 2008 újdonságok 9. – Változó inicializálás deklarációkor

Wednesday, December 19th, 2007

Ma rövid lesz.

Ahogy VB6-ról 7-re újításkor lehetett használni a Dim a as Integér = 5 ‘; deklaráló és értékedó kifejezést egy sorban, úgy SQL Server 2008-ban is megy a declare @a int = 5.

Kb. csak annyira késett ezzel meg a TSQL, mint annak idején a VB. 10 évet.

Sorok adatainak összefűzése SQL Serveren

Tuesday, December 18th, 2007

Azaz, hogyan gyártsuk pl. vesszővel elválasztott listát sok sor adatából?
Én biztos nem csinálnék ilyet szerver oldalon, de aki szeret hekkelni, nézze meg a megoldásokat, ha másra nem, hát tudattágításnak megfelelő (habár speciel erre vannak jobb módszerek is :).

SQL Server 2008 újdonságok 8. – MERGE utasítás 2.

Tuesday, December 18th, 2007

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. :)

Miért nem látja a Windowsom mind a 4 Giga memóriát?

Monday, December 17th, 2007

Ezért.

SQL Server 2008 újdonságok 7. – MERGE utasítás

Monday, December 17th, 2007

Hányszor ír le az ember ehhez hasonló sql blokkokat?

if exists(select * from Tabla where oszl = @ize))
update Tabla … where oszl = @ize
else
insert Tabla (…) values (@ize, …)

Az egy dolog, hogy ilyenkor kétszer kell nekimenni a táblának, ami teljesítményveszteséget okoz, de még arra is oda kell figyelni, hogy attól, hogy az if azt monda, nem létezik a sor, az insert idejében már lehet, hogy egy párhuzamos folyamat beszúrta azt, pofára ejtve az insertet. Megfelelő izolációs szinttel persze ez kivédhető, de akkor meg a konkurencia csökken. A fő gond az, hogy nem volt olyan utasítás, ami ha már megnézte, hogy létezik-e a sor, azon nyomban valamilyen műveletet végez rajta (update vagy delete), vagy betömi a lukat (insert). Jöhet a MERGE.

A merge lényege, hogy van egy forrás adathalmazunk és egy cél táblánk. A kettő között join jellegű kapcsolatot adunk meg, amely segítségével pároztatjuk a két oldal sorait. Miközben soronként megy a pároztatás annak sikerétől vagy sikertelenségétől függően különböző DML műveleteket hajthatunk végre.

A következő példát arcátlanul elloptam a BOL-ból:

MERGE Production.ProductInventory AS pi
USING 
(SELECT ProductID, SUM(OrderQty) 
FROM Sales.SalesOrderDetail sod
JOIN Sales.SalesOrderHeader soh
ON sod.SalesOrderID = soh.SalesOrderID
AND soh.OrderDate = GETDATE()
GROUP BY ProductID) 
AS src (ProductID, OrderQty)
ON (pi.ProductID = src.ProductID)
WHEN MATCHED AND pi.Quantity - src.OrderQty <> 0 
    THEN UPDATE SET pi.Quantity = pi.Quantity - src.OrderQty
WHEN MATCHED AND pi.Quantity - src.OrderQty = 0 
    THEN DELETE;

Ez leválogatja a ma eladott termékeket, az id-jüket és az ezen a napon eladott össz mennyiséget. Ezen adatok alapján babrán a raktárkészleten, az ProductInventory táblán.
Ha a ProductID-nek megfelelő sor van a ProductInventory-ban és levonva a ma eladott cuccokat még marad raktáron, akkor update-eljük a raktáron levő termékszámot. Ha pont annyit adtak ma el, amennyi raktáron volt, akkor kitörlik a raktáros sort, minek egy 0-s sor (persze, ez most egy mesterséges feladat, nem feltétlen törölnénk ki élőben).

Lehetne még írni műveleteket azokra az estekre is, ha valamelyik oldanak nincs párja a másik oldalon. Majd, ha aludtam egyet, írok ilyen példát. :)

Új képek a tündérbogarakról

Saturday, December 15th, 2007

Sokáig nem raktam ki új képeket, mert Pannikámról sose sikerült jó képet készíteni ruhában (csak Benedekkámról, a’la Bálint :), de most végre van valami.

Kezdjük a nagytesóval, Bálinttal, amint a játékkatalógust böngészi:

Bálint ölében Panni:

Benedek 2 hónapos, megy ki levegőzni:

Panni 3 hónaposan szintén indul az udvarba:

Megfújtuk (vagy megittuk? :) az angyaltrombitát:

Mulat a csapat:

Kettesben pihengetünk:

Panni 2 hónaposan vigyorog:

Jó éjszakát (itt még kicsik voltunk):

IIS7 feljlesztői újdonságok a channel 9.hu-n

Saturday, December 15th, 2007

A mesélő ismerős lesz. :)

Nekem most meg se mukkant a video, de letölteni le lehetett volna. De mivel nem szeretem visszanézni magam filmeken, nem tettem. :)

A gárdisták sosem hagynak cserben

Friday, December 14th, 2007

Index cikk.

Írtam egy hosszabb értekezést ezzel kapcsolatban, de úgy döntöttem, jobb lesz, ha nem postolom ki. :)

Csak egy dolog jutott az eszembe. Nemrég temettük Nagymamámat, Isten nyugosztalja. Egy kis faluban élt, Endrőd a neve (Gyomaendrőd része). A házát nagyon súlyos betegsége miatt már 2 éve eladták a szüleim, így nem volt gond a temetésen azzal, amivel ott minden ember szembesül.

Ha temetés van, akkor mindig otthon kell maradni egy embernek a házban, mert az erre szakosodott emberek(?) megnézik, aznap kit, mikor temetnek, és betörnek a házba!

Természetesen a helyi német kisebbség emberei. Ennél undorítóbb, aljsabb dolgot, hogy a embert még a gyászában is megbecstelenítik, nos, ez és még sok hasonló eset az, ami sok embernél kiveri a biztosítékot, és amely élteti a Jobbikot, a Gárdát és a kuruc.infót. Ha teszik, ha nem, ez csak reakció, nem akció, válasz a gyalázatra.

SQL Server interjú kérdések

Friday, December 14th, 2007

Egyenes, jó kérdések. Jó az első rész is.

SQL Server 2008 újdonságok 6. – záró gondolatok a streaming adatokról

Friday, December 14th, 2007

Láttuk az előző részben, hogy sima fájlműveletekkel tölthetjük fel a streaming adatainkat a szerverre, nem sima INSERT-tel. Ebből adódik az a kérdés, mikor ér véget a “sor” beszúrása, mikor indulhat el a streaming oszlopot tartalmazó táblára írt INSERT trigger? Nos, akkor, amikor lezárjuk a file handle-jét.

A fájlok írását a többi adattal egy tranzakcióba lehet szervezni, láttuk az előző rész példájában.

Ahogy egy relációs adatbázistól megszoktuk, ha egyszer azt mondta a COMMIT-ra, hogy rendben, akkor az adatok tényleg tartósan a diszken lesznek, és nem valahol az OS cache-ében. Még szép, ezt is várjuk tőle.

Az izolációs szintek közül csak a READ COMMITTED, azaz az alapértelmezett szint megy fájl alapú műveletek esetén, nem akartak belemenni trükkös zárolásokba, amelyeket fájlokat kellene implementálniuk. Ha valami fájt fog egy másik tranzakció, jön a DOS-os időkből is már ismert sering viola (ERROR_SHARING_VIOLATION).

Ha távolról, másik gépről írjuk a file-t, akkor nincs bufferelés a háttérben, ezét tartózkodjunk a gagyi, bájtonkénti írástól, mert piszok lassú lesz. De erről már 1960 óta lehet olvasni, nem újdonság.

A full-text index megy a filestream adatokra. Be lehet indexelni az univerzumot.

Hogyan néznek ki az adatok a fájlrendszerben? Nyilván ez implementációs részlet, de kukucskáljunk bele:

Directory of C:\test\FileStreamData\96945631-bd32-463a-bc66-aa3ced7b7ed9\
7ec9d1c3-6db1-4cd0-9d90-57f1a70c10e6

12/12/2007 12:14 AM 708,243 00000013-00000010-0040
12/12/2007 12:15 AM 696,575 00000013-00000010-0053
12/12/2007 12:15 AM 691,741 00000013-00000010-0066

Szép, valami belső id alapján nevezik el a fájlokat, ennek megfejtését későbbre hagyom, főleg, hogy ez még sokat változhat a végleges kiadásig.

Egyetlen kérdésre nem találtam még választ, hogy kihasználja-e a szerver, hogy a Windows 2008-ban tranzakcionális az NTFS, kifelé is? Érzésem szerint nem, mert akkor vagy nem menne a szerver Windows 2003-on (menni fog), vagy kétféleképpen meg kellene írni a támogatást a különböző oprendszerekre. Kétlem, hogy ekkor költséget a nyakukba vettek volna.

Az előbbi linkből egyébként látszik, hogy éles környezetben, 32 biten már NEM lesz SQL Server 2008. Így vegyen valaki 32 bites szervert. Nekem most Vistán megy, de azért, mert a developer elmegy 32 biten is, de csakis ez.

SQL fejtörő

Thursday, December 13th, 2007

Ravasz.

Mit ír ki?

DECLARE @i int

SELECT @i =' 123456789 '

SELECT @i, LEN(@i), DATALENGTH(@i)

Tapasztaltok egy 35ezer tranzakció/mp-es rendszer implementációjából

Thursday, December 13th, 2007

Na, nem én csináltam, sajnos nálunk nem nagyon van ilyen, bár, ha tudtok róla, jelezzétek. Mobilosoknál talán van valami hasonló terheltségű db, de az Oracle.

Szóval, nagyon-nagyon jó post a témában, pár meglepő fordulattal, pl.

Néha nem a legszebb megoldás a legjobb:
“When developing the upsert proc prior to tuning the indexes, I first trusted that the If Exists(Select…) line would fire for any item and would prohibit duplicates. Nada. In a short time there were thousands of duplicates because the same item would hit the upsert at the same millisecond and both transactions would see a not exists and perform the insert. After much testing the solution was to use the unique index, catch the error, and retry allowing the transaction to see the row and perform an update instead an insert.”

A CLR implementáció bukása, talán az interop költségek miatt?

“This app requires that several hundred “items” are passed into the stored procedure for processing as a set. I chose to input the data as comma delimited VarChar(max), originally using Erland’s iterative string2set function to parse string into a set inside the proc. It worked well enough. The plan was to substitute a CLR function near the end of development. When the time came, the CLR function ran blazingly fast on my notebook, but under stress it was painfully sloooooow. I didn’t understand at all why. This is exactly what we were told by Microsoft the CLR was for. Pass string data in, manipulate it, pass it back – no data access – sounds great right? In a few emails to my friend Adam, he told me he had the same issues a while back and the CLR simply could keep up with the marshalling of the VarChar(max).”

Stb. Nagyon jó cikk.

SQL Server 2008 újdonságok 5. – streaming adatok kezelése kliensoldalról

Thursday, December 13th, 2007

Az előző részben elkészült a táblánk, ami streaming adatokat tud tárolni. Itt az ideje pár fotót beleszórni az adatbázisba (illetve a fájlrendszerbe).

Az adatokat beküldhetjük SQL parancsként is, a megszokott TDS csatornát felhasználva. Azonban pont azért rakták ki filerendszerbe ezeket a nagy adatokat, hogy NE TDS-en keresztül, hanem SMB-vel, a windows file megosztásán keresztül érjük el őket. Ez kicsit szokatlan lesz, hisz lesz ugyan SQL INSERT, de lesz sima fájlkezelés is a megoldásban. Mondom, menne sima SQL-lel is, de az nem lenne túl hatékony.

No, a megoldás elég bizarr lesz. Nincs ugyanis managed felület az adatok kezelésére! Legalábbis most, 2007 végén nincs. Natív API van, azt lehet rugdosni .NET-ből, interopon keresztül, ha kell.

A megoldás menete vázlatosan a következő.
1. Hozzákapcsolódunk a szerverhez SqlConnectionnel, tranzakciót indítunk, hozzárendeljük a használandó SqlCommandunkhoz. Eddig semmi új.

2. Beszúrjuk a stream metaadatait, a sima adatokat, egy insert-tel, hagyományos módon, ADO.NET-tel.

insert into Kepek (Id, Name, Photo) values (@Id, @Name, cast ('' as varbinary(max)))

A furcsa üres stringet kasztoló izé azért van a parancsban, hogy megágyazzunk az adatoknak a filerendszerben. E nélkül, ha null maradna az oszlop értéke nem jönne létre fájl a diszken, így a következő lépés se menne.

3. Visszaolvassuk a sorunkhoz tartozó stream mint fájl elérési útját és egy tranzakció azonosítót, ez kell majd a következő lépésben. Mindkettőre van egy új függvény illetve metódus (kiemelve).

select Photo.<strong>PathName()</strong> PathName, <strong>get_filestream_transaction_context()</strong> TranCtx from Kepek where Id = @Id

4. Most jön a bizarrabb rész. Kapunk egy natív függvényt, azzal lehet megnyitni trazakcionálisan a streamünket mint fájlt: OpenSqlFilestream. Mivel ő natív cucc, kell hozzá interop deklaráció, kb. így:

[DllImport(“sqlncli10.dll”, SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle OpenSqlFilestream(
string FilestreamPath,
UInt32 DesiredAccess,
UInt32 OpenOptions,
byte[] FilestreamTransactionContext,
UInt32 FilestreamTransactionContextLength,
LARGE_INTEGER_SQL AllocationSize
);

[StructLayout(LayoutKind.Sequential)]
public struct LARGE_INTEGER_SQL
{
public Int64 QuadPart;
public LARGE_INTEGER_SQL(Int64 quadPart) { QuadPart = quadPart; }
}

Ronda, de az interop már csak ilyen. Örüljünk, hogy működik. Vagy tessék használni C++-t.

A paraméterek értékét az előző pont select-je szolgáltatja, amit egy reader-rel érek el:

byte[] tranCtx = (byte[])reader[“TranCtx”];

SafeFileHandle h = OpenSqlFilestream(
(string)reader[“PathName”],
DESIRED_ACCESS_WRITE,
0,
tranCtx,
(uint)tranCtx.Length,
new LARGE_INTEGER_SQL(0))

Kapunk egy szép Handle-t, amit az interop réteg egyből be is csomagol egy SafeFileHandle-be, mert azt úgy illik (aki nem hallott a safehandle-ökről, most álljon meg, és sürgősen olvasson utána, nem hosszú a cikk).

5. A Win32 handle jó dolog, de nem kezdünk neki WriteFile API-val baromkodni, hanem kihasználjuk, hogy a FileStream könnyen összebarátkozik valahonnan szerzett File Handle-ökkel:

FileStream fsWrite = new FileStream(h, FileAccess.Write)

6. Most már csak írni kell bőszen az fsWrite-ba. Figyeljük meg, hogy ez a lényege pont a streaming elérésnek, hogy nem összerakunk egy 34 GByte-os ojjektumot, mondjuk byte[]-öt, és odavágjuk a szervenek (eleve, a kliens belehalna ebbe), hanem apránként lapátoljuk be az adatokat. Ráérősen, öregesen. Valahogy így pl.

int readed;
byte[] buff = new byte[4096];
do
{
readed = fsRead.Read(buff, 0, buff.Length);
fsWrite.Write(buff, 0, readed);
}
while (readed > 0);

Az fsRead a helyi képre van megnyitva, amit betolunk a szervernek. Teccik látni, kicsi kis darabkákban megy be a hatalmas kép.

Számos kérdés, mint izolációs szint, mi látszik ebből a fájlrendszerben, kihasználja-e a tranzakcionális NTFS-t, stb. merül még fel, ezekkel a következő részben foglalkozok.

Zárásul berakom a teljes kódot, élvezzétek. :)

(Tud valaki valami normális, kész, szép wordpress theme-et, ami úgy van belőve, hogy olyan szélesen formázza meg a szöveget, mint amekkora az ablak mérete? Elkezdtem átszabni ezt a Kubrick theme-et, de ez halál, nekem bonyolult megérteni és átírni. A kódok miatt kellene.)

using System;
using System.Data.SqlClient;
using System.IO;
using System.Data;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace FSTest
{
class Program
{
static void Main(string[] args)
{
SqlTransaction tran = null;
try
{
using (SqlConnection conn = new SqlConnection(
@”Data Source=.\sql2008;Initial Catalog=FSTeszt;Integrated Security=true;”))
{
using (SqlCommand cmd = new SqlCommand())
{
conn.Open();
tran = conn.BeginTransaction();
cmd.Connection = conn;
cmd.Transaction = tran;

foreach (string f in Directory.GetFiles(@”E:\ment\kepek\Hivatni”, “*.jpg”))
{
//Kép alapadatok beszúrása
cmd.CommandText = @”insert into Kepek (Id, Name, Photo)
values (@Id, @Name, CAST (” as varbinary(max)))”;
cmd.Parameters.Clear();
cmd.Parameters.Add(“@Name”, SqlDbType.NVarChar).Value = Path.GetFileName(f);
Guid id = Guid.NewGuid();
cmd.Parameters.Add(“@Id”, SqlDbType.UniqueIdentifier).Value = id;
cmd.ExecuteNonQuery();

//Maga a kép mentése mint streaming adat
cmd.CommandText = @”select Photo.PathName() PathName,
get_filestream_transaction_context() TranCtx from Kepek where Id = @Id”;
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow))
{
if (reader.Read())
{
byte[] tranCtx = (byte[])reader[“TranCtx”];

using (SafeFileHandle h = OpenSqlFilestream(
(string)reader[“PathName”],
DESIRED_ACCESS_WRITE,
0,
tranCtx,
(uint)tranCtx.Length,
new LARGE_INTEGER_SQL(0)))
{

if (!h.IsInvalid)
{
using (FileStream fsWrite = new FileStream(h, FileAccess.Write))
using (FileStream fsRead = File.OpenRead(f))
{
int readed;
byte[] buff = new byte[4096];
do
{
readed = fsRead.Read(buff, 0, buff.Length);
fsWrite.Write(buff, 0, readed);
}
while (readed > 0);
}
}
else
{
int errno = Marshal.GetLastWin32Error();
Console.WriteLine(errno);
Environment.Exit(-1);
}
}
}
}

}
}
tran.Commit();
}
}
catch (Exception e)
{
tran.Rollback();
Console.WriteLine(e);
}
}

public const UInt32 DESIRED_ACCESS_READ = 0x00000000;
public const UInt32 DESIRED_ACCESS_WRITE = 0x00000001;
public const UInt32 DESIRED_ACCESS_READWRITE = 0x00000002;

[DllImport(“sqlncli10.dll”, SetLastError = true, CharSet = CharSet.Unicode)]
public static extern SafeFileHandle OpenSqlFilestream(
string FilestreamPath,
UInt32 DesiredAccess,
UInt32 OpenOptions,
byte[] FilestreamTransactionContext,
UInt32 FilestreamTransactionContextLength,
LARGE_INTEGER_SQL AllocationSize
);

[StructLayout(LayoutKind.Sequential)]
public struct LARGE_INTEGER_SQL
{
public Int64 QuadPart;
public LARGE_INTEGER_SQL(Int64 quadPart) { QuadPart = quadPart; }
}

[DllImport(“kernel32.dll”)]
private extern static void CloseHandle(IntPtr handle);
}
}

SQL Server 2008 újdonságok 4. – streaming adatok tárolása

Wednesday, December 12th, 2007

No, ez alaposan más, mint amit eddig megszoktunk egy adatbázistól. Ennek elsősorban a filmeket, nagy doksikat, képeket vagy egyéb nagy tömegű, strukturálatlan adatokat tárolni kívánó fejlesztők fognak neki örülni. A VARBINARY(MAX) oszlopokban, ha megjelöljük őket a FILESTREAM attributummal, akkor az adatok NEM az adatbázisban fognak tárolódni, hanem a filerendszerben, sima fájlokként. Így még a 2 GByte-os korlát is megszűnik! Nem kell most már azon se filózni, hogy a képek adatbázisban legyenek, vagy fájlrendszerben, aggódva a tranzakcionális konzisztencia miatt. Egyszerre mindkettőben, ezzel az új megoldással. :)

Ez elég szokatlan, ha belegondolunk. Mivel ez nagyon furcsa jószág, nézzük meg egy példán keresztül.

Először engedélyezni kell az új szolgáltatást a szerver példányra:

EXEC sp_filestream_configure @enable_level = 3;

A szintek:

0 Disabled. This is the default value.
1 Enabled only for Transact-SQL access.
2 Enabled only for Transact-SQL and local file system access.
3 Enabled for Transact-SQL, local file system access, and remote file system access.

Ebből már látszik, hogy az adatokat sima filerendszeren (share) is elérhetjük majd. Plusz paraméterként a share nevét is megadhatnánk (nyilván csak a 2-es és 3-as szinthez kell csak). Mivel nem adtam meg nevet, az instance neve lesz a share neve, esetemben ez sql2008:

C:\>net share

Share name Resource Remark
—————————————————————————————————
SQL2008 \\?\GLOBALROOT\Device\RsFx0100\\SQL2008 SQL Server FILESTREAM share

Az elérési út elég érdekes, infót még nem találtam róla, de a saját gépemen találtam egy .inf fájlt, ami azt sugallja, hogy egy File System Drivert telepítenek, az érti ezt az elérési utat. Az infben van az alábbi szöveg, ami sokat segít megfejteni, mi is ez:

“RsFx 0100 driver allows Win32 user-mode applications/services to own and manage Win32 namespaces of the UNC format.” Amúgy itt van implementálva: system32\DRIVERS\RsFx0100.sys. Érzésre kicsit hasonló, mint a http.sys, csak ez nem http végpontokat, hanem UNC pathokat enged lefoglani user módú alkalmazásoknak.

Olyan WinFS (Isten nyugosztalja) utóérzésem van, amikor erről olvasok. :)

A streaming adatokat külön filegroupba kell terelni, ezért eleve így hozom létre az adatbázist:

CREATE DATABASE FSTeszt ON PRIMARY
(
NAME = FSTesztData,
FILENAME = N’C:\test\FSTeszt.mdf’
),
FILEGROUP FileStreamGroup1 CONTAINS FILESTREAM
(
NAME = FileStreamData,
FILENAME = N’C:\test\FileStreamData’
)

A CONTAINS FILESTREAM jelöli ki a speciális filegroupunkat. A FILENAME valójában ebben az esetben nem egy filenév, mint megszokhattuk, hanem egy könyvár elérési útja, itt lesznek az stream adatok tárolva, fájlokban.

Ez lesz belőle a diszken:

Directory of C:\test

12/11/2007 08:27 PM <DIR> .
12/11/2007 08:27 PM <DIR> ..
12/11/2007 08:27 PM <DIR> FileStreamData
12/11/2007 08:27 PM 2,228,224 FSTeszt.mdf
12/11/2007 08:27 PM 516,096 FSTeszt_log.LDF

Vistán LUA adminként nem láthattam a könyvtár tartalmát, de admin eszkalációval ezt találtam benne:

Directory of C:\test\FileStreamData

12/11/2007 08:27 PM <DIR> .
12/11/2007 08:27 PM <DIR> ..
12/11/2007 08:27 PM <DIR> $FSLOG
12/11/2007 08:27 PM 422 filestream.hdr

Az itt keletkező további fájlokról majd egy későbbi részben értekezek (először be kell tudni tölteni őket).

Végre eljutottunk oda, hogy létrehozzunk egy táblát, ami használ streaming adatokat.

CREATE TABLE Kepek
(
Id uniqueidentifier rowguidcol not null primary key default (newid()),
Name nvarchar(256) not null,
Photo varbinary(max) filestream null
)

Mindenképpen kell egy rowguidcol-os oszlop, ez lehet PK is, vagy csak egy sima oszlop, de kell, ezzel tudja a db összehozni a tábla sorait a fájlokkal.

Mikor érdemes használni a filestream store-t?

1. Objects that are being stored are, on average, larger than 1 MB.
2. Fast read access is important.
3. You are developing applications that use a middle tier for application logic.

(For smaller objects, storing varbinary(max) BLOBs in the database often provides better streaming performance.)

A 3. pont azért fontos, mert az adatokat tipikusan nem TDS-en érjük el, hanem SMB-vel, filemegosztáson keresztül, azaz nem a megszokott adatbázis apival. Erről holnap, a következő részben írok.

SQL Server 2008 újdonságok 3. – UDT méretkorlát megszűnése

Tuesday, December 11th, 2007

Az SQL Server 2005-ben megjelent a VARCHAR(MAX) és VARBINARY(MAX), ami nagyon leegyszerűsítette a nagyobb szövegek vagy bináris adatok tárolását és elsősorban a kezelését. Emellett eltűnt a sorméret 8 kbyte-os korlátja is.

Azonban a User Defined Type-ok, azaz a CLR típusként definiált típusok esetén a 8 k limit megmaradt. Ez sajnos igaz volt az aggregáló függvényekre is. Az előbbinél még csak-csak lenyeli az ember a dolgot, habár mondjuk ha egy UTF-8-ként stringet tároló típust akarok írni, akkor a 8k limit nagyon bosszantó. Az aggregátumoknál viszont pillanatok alatt át lehet lépni a 8kt, gondoljunk egy string összefűző függvényre.

No, 2008-ban az UDT-k és az aggregáló függvények által feldolgozott adatok felhízhatnak 2 G-ig, mint a többi nagy típus. De azért csak ésszel, nem minden kliens örül, ha fel kell dolgoznia egy 2 GByte-os ojjektumot.

Magyar gárda és a cigányok

Tuesday, December 11th, 2007

Érzékeny területre kalandozok, és bár igyeszem nem politizálni a blogomban, néha nem állom meg, így most se.

Élénk visszhangja volt a Magyar Gárda felvonulásának. Nyilván nem sok közvetlen hatása van a dolognak, ártatlan bohóckodásnak is tekinthető. Egyre viszont jó: harsányan felhívja arra a figyelmet, hogy bámennyire is el akarja hallgattatni a hatalom a kérdést, a cigányok jelentős részével nem tud együttélni a társadalom. Azonban amíg a pártok segílykével és csomagokkal kilóra megveszik a cigányok szavazatait, addig érdekükben áll a nyomorban, bűnözésben tartani őket, sajnos. Ez a hatalmasoknak jó, az átlagembernek elviselhetetlen, és persze a cigányoknak se jó.
Az ilyen attrakciók, mint a felvonulás egyre jók: mivel beszél róla a sajtó, talán, talán elkezdenek gondolkodni az emberek, hogy ezt a kérdést valahogyan orvosolni kell. Nem dughatjuk a homokba a fejünket, egyrészt, mert ha betelik a pohár az embereknél valódi etnikai tisztogatás kezdődhet (Vendetta), másrészt hosszútávon nem tudja eltartani az ország sok segílykéből élő herét. Az egészségügyből el lehet venni pénzt, ott aki pórul jár úgyis meghal, nem ugat. A 10. gyerek után viszont boldogan fizeti az ország a segélyt. Noooormális?

Szóval a helyzet szerintem évről-évre feszültebb lesz, és az egészet önös érdekből hagyja az aktuális kormány eszkalálódni, nem mer hozzányúlni a cigánykérdéshez, hisz nem akarja elveszteni a szavazóbázisát (mindkét oldal játszott erre).

Az egész erről az “értelmes” cigánybejegyzésről jutott az eszembe.

Remélem, a gyerekeim felnőttkorára már normalizáltabb helyzet lesz az országban.

SQL Server 2008 újdonságok 2. – Dátum típusok

Monday, December 10th, 2007

Hát igen, már az SQL Server 2005-ben is úgy volt, hogy lesz DATE és TIME típus. Meg is írták CLR típusként, aztán kidobták, nem illett bele a képbe, kilökte azt a gazdatest.

Most, 2008-ban újra nekiláttak, ezúttal sikerrel (ha élcelődni szeretnék, márpedig miért ne, 10 éve kellett volna ezt meglépni).

No, mi a bajunk a DATETIME és SMALLDATETIME típusokkal.
1. Kicsi az értékkészletünk, 1753 előtt is volt már világ.
2. Kicsi a pontosságuk, a pontosabbnak, a DATETIME-nak is 3 ms a felbontása.
3. Nem kezelnek időzónát.
4. Nincs külön csak dátum és csak idő tároló típus. Eleve, sokszor csak az egyik kell, pl. napra kerekített dátum tárolás, és ilyenkor nem csak könnyebb kezelni a külön tárolt darabokat, de hely se kell neki annyi.

No, lássuk, mit kapunk hát a 2008-ban?

1. DATE típus. 0001-01-01 és 9999-12-31 között működik, és csak 3 byte-ot eszik. Nyilván nap a felbontása.

2. TIME típus. Maximum 100ns felbontású, lehet szabályozni, mennyire legyen pontos. TIME(7) pl. 100ns-os, és 5 byte-os igényel. TIME(0) csak 3 byte, cserébe csak század mp-ig pontos. Még egy példaként TIME(4) 4 byte, és 3-4 digitig pontos (kb. ms-os felbontás).

3. DATETIME2. Az előző kettő hibridje, 6-8 byte kell neki, nyilván az idő tag pontosságától függően.

4. DATETIMEOFFSET típus. A DATETIME2 időzónával kiegészített változata. Stringként így szoktuk leírni: ‘2007-05-08 12:35:29.1234567+12:15, azaz plusz 12 óra 15 perc az időeltolódás.

Ami izgi kérdés, hogyan látszanak ezek a típusok ADO.NET-ből, ráadásul, mit látunk 2.0-ból, és mit a 3.5-ből, ami már fel van készítve az új típusokra?

Először nézzük a 3.5-öt. Az SqlDbType-ban készült négy új érték:

SqlDbType.Date
SqlDbType.Time
SqlDbType.DateTime2
SqlDbType.DateTimeOffSet

Az SQL DATE és DATETIME2 a CLR megszokott DateTime típusára képződik le. A TIME a TimeSpanra, a DATETIMEOFFSET pedig egy új CLR típusra a System.DateTimeOffsetre alakul át. Bővebben itt.

Ez alapján a .NET fw. 2.0 SP1 és 3.0 SP1 is tartalmazza a DateTimeOffset-et. Furcsa mód, reflectorral megnézve a 3.5-ös mscorlibet nem találtam benne ezt az új típust. VS 2008 látja. A 2.0-s mscorlib-ben látja. Bugos a reflector?

Régebbi kliensek szerintem byte[]-ként látják az DateTimeOffSet-et, de most nem tudom tesztelni.

SQL Server 2008 linkek

Friday, December 7th, 2007

Miközben nézem az újdonságokat sok oldalt át fogok nézni, az arra érdemeseket berakom az ízletesbe, így akit érdekel a téma, érdemes feliratkozni ennek is a feedjére.

A link:
http://del.icio.us/ZsoltSoczo/SQL2008

A feed erre:

http://del.icio.us/rss/ZsoltSoczo/SQL2008