A következő címkéjű bejegyzések mutatása: SQL. Összes bejegyzés megjelenítése
A következő címkéjű bejegyzések mutatása: SQL. Összes bejegyzés megjelenítése

2021. november 22., hétfő

Nagy mennyiségű adat beolvasása SQL adatbázisba EntityFramework segítségével

  

Nagy mennyiségű adat beolvasása könnyen hatékonysági problémába torkolhat. Dotnet keretrendszerbe az adatbázisba írásra és olvasásra használhatunk EntityFramework-öt. Ezt elsősorban egy egyszerű példán mutatnám be. Ehhez a példához egy konzol aplikációt kezdtem el 4.7.2 keretrendszerben. Elsősorban kell az EntityFramework NuGet csomag:



Feladat:

A feladat a következő: Egy txt fájlból írjuk be az adatokat SQL adatbázisba. A fájlba utaknak a nevei és kódja vannak tárolva. A fájl minden sora egy utat ír le, ahol az első tag a név a második a kód ezek egy helyközzel vannak elválasztva.

Megoldás:

Elsősorban létrehoztam egy Model könyvtárat a megoldáson belül és ebben létrehoztam az Utak nevű osztályt. Ez az osztály reprezentálja az adatbázisban a táblát, aminek 3 oszlopa lesz: Id, Nev, Kod.

namespace Beolvasas.Model

{

    [Table("Utak")]

    public class Utak

    {

        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]

        [Key]

        public int Id { getset; }

        public string Nev { getset; }

        public long? Kod { getset; }

 

    }

}

Ezek után létrehoztam a Data könyvtárat a megoldáson belül. Ebben raktam az adatbázis osztályt. Ami tartalmazta az Utak táblát.

namespace Beolvasas.Data

{

    class UtakDbContext : DbContext

    {

        public UtakDbContext()

            base("name=UtakDbContext")

        {

        }

        public DbSet<Utak> Utak { getset; }

    }

}

Amit fontos még megtenni, hogy az App.configba rakjuk bele a connectionStringet.

<connectionStrings>

             <add name="UtakDbContext" connectionString="data source=.;initial catalog=UtakDb;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />

       </connectionStrings>

Végül az adatbázis kezelésére is létrehoztam egy osztályt.

namespace Beolvasas

{

    public class AdatbazisKezeles : IDisposable

    {

        private UtakDbContext _utakdb;

        public AdatbazisKezeles()

        {

            _utakdb = new UtakDbContext();

        }

        public void AdatbazisLetrehozasa()//letrehozza az adatbázist, ha nem létezik

        {

            _utakdb.Database.CreateIfNotExists();

            _utakdb.SaveChanges();

        }

        public void AdatokBetoltese(string fajl)

        {

            string fileName = fajl;

            FileStream fs = File.OpenRead(fileName);

            StreamReader sr = new StreamReader(fs);

            string sor;

            while ((sor = sr.ReadLine()) != null)

            {

                Utak ut = new Utak();

                string[] adatok = sor.Split();

                ut.Nev = adatok[0];

                ut.Kod = long.Parse(adatok[1]);

                _utakdb.Utak.Add(ut);

            }

            _utakdb.SaveChanges();

        }

        public void Dispose()

        {

            if (_utakdb != null)

                _utakdb.Dispose();

        }

    }

}

Ennek az osztálynak az AdatbazisLetrehozasa() nevű eljárás létrehozza az adatbázist, ha nem létezik, az AdatokBetoltese(string fajl) eljárás pedig a paraméterben megadott fájlnak az adatait betölti az adatbázisba.
Végül a főprogram:

namespace Beolvasas

{

    class Program

    {

        static void Main(string[] args)

        {

            AdatbazisKezeles adatb = new AdatbazisKezeles();

            adatb.AdatbazisLetrehozasa();      adatb.AdatokBetoltese(@"C:\Users\LászlóCsernok\source\repos\Beolvasas\Utak.txt");

        }

    }

}

