Archive for the ‘Szakmai élet’ Category

Boldog Karácsonyt

Saturday, December 24th, 2011

Kedves Olvasóim,
boldog Karácsonyt nektek.
Idén megszakadtam a munkától, így szinte alig írtam, pedig rengeteg új dolgot tanultam: Oracle, NHibernate, WCF mélységei, WPF perf tuning, stb. Jövőre remélem sikerül kidumpolni az agyamból ezeket, mert -főleg az NHibernate- nagyon jó dolog. EF elment szabira, mióta megismertem. :)

Alapban egy Dependency property nem kétirányú bindingos

Monday, June 20th, 2011

Meg kell adni a regisztrációjakor explicit, ha azt akarjuk, az legyen.


public static readonly DependencyProperty MyPropertyProperty =
    DependencyProperty.Register(
         "MyProperty",
         typeof(int),
         typeof(Window1),
         new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

1 órámat ismét elvittek. :)

Gondolatok a queue alapú kliens-szerviz kommunikációhoz

Thursday, June 16th, 2011

Az előző post kommentjei alapján (amit nagyon köszönök mindenkinek) nem kaptam sok bátorítást az aszinkron, queue alapú, kérés-választ különválasztó gazdag kliens - szerviz kommunikációhoz, úgy tűnik senki nem csinált ilyet, így nem akarok úttörő lenni a témában.
Ehhez még hozzájárul, hogy hétvégén méregettem az MSMQ teljesítményét. Azért ezt, mert a WCF is erre épít, és pl. az NServiceBus is.

A tesztkód 1.5kByteos üzeneteket rak át egyik sorból a másikba. Az ötlet innen jött, csak többszálasítottam.

A tesztkód:


using System;
using System.Diagnostics;
using System.Messaging;
using System.Threading;

namespace MsmqTran
{
    class Program
    {
        private const int NumberOfTests = 1000;
        private const int MaxDop = 10;
        private static readonly ManualResetEvent[] WaitForEmpty = new ManualResetEvent[MaxDop];

        static void Main()
        {
            var q1 = new MessageQueue(@".\private$\test_queue1");
            var q2 = new MessageQueue(@".\private$\test_queue2");

            Console.WriteLine("Filling source queue...");
            var b = new byte[1500];
            using (var msmqTx = new MessageQueueTransaction())
            {
                msmqTx.Begin();
                for (int i = 0; i < NumberOfTests; i++)
                {
                    q1.Send(b, msmqTx);
                }
                msmqTx.Commit();
            }

            q2.Purge();
            Console.WriteLine("Starting to move data from source queue to destination queue");

            var sp = Stopwatch.StartNew();

            for (int i = 0; i < MaxDop; i++)
            {
                WaitForEmpty[i] = new ManualResetEvent(false);
                ThreadPool.QueueUserWorkItem(o => ProcessMsg(q1, q2, (ManualResetEvent)o), WaitForEmpty[i]);
            }

            WaitHandle.WaitAll(WaitForEmpty);

            Console.WriteLine("Duration: {0}ms, throughput: {1:F0} messages/s", sp.ElapsedMilliseconds, 1000.0 * NumberOfTests / sp.ElapsedMilliseconds);
        }

        private static void ProcessMsg(MessageQueue q1, MessageQueue q2, ManualResetEvent w)
        {
            while (true)
            {
                using (var msmqTx = new MessageQueueTransaction())
                {
                    msmqTx.Begin();

                    Message message;
                    try
                    {
                        message = q1.Receive(TimeSpan.FromMilliseconds(0), msmqTx);
                    }
                    catch (MessageQueueException e)
                    {
                        Console.WriteLine(e);
                        w.Set();
                        break;
                    }

                    q2.Send(message, msmqTx);

                    msmqTx.Commit();
                }
            }
        }
    }
}

A gépemen 50 tran/sec-kel megy 1 szálon, és 200 fölé nem nagyon megy. Jó, ez laptop, de relációs adatbáziskezelővel (sql server és oracle is fut a gépen) több ezer tran/seccel mennek a dolgok. Szóval ez elég gázosan lassú. Emellett csúnya leállásokról is írnak a blogokban, amikor beáll az msmq.

