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
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.
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"
In Memory-Selects waren schon immer lahm...
Du hast ja schon die ParentRow... Also mach es doch mit:
dann müssten auch die Indizes verwendet werden...
Gruss
Programmierhans
Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...
@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