Számunkra az AdatokBetoltese nevű eljárás fontosabb. Ebben az eljárásban soronként olvassa be a program az adatokat a fájlból majd létrehoz egy Utak nevű osztályt, ezt feltölti a megfelelő adatokkal, amit a fájlból olvas be. Egy adatot szépen berak a DbSet<Utak> Utak nevű változóba. Amikor minden soron végig ment akkor az adatbázisba beleírja az adatokat a _utakdb.SaveChanges() metódus hívással.
Ez egy működő megvalósítás kisebb adatokra szépen gyorsan működik, de nagyobb adatoknál már érezhető a lassulás. A legnagyobb problémát _utakdb.Utak.Add(uthívás jelenti, mert ez a legidőigényesebb.

 

Javítás 1:

 

Első próbálkozás az lehet, hogy csak egyszer hívjuk a legköltségesebb műveletet az Add-ot. Ezt úgy érjük el, hogy létrehozunk egy listát, amibe beletesszük a beolvasott adatokat. Majd ez a listát adjuk hozzá. Ezzel jelentős mértékben növelhetjük a kód hatékonyságát.
A módosított kód:

 

public void AdatokBetoltese(string fajl)

        {

            string fileName = fajl;

            FileStream fs = File.OpenRead(fileName);

            StreamReader sr = new StreamReader(fs);

            string sor;

            List<Utak> utak = new List<Utak>();// a lista amikbe az elemeket rakjuk

            while ((sor = sr.ReadLine()) != null)

            {

                Utak ut = new Utak();

                string[] adatok = sor.Split();

                ut.Nev = adatok[0];

                ut.Kod = long.Parse(adatok[1]);

                utak.Add(ut);  //hozzá adás

                //_utakdb.Utak.Add(ut); -- régi megvalósítás

            }

            _utakdb.Utak.AddRange(utak);// hozzá adjuk a dbSethez

            _utakdb.SaveChanges();

        }

 

Javítás 2:


Ha még szeretnénk gyorsítani a kódon akkor használhatunk egy NuGet csomagot én az alábbit használtam:



A kód az előbbihez nagyon hasonló marad, csak a _utakdb.Utak.AddRange(utak) sort kell            _utakdb.BulkInsert(utak) -ra kicserélni.

public void AdatokBetoltese(string fajl)

        {

            string fileName = fajl;

            FileStream fs = File.OpenRead(fileName);

            StreamReader sr = new StreamReader(fs);

            string sor;

            List<Utak> utak = new List<Utak>();// a lista amikbe az elemeket rakjuk

            while ((sor = sr.ReadLine()) != null)

            {

                Utak ut = new Utak();

                string[] adatok = sor.Split();

                ut.Nev = adatok[0];

                ut.Kod = long.Parse(adatok[1]);

                utak.Add(ut);  //hozzá adás

                //_utakdb.Utak.Add(ut); -- régi megvalósítás

            }

 

            //_utakdb.Utak.AddRange(utak);// hozzá adjuk a dbSethez -- helyett

            _utakdb.BulkInsert(utak);

            _utakdb.SaveChanges();

        }

Szerintem ezzel a módszer elég gyors adatbetöltés érhető el. Esetleg baj lehet, ha sok az adat és nem fér el a listában, ekkor szerintem darabolással megoldható.

Másik példa: https://dotnetfiddle.net/awlJdf

2016. június 28., kedd

Entity Frameworkkel UDT paraméterű tárolt eljárás futtatása

Az Entity Framework alaphangon nem támogatja a saját SQL típust, vagyis a User Defined Type-ot. Ha egy olyan tárolt eljárást szeretnénk importálni az EF-fel, ami UDT típusú paramétert vár, akkor ugyan nem fog hibát dobni, de nem is fogja legenerálni a hozzátartozó kódot.

Ennek áthidalására egy jó módszer az EntityFrameworkExtras nevű NuGettel is elérhető csomag, aminek segítségével típusosan lehet ilyen tárolt eljárást futtatni. A bekötéséhez ezen a linken van részletesebb leírás.

2013. október 7., hétfő

OPENQUERY vs. 4 Part Linked Server Syntax

Ha SQL Server alá belinkelünk bármilyen egyéb szervert ODBC driverrel, akkor általában 2 módszerünk van lekérdezni (3 is lehet, de az most nem érdekes).

1. 4 Part Linked Server Syntax

Ez az, amikor simán megírunk egy queryt, mintha "nálunk" lenne a tábla, így:

2. OpenQuery

Az OpenQuery név talán ismerős lehet, ez az, amikor egy string-ben 'átadjuk' a query-t, aminek az eredményét szeretnénk látni. Így néz ki:

Miben különböznek?

Előszeretettel használják a legtöbben az egyszerűség és a paraméterezhetőség miatt az elsőt, hisz olyan mintha egy sima sql query-t írnánk. Akár tárolt eljárásban is használhatjuk, paraméterezhető, egyszerű a where feltétel megadása, sőt, ha így kreálunk egy view-t, abból meg még egyszerűbb lekérdezni.

Feketeleves

Amikor a "túloldalon" megszaporodnak a sorok, egyre lassabb lesz. Egészen egyszerű az oka, ugyanis az történik, hogy a túloldalról ÁTHOZ minden rekordot az adott táblából, és helyben végzi el a szűrést, ami többmilliós rekordszámnál annyira nem hatékony. Emiatt a VIEW-t sem tudjuk használni ami annyira kényelmesnek tűnik, hiszen amikor belekérdezünk a VIEW-ba, akkor is mindent áthoz, majd abból fog selectálni helyben.

A második eset NAGYON macerás, ha tárolt eljárást akarunk írni és paraméterekkel használni, de nem helyben fut le a lekérdezés. Hanem? A túloldalon, és már csak az eredményhalmaz kerül át az sql szerverhez. Szóval sokkal gyorsabb, de sajnos az OPENQUERY-nek nem lehet csak úgy paramétereket átadni.
Magyarul össze kell matyizni az sql stringet úgy, hogy paraméterektől függően legyen benne a where feltétel megírva. 1 paraméternél még annyira nem gáz, de amikor már van 4-5, és vannak dátumok is, és mindegyik lehet null is, akkor nem 2 perces feladat.

Szóval, 2 ma esti mérési eredmény, SQL Express alá linkelt SQLite adatbázissal, aminek adott táblájában 4.7 millió rekord van:
1. módszer: 
futási idő 405 sec

2. módszer: 
futási idő 1 sec

Nem mindegy...

2012. szeptember 28., péntek

Everything SQL Server Compact

http://erikej.blogspot.hu/2011/01/comparison-of-sql-server-compact-4-and.html
Táblázat a különböző verziók tudásáról:
Compact 35, Compact 4, SQL Express, SQL Server LocalDB

2012. szeptember 20., csütörtök

ORM <-> Relational


Összeszedve a lényeg, bár írják, hogy kezeld fenntartással néhány megállapítást.

2011. július 29., péntek

DateTime.Kind SQL-ből EF-fel felolvasott és jQuery-vel használt dátumoknál

DB-ben nem tárolódik a DateTime KIND értéke (Local, Utc, Unspecified). a jQuery viszont mindig normalizál UTC-re, így egy local stílusban tárolt DateTime megjárva a UI-t el fog mozdulni 2 órával UTC +2H esetén. Mivel az SQL-be nincs lenyomva ez az érték, ezért normalizálni kell UTC-re, hogy egy teljes kör megtétele után ne másszon el az érték:

Megoldás a Entity Framework-nél:
http://www.aaroncoleman.net/category/Database.aspx

2011. július 1., péntek

SQL tunning tipp

Ha egy rendszernél gyanítjuk hogy az sql szerver a lassú, de nem tudjuk trace-elni, vagy nem reprodukálhatóan lassú hanem csak esetenként, akkor itt egy szuper leírás, hogy hogyan kérdezzük le sql szervernél a top sql-eket, VISSZAMENŐLEG! A lekérdezésből minden látszik amire szükség lehet ilyen esetben (mennyiszer futott le, mennyi IO, mennyi az átlagos futási idő stb.).

UI: Ma tanultam, köszönet érte a blogolónak :)