Marad a aszinkronított WCF egyelőre, csak a szerverről visszafelé hívásokat tervezem queue alapon megcsinálni, WCF msmq bindinggal. Így tudom értesíteni az appokat polling nélkül. Erre 3 okom van most:
1. Az offline (disconnected) pessimistic lock feloldódott, lehet szerkeszteni valamit.
2. Frissíteni kell a kliens cache-ben valamit.
3. Email jellegű üzenetküldés az appok között.

Köszönöm még egyszer az építő javaslatokat.

Message alapú LOB app tervezés

Saturday, June 11th, 2011

Próbálom megtörni magamban a megszokott gondolkodási sémákat. Tervezek egy nagy, többrétegű LOB alkalmazást. A szervizrétegnek elsődlegesen a WPF rich klienst kell kiszolgálni, de jó lenne, ha mobil kliensek is tudnák ezt fogyasztani.
Ha a szerviz interfész finom felbontású, sok kicsi metódussal, akkor a kommunikáció sok időt rabol el, lassú lesz az app (chatty interfész).
Ha durva felbontású (chunky), akkor megvan a veszélye, hogy a fogyasztó WPF app formjaihoz lesz illesztve, célirányosan, így akkor meg mobil kliensek nem fogják tudni hatékonyan fogyasztani.
Egy érdekes alternatívát láttam a probléma feloldására itt és itt.
Ebben az összes kliens-szerviz kommunikáció message alapú, teljesen aszinkron. Egy kitöltött form adatait elküldik egy queueba, a szerver pedig egy másik üzenettel válaszol erre. Azaz a kliens alapjaiban aszinkron, és a válaszok események formájában jönnek vissza.
A message alapú kommunikáció nagy előnye, hogy az üzeneteket össze lehet nyalábolni, és egyben elküldeni. Így a szerviz lehet finom felbontású, de a kommunikáció mégis gyors, nem sok kis darabból áll. Érthetően ezt szinkron, rpc jellegű szerviz hívásokkal nem lehet megcsinálni.
Ez a fajta queued, service bus alapú gondolkodásmód szokatlan nekem, de nagyon szimpatikus. Van valakinek tapasztalata ilyen rendszer tervezésével? Bármilyen url, pointer, könyv, személyes tapasztalat érdekelne. Úgy érzem nagy dolgokat lehet ezzel összerakni, csak meg kell alaposan értenem.

Még egy gondolat. WCF proxyk használatával az alapban message alapú, asszinkron kérés-válaszokból összeraknak egy RPC jellegű kommunikációt, úgy, hogy a kliens vár a szerver válaszára. Aztán a kliens kedvéért, hogy ne blokkoljuk le a GUIt csinálunk háttérszálat, ahol bevárjuk a szerviz válaszra váró szálat. :)
Miért nem használunk eleve aszinkron üzenetváltásokat, és akkor sokkal egyszerűbb lesz a kép. A kliens bepostázza a kérést a buszra, és megy tovább. Amikor a válasz megjön, akkor meg erről eseményben értesül. Nyilván itt is vannak háttérszálak, hisz ki fogadná akkor a szervertől jövő választ, és ki hívná vissza a kliens eseményeken keresztül, de valahogy akkor is tisztábbnak tűnik a kép, mint az erőltetett RCP-jellegű módszernél. Nem?

Kindle DX - szeretem

Sunday, May 22nd, 2011

Pár hónapja vettem egy Kindle DX-et. Nagy érvágás volt, tokkal, vámmal 570$ volt, ami fájt. De nagyon nem bántam meg, és ezt nem a kognitív disszonancia mondatja velem. :)
Amióta megvan, sokkal többet olvasok. Az elmúlt két hónapban legalább 20 könyvet és vagy 30 kisebb cikket tanulmányoztam benne. Nem mindet olvastam ki A-tól Z-ig, de tudjuk, hogy a szakmai könyveket nem is mindig így szoktuk (max., ha új területről van szó).
Ha van pl. egy codeproject, msdn, stb. cikk, vagy egy whitepaper, bármi, ami pár oldalnál több, már nyomom is rá calibre-vel, aztán olvasom az ágyban (kocsiban váráskor, játszótéren, dokinál, stb.).
És még nem használom ki csak tört részét annak, amit lehetne vele olvasni (hírek, blogok, stb.). Az esti mese is erről megy, a mekről. :)

