Laden...

DataSet - Schneller Zugriff auf Fremdschlüssel

Erstellt von leb0rtran vor 10 Jahren Letzter Beitrag vor 10 Jahren 2.288 Views
L
leb0rtran Themenstarter:in
26 Beiträge seit 2008
vor 10 Jahren
DataSet - Schneller Zugriff auf Fremdschlüssel

Hi,

ich habe in meinem Programm eine Datenbank dir mit einem DataSet realisiert ist. Eine echte Db ist nicht geplant, das Programm soll die Daten nur im DataSet vorhalten (und dann als XML speichern oder Laden).

Meine Datenbank sieht wie folgt aus:

            data = new DataSet("HddInfoDB");

            //HDD
            DataTable hdd = data.Tables.Add("Hdd");

            //ID
            DataColumn hddID = hdd.Columns.Add("ID");
            hddID.DataType = System.Type.GetType("System.Int16");
            hddID.AutoIncrement = true;
            hddID.AllowDBNull = false;
            hddID.Unique = true;
            hddID.AutoIncrementSeed = 1;

            //VolumeID
            DataColumn hddVolumeID = hdd.Columns.Add("VolumeID");
            hddVolumeID.DataType = System.Type.GetType("System.String");
            hddVolumeID.AutoIncrement = false;
            hddVolumeID.AllowDBNull = false;            
            hddVolumeID.Unique = true;
            hddVolumeID.MaxLength = 16;

            //Name
            DataColumn hddName = hdd.Columns.Add("Name");
            hddName.DataType = System.Type.GetType("System.String");
            hddName.AutoIncrement = false;
            hddName.AllowDBNull = true;            
            hddName.Unique = false;
            hddName.MaxLength = 32;

            //Size
            DataColumn hddSize = hdd.Columns.Add("Size");
            hddSize.DataType = System.Type.GetType("System.Int64");
            hddSize.AutoIncrement = false;
            hddSize.AllowDBNull = false;            
            hddSize.Unique = false;
            
            //Primary Key
            hdd.PrimaryKey = new DataColumn[] { hddID };


            //Dir
            DataTable dir = data.Tables.Add("Dir");

            //ID
            DataColumn dirID = dir.Columns.Add("ID");
            dirID.DataType = System.Type.GetType("System.Int64");
            dirID.AutoIncrement = true;
            dirID.AllowDBNull = false;            
            dirID.Unique = true;
            dirID.AutoIncrementSeed = 1;

            //Name
            //Name ohne führendes und folgendes "/", /a/b/ -> b
            DataColumn dirName = dir.Columns.Add("Name");
            dirName.DataType = System.Type.GetType("System.String");
            dirName.AutoIncrement = false;
            dirName.AllowDBNull = false;            
            dirName.MaxLength = 256;

            //Hdd ID - Foreign Key
            DataColumn dirHddID = dir.Columns.Add("HddID");
            dirHddID.DataType = System.Type.GetType("System.Int16");
            dirHddID.AutoIncrement = false;
            dirHddID.AllowDBNull = false;
            

            //Parent Dir ID - Foreign Key
            DataColumn dirParentDirID = dir.Columns.Add("ParentDirID");
            dirParentDirID.DataType = System.Type.GetType("System.Int64"); 
            dirParentDirID.AutoIncrement = false;
            dirParentDirID.AllowDBNull = true;
            

            //Primary Key
            dir.PrimaryKey = new DataColumn[] { dirID };


            //File
            DataTable file = data.Tables.Add("File");

            //ID
            DataColumn fileID = file.Columns.Add("ID");
            fileID.DataType = System.Type.GetType("System.Int64");
            fileID.AutoIncrement = true;
            fileID.AllowDBNull = false;           
            fileID.Unique = true;
            fileID.AutoIncrementSeed = 1;

            //Name
            //Name ohne führendes und folgendes "/", name.extension (beides und "." enthalten), a/b/c.d --> c.d            
            DataColumn fileName = file.Columns.Add("Name");
            fileName.DataType = System.Type.GetType("System.String"); 
            fileName.AutoIncrement = false;
            fileName.AllowDBNull = false;            
            fileName.MaxLength = 256;

            //Hdd ID - Foreign Key
            DataColumn fileHddID = file.Columns.Add("HddID");
            fileHddID.DataType = System.Type.GetType("System.Int16"); 
            fileHddID.AutoIncrement = false;
            fileHddID.AllowDBNull = false;
            

            //Parent Dir ID - Foreign Key
            DataColumn fileParentDirID = file.Columns.Add("ParentDirID");
            fileParentDirID.DataType = System.Type.GetType("System.Int64"); 
            fileParentDirID.AutoIncrement = false;
            fileParentDirID.AllowDBNull = true;
            

            //Size
            DataColumn fileSize = file.Columns.Add("Size");
            fileSize.DataType = System.Type.GetType("System.Int64"); 
            fileSize.AutoIncrement = false;
            fileSize.AllowDBNull = false;
            
            fileSize.Unique = false;

            //Primary Key
            file.PrimaryKey = new DataColumn[] { fileID };


            //Foreign Key
            ForeignKeyConstraint hddDirFK = new ForeignKeyConstraint("HddDir",hddID, dirHddID);
            hddDirFK.DeleteRule = Rule.Cascade;            
            dir.Constraints.Add(hddDirFK);

            ForeignKeyConstraint dirParentFK = new ForeignKeyConstraint("DirParentDir", dirID, dirParentDirID);
            dirParentFK.DeleteRule = Rule.Cascade;
            dir.Constraints.Add(dirParentFK);

            ForeignKeyConstraint hddFileFK = new ForeignKeyConstraint("HddFile", hddID, fileHddID);
            hddFileFK.DeleteRule = Rule.Cascade;
            file.Constraints.Add(hddFileFK);

            ForeignKeyConstraint fileParentFK = new ForeignKeyConstraint("FileParentDir", dirID, fileParentDirID);
            fileParentFK.DeleteRule = Rule.Cascade;
            file.Constraints.Add(fileParentFK);