UI2: Oracle-nél is van hasonló, csak ott valamilyen paraméterezési beállítástól függ, hogy készítsen magának ilyen statisztikákat.

2011. március 17., csütörtök

SSIS csomag futtatása

Futass SSIS csomagot parancssorból:
http://www.simple-talk.com/content/print.aspx?article=270

2011. március 10., csütörtök

2011. március 6., vasárnap

SSIS csomag futtatási lehetőségei

Az SSIS-ről már volt egy gyorstalpaló, és a futtatási lehetőségekről is volt egy rövidebb bejegyzés.

Arról viszont nem esett még szó, hogy ehhez az egészhez, hogy ütemezve futtatni tudjuk, kell-e sql engine-t feltelepíteni.

Hát, ha ezen az msdn blogon vagy ezen a másik sql tips oldalon a leírásokat végignézzük, akkor azt a következtetést tudjuk levonni, hogy nem csak az SQL Agent-el lehet futtatni package-et, hanem egy command line util-al, a DTEXEC-el is.

Ebből pedig következik az, hogy ha ezzel a cuccal ezt lehet futtatni, akkor ha írunk egy batch filet vagy egy exe-t vagy valamit, akkor ezt már a sima windows-os ütemezővel tudjuk akkor futtatni, amikor akarjuk.