Szóval imádom, aki szeret olvasni, szeretni fogja. A technikai speciókról nem írok, ez olyan, amit látni kell, aztán vagy utálod, vagy szereted.

Alternatíva a gonosz EAV-val szemben?

Thursday, May 19th, 2011

Az üzleti igény egy olyan adatbázis struktúra létrehozása, amelyben az előre definiált táblák adatai mellé a kusztomerek saját maguk is fel tudnak venni plusz adatokat, anélkül, hogy ehhez az adatbázistáblákat és az alkalmazást módosítani kellene. Sajnos nem csak letárolják ezeket, de még keresni is akarnak egyesekre. Azaz egyfajta open schemat kellene megvalósítani. Erre jó lehet az Entity-Attribute-Value megoldás, de úton-útfélen gonosznak van kinevezve. Értem én, hogy szar belőle lekérdezni, marha nagy lehet, ha egyszerűen csinálják meg nem típusos (én minden típushoz akarok egy oszlopot létrehozni), de milyen használható alternatíva van vele szemben?
Olvastam a serialized blobot, pl. egy xml oszlopba rakjuk a plusz dolgokat. Elvileg lehet xqueryzni, meg indexelni, de érzésre ez se egyszerű, se gyors nem lesz.
A sparse colum esetén séma módosítással jár az új adat definíciója. Ez egyrészt nem tetszik secu okokból (alter table kell hozzá), másrészt elég gáz szervizelni az alkalmazást, ha 50 ügyfél saját oszlopokkal bővítheti a tábláinkat.
Szóval, mi a jó a feladatra? Nekem még mindig az EAV tűnik a legkezelhetőbbnek.

SQL Server - Oracle könyv

Wednesday, April 27th, 2011

Meglegyintett a sötét oldal, ezért kénytelen leszek kicsit érteni az Oracle-höz is. :)

Tud valaki jó könyvet, ami valamiféle SQL Server - Oracle turbó átképzésről szól? Nem akarom a create table-nél kezdeni, ikább valami diff könyv kellene.
Persze, lehet ez nem ilyen egyszerű, el kell kezdeni valami Essential Oracle könyvvel.

Érdekes SQL programozási hibák

Wednesday, April 27th, 2011

Előző héten két érdekes hibát láttam ügyfeleknél.
Az egyikben láttam, hogy a lekérdezés végrehajtási tervében konverzió van, datetime -> integer, emiatt scan volt seek helyett, azaz lassú volt a lekérdezés.
Ami fura volt ebben, hogy egy izeID oszlop volt összehasonlítva egy datetime értékkel. Az ID-k tipikusan intek, megnézve a táblát tényleg az volt. Volt egy lokális változó, ami copy-paszta miatt datetimera sikerült int helyett, és ezzel írták tovább a where szűrést. A lekérdezés már 2 éve ment élesben. :)
Ami érdekes volt benne, hogy funkcionálisan jól működött, a számok szépen konvertálódtak dátumokká és vissza, nem volt vele gond, csak lassú volt az egész. Bizarr hiba.

A másik esetben egy ilyen lekérdezés volt:


declare @a int
while valami
begin
  select @a = oszlop from tabla where ...
end

Furcsa volt, hogy a @a furcsa módon tartalmazott értéket akkor is, amikor nem érintett sort a select a where miatt.
Jobban belegondolva ez nem is fura, hisz a select NEM ad értéket a változónak, ha nem érint sort, és NEM is nullázza ki. Első iterációkor a @a null, hisz nem kapott értéket, a második iterációnál meg benne volt az első futás eredménye, ami már nem null volt, bár azt várták. Nem nagy hiba, de időrabló tud lenni.

option(recompile) nem mindig működik

Thursday, April 14th, 2011

Hirtelen egymás utáni napokon 3 cégnél égetett be az SQL Server 2005-től létező option(recompile), miután nem működik.

A hint célja az lenne, hogy a konkrét paraméterértékek ismeretében kérünk egy újrafordítást, így az optimizer ki tud dobni felesleges ágakat a lekérdezésből, ezzel nagyon hatékony terveket tud létrehozni egyes speciálisabb lekérdezésekhez.
Pl. gyakori, hogy a paraméterre csak akkor kell szűrni, ha nem null, ha null, jöjjön vissza minden sor:

… where @param is null or oszlop = @param