Nun möchte ich zu einer DataRow, zB einem Verzeichnis (Dir) alle nachfolgenden Dirs und Files haben.

Folgende Select Abfrage ist aber sehr langsam (dr ist hier eine DataRow auf der Tabelle Dir):

DataRow[] dirs = this.data.Tables["Dir"].Select(
                    "HddID = '" + dr["HddID"] + "' AND ParentDirID = '" + dr["ID"] + "'");
                DataRow[] files = this.data.Tables["File"].Select(
                    "HddID = '" + dr["HddID"] + "' AND ParentDirID = '" + dr["ID"] + "'");

Anscheinend geht Select nur alle Zeilen durch.
Gibt es da eine schnellere Möglichkeit?
Die Spalten HddID und ParentDirID sind ja Fremdschlüssel. Da muss doch ein Index draufliegen um ähnlich schnell wie beim Primärschlüssel suchen zu können.

Schonmal vielen Dank

MFG

2.078 Beiträge seit 2012
vor 10 Jahren

Wie willst du in einer Tabelle suchen, ohne von oben nach unten durch zu laufen?

Egal, was du suchst, du musst von ganz oben runter zählen und schauen, ob die aktuelle Zeile auf die Bedingung passt und wenn nicht, wird sie übersprungen.

Es kann ein Fremdschlüssel auch mehrfach vor kommen und da funktioniert dann der Index nicht mehr, weil nicht mehr eindeutig bestimmt werden kann, wo das Element, das du suchst, liegt, da es Mehrere gibt.

Da ich nicht weiß, wie ein DataSet genau arbeitet, weil ich bisher meist die altmodische Art verwendet habe.
Da baue ich mir dann halt mein Statement per Hand zusammen, sende es an die Datenbank und bekomme einen DataReader, den ich Zeile für Zeile durch gehen kann.
Bis das da aber langsam wird, müssen es schon einige Tausend Zeilen sind und dann macht sich das wohl gerade mal in wenigen Sekunden spürbar.

Ich vermute einfach mal, dass der Unterschied darin liegt, dass bei deiner Vorgehensweise erst alle Ergebnisse zusammen gesammelt und dann gesammelt ausgegeben werden.
Ein DataReader hat den Vorteil, dass er durch die Ergebnisse iteriert und dabei folgende Zeilen erst kennen muss, wenn er in der Reihenfolge dort angekommen ist. Heißt, er muss das letzte Element nicht kennen, während er beim Ersten ist.
So kann ich die erste Zeile schon verarbeiten, während die Datenbank noch fleißig am ausgeben ist.

Da du aber keine Datenbank hast, sondern nur ein DataSet, was dann als XML gespeichert wird, weiß ich nicht, ob die Möglichkeit auch geht.

Aber so oder so: Warum keine Datenbank?
Wenn du so viele Daten hast, dass so eine Abfrage so lange dauert, dann schreit das geradezu nach einer Datenbank. Außerdem kannst du dir dann das Speichern sparen, da es sowieso gespeichert wird.

P
660 Beiträge seit 2008
vor 10 Jahren

ergänzend zu Palladins Post

Wenn du die Installation einer Datenbank scheust dann such dir eine embedded Datenbank wie z.b. SQLite.

MfG
ProGamer*Der Sinn Des Lebens Ist Es, Den Sinn Des Lebens Zu Finden! *"Wenn Unrecht zu Recht wird dann wird Widerstand zur Pflicht." *"Ignorance simplifies ANY problem." *"Stoppt die Piraterie der Musikindustrie"

4.221 Beiträge seit 2005
vor 10 Jahren

In Memory-Selects waren schon immer lahm...

Du hast ja schon die ParentRow... Also mach es doch mit:

DataRow.GetChildRows-Methode

dann müssten auch die Indizes verwendet werden...

Gruss
Programmierhans

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

L
leb0rtran Themenstarter:in
26 Beiträge seit 2008
vor 10 Jahren

@Programmierhans:
Danke, sowas in der Richtung habe ich gesucht, das werde ich mal testen.

@Palladin007 und ProGamer
Das Programm ist nur für mich Privat und eigentlich recht simpel gehalten, da möchte ich nicht gleich ne DB bemühen.
Notfalls schreibe ich mir nen Index für den Zweck halt selber, sollte ja ganz simpel sein (Zeilen nach der ParentDirID & ID Sortieren und sich Start und Endzeile für jede Kombination merken, davon gibts ja nicht so viele).

Aber erstmal teste ich den Vorschlag von Programmierhans

Edit:
Der Vorschlag von Programmierhans (getChildRows) ist genau das was ich gesucht habe.
Meine DB musste ich dazu aber noch anpassen, da ein ForeignKeyConstraint nicht automatisch eine Relation erzeugt.

Vielen Dank für eure Hilfe,
kann geschlossen werden