Tehát van megoldás arra, hogy sql engine nélkül ütemezetten futtassunk SSIS csomagokat.

2011. február 4., péntek

SQL server tananyag - video

Találtam egy nagyon szuper összeállítást, kb 40 órányi video anyag, SQL server témakörben.
Akit érdekel, itt a link:

http://www.sqlskills.com/T_MCMVideos.asp

2010. december 17., péntek

SQL server tévhitek

Sokszor vagyunk tanácstalanok, vajon hogyan is működik valami az SQL szerveren. Van tippünk, de valaki határozottan ellentmond, más meg határozottan azt tudja amit mi.
Na, erre találtam egy jó kis doksit, ami tipikus sql szerveres tévhiteket cáfol meg, pontosabban válaszol meg, hogy igaz vagy nem igaz.

Common SQL Server Myths

Nem rövid, de tanulságos.

Ha már sql server, akkor itt pedig egy ingyenes MSSQL 2008 könyv , administration és business intelligence developement.

2010. augusztus 30., hétfő

Backup SQL Server 2008 R2 Restore on SQL 2008

Az imént futottunk bele az SQL 2008 R2 restore on 2008 problémájába.

2010. április 15., csütörtök

Mappelés dokumentálása, XSD generálás

Dokumentálni szeretnénk két adatbázis közötti kapcsolatot, van egy régi adatbázis amit majd migrálni kell egy újba. Más szerkezet, más mezőnevek, más táblanevek, de valahogy ki kell igazodni rajta.

Ezen felbuzdulva elkezdtem használni egy jó kis tool-t, az Altova MapForce-t. ALTOVA cég fejleszti, az XMLSpy is az övék (talán ezt többen ismerik). Altova.com oldalról letölthető egy ingyenes verzió (1 hónapos trial), a cucc egyébként sajnos fizetős. Abban segít sokat, hogy bármilyen adatbázist fel tud nyalni, és egyszerűen ha beállítunk egy source-t és egy destination-t, akkor szépen össze lehet kötögetni hogy ki kivel játszik, és ha valamilyen transzformáció szükséges, azt is megoldhatjuk.
Flash bemutató róla, ha valakit érdekel hogy hogy kell használni. Egyébként így néz ki vele egy egyszerűbb mapping:



Marha jó doksit generál, de nem ez az egyetlen előnye, XSLT transzformációt képez, valamint c#, c++, java kódokat is tud. C# solution generálást kipróbáltam, soksok kódot ír, futtatni nem futtattam, nem volt időm.
Szép, okos, megbízható progi, ajánlani tudom.

Aztán jött az ötlet, hogy hát ezt a mappelést, hogy melyik régi mező tartalma majd hova kerül, a BizTalk fejlesztéséhez használt Visual Studio-val is csinálhatnánk, és akkor szépen bekerülhetne a source control alá is, akár valamelyik solution-ünkbe is. Aztán ezen felbuzdulva feltettem a BizTalk-ot, hogy megnézzem hogy azzal mit tudunk csinálni.
Hogy őszinte legyek, kicsit nagyobb rugalmasságot vártam volna, főleg a 2009-estől, de sajna elég fapados. Nem tud egyszerűen felnyalni egy adatbázis táblaszerkezetet. Úgy működik, hogy először az SQL adapterrel egy XSD-t kell generálni, majd azok között lehet a mappelést berajzolni. Ez önmagában nem is baj, de xsd-t generálni egyszerre csak 1 tábláról tud, pontosabban egy select-ben lehet definiálni (vagy tárolt eljárásban), hogy miből legyen az xsd. Nem egyszerű az sem, ezt a linket érdemes elolvasni MIELŐTT az ember nekiugrik. Kis trükk van a select statement megadásakor :)
Arról nem is beszélek, hogy a BizTalk telepítésekor nem regisztrálódott be néhány fontos dll (ezen a linken resolution 3), ami miatt sokat szívtam (szívtunk) mire rájöttem hogy mivan.
Tehát a baj az vele, hogy egy tábláról tudok (egyenlőre) csak xsd-t csinálni az Sql Adapterrel, ami meg azért nem jó, mert csak 1 xsd lehet a bal, és egy a jobb oldalon (amik között a mappelést be akarom rajzolni).
Nagyjából így néz ki egyébként egy mapping:



Tehát a végső megoldásom az lett, hogy más eszközzel legeneráltam az xml sémákat, amikre szükségem van, majd azokat beraktam a BizTalk mappingbe. Kérdés, hogy ha majd ezek a sémák változnak, hogyan fogja a berajzolt mapping azt lereagálni... Ez a jövő zenéje.

2010. február 23., kedd

SQL tárolt eljárásban feltétel vizsgálat

Lehet hogy nem mindenkinek mondok újat a mostani bejegyzéssel. Tegnap már sokadjára olyan problémába ütköztem, hogy egy sql tárolt eljárás írása közben a WHERE feltételben nagyon jól jött volna egy IF-ELSE jellegű dolog.
Megpróbálom érthetően leírni mire is gondolok, egy példával szemléltetve.

Csomag listát szeretnék lekérdezni, és a WHERE-ben kell megadnom pl egy ilyen feltételt:
HA kell rá elszámolni Árushelyi jutalékot(feltétel1), akkor kell léteznie már ilyen elszámolásnak(feltétel2). (Magyarul el kellett már számolni, ha egyáltalán el kell számolni, ha nem kell elszámolni, akkor nem érdekes).
Ezt a where-ben így lehet megoldani:
WHERE (feltétel1 = false) OR (feltétel1 = true AND feltétel2 = true)

Idáig még nem is annyira bonyolult. Akkor kezd bonyolulttá válni, ha a feltétel1-et úgy tudom eldönteni, hogy másik 5 feltételt kell vizsgálnom hozzá.
így:
Feltétel1=(feltétel1.0 OR feltétel1.2) AND (feltétel1.3 AND (feltétel1.4 OR feltétel1.5))
Kezd bonyolódni, főleg azért mert mindegyik feltétel1.x vizsgálat bonyolult.
Tehát jó lenne ha csak egyszer kellene megvizsgálni, hogy el kell-e számolni, és amennyiben el kell, akkor meg kell lennie az elszámolásnak, minden más esetben nem érdekes.

Így lenne jó ha lenne:
WHERE
IF(feltétel1 = true)
BEGIN
feltétel2-nek igaznak kell lennie!
END
ELSE nem érdekel az ég világon semmi!

T-SQL-ben ilyet nem lehet (PLSQL-ben sem).
Nekem nem volt annyira egyértelmű (ezért is blogoltam, mert talán másnak sem) mire rájöttem, hogy ezt egy VIEW-val egyszerűen meg lehet oldani. Létrehozunk egy VIEW-t, aminek egy mezőjében visszaadjuk azt, hogy mi a feltétel1 (el kell-e számolni), és egy másik mezőben meg visszaadjuk, hogy az elszámolás megtörtént-e (left join-al, ha nem null akkor megtörtént).
Ezt a VIEW-ban nem annyira nehéz összehozni, és a tárolt eljárásban jelentősen leegyszerűsödik a dolog, és sokkal átláthatóbb, olvashatóbb a kód is.
És ezek után a VIEW segítségével már így le tudom kérdezni, ugyanúgy néz ki mint az elején, csak a bonyolult feltétel vizsgálat a view-ban van, egyszer, és a visszaadott (BIT) mezőt kell csak vizsgálni:
SELECT * FROM view v
WHERE v.feltétel1 = false OR (v.feltétel1 = true AND v.feltétel2 = true)

Megjegyzés: Nem a spanyolviaszt találtam fel, de lehet más is belefut ebbe, ne teljen bele majdnem egy nap fejtörésbe hogy hogyan lehetne egyszerűbben :)

2010. január 29., péntek

SQL insert script generálás

Többször előfordul, hogy a fejlesztés során szükségünk van arra, hogy az adatbázisba valamilyen módon felvitt teszt adatokat ki tudjuk tenni egy insert script-be, hogy azzal utána bármikor újra elő tudjuk állítani a teszt (vagy enum) adatainkat.

Erre már régóta egy jól működő tárolt eljárást használok, nekem sokat segített. Csak meg kell futtatni, tábla neve a bemenő paraméter, és megkapjuk az insert scriptet, hozzá sem kell nyúlni.

Itt érhető el:
http://docs.google.com/View?id=ddfrb926_84gw35s8dm

Ha nem nyílik meg, itt tölthető le az sql file:
https://docs.google.com/leaf?id=0B9q258JugRDsY2IxYjAyYmEtZTk1My00Zjg5LWE4OWYtYTdmOGE5YTcwMGQ5&hl=en

Nem én írtam, csak használom. Neten találtam, szabadon használható.