Ez alapban scan lenne, lassú. Az option(recompile) hatására azonban ha a @param null, akkor teljesen kiesik a sor, ha nem null, akkor meg leegyszerűsödik erre:

… where oszlop = @param

Ez meg már jó kis gyors seek lesz, megfelelő index esetén.

Mi itt a gond? Csak az, hogy ez sok verzión NEM működik. Pedig működött. :)

Az ok a következő. SQL 2005-ben még nem működött. 2008-ra megcsinálták, ment, csakhogy egy igen durva bugot is beleszereltek: ha többen hajtanak végre ilyen hintelt lekérdezést, akkor összekeveredhetnek az eredményhalmazok. Ez az innye, bazmeg típusú bug. Gyorsan ki ki kommenteltek pár sort, mivel megvarrni meg nem olyan egyszerű, mint elrontani. Így ez most nem megy az újabb SQL Servereken.
Erland barátunk szerint az R2 RTM NEM tartalmazza még a javított verziót (magyarul helyesen működik, nem nem gyorsít).
Viszont ez az R2 CU1 már javítja.
Ezen doksi szerint már 2008-hoz is van javítás, a CU5-ben.

Már csak le kéne cseréni a cégeknek a 2005-öt. :)

.NET fejlesztői álláslehetőség

Monday, April 4th, 2011

Kb. egy hónap múlva elkezdek dolgozni egy igen komplex nagyvállalati enterprise app prototípusán. Ebben pár hónapon keresztül fogok részt venni, viszont valakinek ebből kész terméket is kellene készíteni. Ehhez nyitott meg a megrendelő cég 2 állást, amelyeket alább részletezek.
Ha kedvet érzel .NET-tel, a legmodernebb technológiákkal dolgozni és emberileg is passzol rád amit a követelményekben leírnak, akkor várunk szeretettel.
Az állások már most nyitottak, mivel a velem együtt dolgozzuk ki a prototípust, a cél az, hogy a tartósan a projekten dolgozó emberek mélységében tisztában legyenek azzal, amit tervezek.
Ha érdekel a dolog, az email címemen keresztül jelentkezhetsz.

A két állás:

Termékfejlesztési vezető

Több, mint 10 éve eredményesen működő, nemzetközi háttérrel rendelkező szoftverfejlesztő cég .NET és Oracle technológián alapuló, integrált vállalatirányítási rendszer fejlesztési projektjéhez termékfejlesztési vezető keres főállásban.

Feladat:
• Folyamatban lévő termékfejlesztési projekt vezetésének átvétele, újraszervezése;
• A termék specifikációjának áttekintése és teljessé tétele, a rendszerterv elkészítése;
• A fejlesztési csapat munkájának összehangolása , a termékfejlesztési projekt különböző fázisaiban a projekt céljainak elérése érdekében a megfelelő, felelős szakmai és vezetői döntések meghozatala;
• A projekttel kapcsolatos erőforrás-menedzsment, felelős becslések elkészítése , feladatok kiosztása, megoldások ellenőrzése;
• Fejlesztői csapat vezetése, együttműködés más szakmai vezetőkkel.

Cégünk számára az ideális jelölt:
Informatikai vagy műszaki felsőfokú végzettséggel rendelkezik, tapasztalata van, sikeres .NET-es termékfejlesztési projektekben vezető fejlesztőként/tervezőként, továbbá projektvezetésre alkalmas vezetői készségekkel rendelkezik. Határozott, céltudatos személyiség képes gyors döntések foganatosítására. Stratégiai, üzleti szemlélet jellemzi, képes ’big picture’-ben gondolkodni. Munkája magabiztos elvégzéshez Oracle ismerettel és közép szintű angol nyelvismerettel rendelkezik.

Munkavégzés helye: Budapest

Jelentkezés:
Hosszú távú, kölcsönös együttműködésre alapuló munkakapcsolat kialakítása érdekében várjuk jelentkezését.
Jelentkezését csatolt fényképes magyar és angol nyelvű szakmai önéletrajzzal, motivációs levéllel, referencia megjelölésével az alábbi címen várjuk: zsolt.soczo@gmail.com

A másik:

.NET vezetőfejlesztő

Több, mint 10 éve eredményesen működő, nemzetközi háttérrel rendelkező szoftverfejlesztő cég .NET és Oracle technológián alapuló, integrált vállalatirányítási rendszer fejlesztési projektjéhez vezetőfejlesztőt keres főállásban.

Feladat:
• A senior és junior fejlesztők szakmai irányítása, modultervezés, a feladatok megvalósítása a fejlesztőkkel és riportálás.
• A hozzá rendelt fejlesztők munkájának szakmai irányítása, számukra feladatok szabása és értékelése.
• Termék moduljainak tervezése a fejlesztési munka feladatokra bontása, kiosztása, visszavétele. A specifikációt végző konzulenssel való együttműködés a tervezés érdekében az ügyfél igény pontosítása ügyféllel szükség esetén.
• Döntés a tesztelési feladatokról, együttműködés a tesztelési csoporttal.
• Erőforrásbecslés, a megadott becslések visszaellenőrzése, a becslési folyamat pontosítása.

Cégünk számára az ideális jelölt:
Informatikai vagy műszaki felsőfokú végzettséggel rendelkezik, tapasztalata van, sikeres .NET-es termékfejlesztési projektekben vezető fejlesztőként. Pontos, precíz összehangolt munkavégzésre képes, munkája magabiztos elvégzéshez Oracle ismerettel és közép szintű angol nyelvismerettel rendelkezik.

Munkavégzés helye: Budapest

Jelentkezés:
Hosszú távú, kölcsönös együttműködésre alapuló munkakapcsolat kialakítása érdekében várjuk jelentkezését.
Jelentkezését csatolt fényképes magyar és angol nyelvű szakmai önéletrajzzal, motivációs levéllel, referencia megjelölésével az alábbi címen várjuk: zsolt.soczo@gmail.com

Microsoft Visual Studio 2010 Service Pack 1 letölthető

Wednesday, March 16th, 2011

Még nem néztem, mi van benne, de VS SPknél nem szoktam agyalni, felrakom.

Érdekes .NET perf tapasztalat

Saturday, March 5th, 2011

Amikor profilerrel megnézünk egy .NET kódot sokszor megdöbbentő helyen lesz benne bottleneck.

Az alábbi kód 1% időt visz el egy nagyon processzorintenzív kódban:


if (bar.L == 0)

Ami ebben lassú, az a System.Decimal.op_Implicit(int32). A bar.L egy decimal. Érdekes, mi?
Mi a megoldás? A 0 legyen valóban decimal, de int, amit konvertálni kell:


if (bar.L == 0M)

1% kevés, de sok 1% már számít.

Insert… select … minimálisan logolt módú az SQL 2008-ban

Thursday, March 3rd, 2011

Ha az insert table with (tablock) módon írjuk a lekérdezést, és az heap, akkor bulk insert lesz a sima insert. Nem kevés idő megtakarítást jelent ez.

Mitől ébred fel a gép?

Tuesday, March 1st, 2011

Pár napja amikor elaltatom a gépet, azonnal felébred.

powercfg -devicequery wake_armed

Dell Wireless 1510 Wireless-N WLAN Mini-Card
Intel(R) 82567LM Gigabit Network Connection

Kiderült, hogy egy nemrég felrakott termék beállította a második hálókártyánál, hogy ébressze fel a gépet. Az volt a céljuk szerintem, hogy a wake on lan menjen, de ennél többet állítottak be, ez külön opció.

Adding External References to SQL CLR Projects

Friday, February 25th, 2011

Ha egy SQLCLR eljárásból kell meghívni valamilyen külső komponenst.
A trükk az, hogy a külső assembly-t is be kell telepíteni az SQL serverbe CREATE ASSEMBLY-vel, mivel az csak onnan hajlandó betölteni assemblyket (pár fw. alap dllt kivéve).

SQL Server Profilerben TSQL hibák vizsgálata

Wednesday, February 16th, 2011

SQL tanfolyamot tartok egy cégnél (.NET 4-et és Design Patternst meg egy másiknál :). Az oktatások arra jók, hogy mindig rájövök mit nem tudok, így aztán van módom újat tanulni. Azt meg általában szeretek. :)

Na szóval, ha a szerver oldalon történik egy exception valamilyen sql parancs végrehajtási hiba következtében, akkor ez szépen látszik a profilerben, csak az nem, mi váltotta ezt ki.
Most lapozgattam az Inside Microsoft SQL Server 2005: Query Tuning and Optimization könyvet, és ott belefutottam a megoldásba.
Nem csak az Errors And Warnings: Exception eseményt kell bekapcsolni, hanem a User Error Message-et is.
.
A képen látható, az EventSequenceből szépen összerakható az események egymásutánisága.
Ez megint egy olyan apró kérdés volt, amit 5 perc guglizás megold, de hát nem ilyenekkel van tele az ember feje?

SQLXMLBulkload

Monday, February 7th, 2011

Már el is felejtettem, hogy van ilyen. Öregszem. :)

C# 4 Covariance

Tuesday, February 1st, 2011

.NET 4-es tanfolyamhoz írok egy prezentációt. A co és -contravarianciát próbálom érhetővé tenni. Kb. ez van a slideokon:

Variance annotations – covariance 1.


class Allat { }
class Kutya : Allat { }

interface IAllatGyar<T> where T : Allat
{
    T Create();
}
class AllatGyar<T> : IAllatGyar<T> where T : Allat
{
    public T Create()
    {
        return default(T);
    }
}
AllatGyar<Kutya> kutyagyar = new AllatGyar<Kutya>();
IAllatGyar<Allat> allatGyar = kutyagyar; //cast, jogos?
Allat a = allatGyar.Create(); //mit ad vissza, típusbiztos?

Type ordering
Kutya <: Allat - A Kutya az Allat osztály leszámazottja
k : Kutya - k a Kutya típus egy példánya
Ha k : Kutya és Kutya <: Allat, akkor k : Allat (Liskov féle helyettesítési elv)
Igaz-e, hogy ha Kutya <: Allat, akkor IAllatgyar< Kutya > <: IAllatgyar< Allat > ?
Egy Kutya példány implicit castolható Allattá. Az Allatgyar< Kutya > típusbiztosan castolható-e Allatgyar< Allat >-tá?
Ha csak kimeneti paraméterként vagy visszatérési értékként jön ki T, akkor igen, mert Kutya <: Allat, így minden kimeneti paraméternél a Allat referenciával biztonságosan elérhető egy leszármazott Kutya példány is.
Ha igen, akkor T covariant, mert a típusparaméterek type orderingje érvényes a típust használó generikus típusra is, ugyanabban a sorrendben (Kutya <: Allat ==> IAllatgyar< Kutya > <: IAllatgyar< Allat >).
C# 4-ben az interfész metódus vagy delegate szignatúrában az out kulcsszó jelzi ezt:


interface IAllatgyar<out T>.

Amikor ez nem megy:


class Allat { }
class Kutya : Allat { }

interface IAllatGyar<T> where T : Allat
{
    T Create();
    void Valami(T t); //T befelé megy!
}

AllatGyar<Kutya> kutyagyar = new AllatGyar<Kutya>();
IAllatGyar<Allat> allatGyar = kutyagyar;

kutyagyar.Valami(new Kutya()); //Ez nyilván ok

allatGyar.Valami(new Lo());    //És ez???

Ha bemeneti paraméter is T, akkor az IAllatgyar< Allat > statikus típuson, amely valójában a IAllatgyar< Kutya > dinamikus típusra mutat átadható lenne más állat is, pl. Ló, amely szintén Allat leszármazott, de az nyilvánvaló runtime hibát okozna. Ezért nem lehet covariant T, ha az interfészen bemenetként is szerepel T.

Érhető ez így? Bármilyen javaslatot szívesen fogadok. A delegatekhez is írok persze anyagot, ez csak az eleje.

Csúnya multithreading hiba

Friday, January 21st, 2011

Double checked lockingnak indult, de bug lett belőle. Mit rontottam el?


private static volatile Dictionary<DateTime, Pair<TimeSpan>> holidayDays;
private static readonly object staticLock = new object();

private Dictionary<DateTime, Pair<TimeSpan>> GetHolidayDays()
{
    if (holidayDays == null)
    {
        lock (staticLock)
        {
            if (holidayDays == null)
            {
                holidayDays = new Dictionary<DateTime, Pair<TimeSpan>>();
                FillTradingHours(holidayDays, "HOL");
            }
        }
    }
    return holidayDays;
}

How to Share Data Between Stored Procedures

Thursday, January 6th, 2011

Felírom magamnak, hasznos lehet.
http://www.sommarskog.se/share_data.html