verwendetes Datenbanksystem: keines - nur eine XML Datei
Hallo 😃
Mittels eines Formulars soll es möglich sein eine XML Datei zu durchsuchen und die gefundenen Datensätze anzeigen zulassen.
Dabei bin ich auf ein Problem gestoßen.
Die Anzahl der Suchparameter ist unbekannt.
Es gibt z.B. drei Felder: Vorname, Nachname, Emailadresse
(In der realität sind es mehr Felder)
Gibt man nur den Vornamen an, sollte natürlich nur der Vorname abgefragt.
Gibt man Vorname und Emailadresse an, natürlich nur Vorname und Emailadresse.
Da ich aber nicht weiß, wieviele Suchparameter genutzt werden weiß ich nicht genau wie ich die Abfragen realisieren soll.
die Datensätze in der XML Datei sehen folgendermaßen aus:
<dataset firstName="Olga" lastName="Wukawitsch" email="olga.wukawitsch@1234.de" />
Für Ideen und Hilfestellungen bin ich dankbar,
viele Grüße,
Sythus
Hallo Sythus
Du musst halt die XPath-Abfrage abhängig von der User-Eingabe zusammenbauen. Was ist denn genau das Problem?
Grüße
Flo
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.
Hallo Florian,
ich habe mehrere Eingabefelder
Vorname, Nachname und Email
wäre nun für Vorname : Manfred und für Nachname : Müller eingegeben würde die perfekte Abfrage folgendermaßen aussehen
node = (from Dataset in Stammdaten.Elements()
where Dataset.Attribute("Vorname").Value == "Manfred"+ Dataset.Attribute("Nachname").Value == "Müller"
select Dataset);
Wenn aber nur das Feld für Vorname belegt wäre mit "Manfred" würde die Perfekte Abfrage wie folgt aussehen
node = (from Dataset in Stammdaten.Elements()
where Dataset.Attribute("Vorname").Value == "Manfred"
select Dataset);
(Die Strings wie Vorname und Manfred wären normalerweise in Variablen enthalten)
Natürlich könnte man das bei 3 Feldern problemlos mit if Abfragen prüfen
if(vorname.text != "" && nachname.text != "")
dann folgende Abfrage
oder
if(vorname.text != "" && nachname.text == "")
dann folgende Abfrage
Aber bei sehr vielen Eingabefeldern wären das sehr viele IF Abfragen - und das muss doch sicher auch einfacher gehen oder?
Ich hoffe du verstehst mein Problem - oder seh ich den Wald vor lauter Bäumen nicht mehr?
Danke und schöne Grüße
Sythus
Wie schon gesagt, ich hätte einfach XPath verwendet, aber du kannst das auch über string.IsNullOrEmpty abbilden:
string firstName = txtFName.Text;
string lastName = txtLName.Text;
node = (from Dataset in Stammdaten.Elements()
where (string.IsNullOrEmpty(firstName) || Dataset.Attribute("Vorname").Value == firstName)
&& (string.IsNullOrEmpty(lastName) || Dataset.Attribute("Nachname").Value == lastName)
select Dataset);
Grüße
Flo
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.
Vielen dank, darauf wär ich warscheinlich nich gekommen - aber du hast recht das wird bei vielen datenfeldern auch ne monströse abfrage...
wie sähe denn das zusammenbauen mit xpath aus?
Danke und lg
Hallo,
die Abfrage bzw. die Where-Klauseln können mit dem PredicateBuilder zusammengesetzt werden. Kurzbeispiel gibt es hier.
mfG Gü
Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.
"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
Beispiel für XPath (benötigt "using System.Xml.XPath")
// DOM from anywere
XDocument dom = new XDocument();
StringBuilder xpath = new StringBuilder();
xpath.Append("dataset [");
// first name
if (!string.IsNullOrEmpty(textBox1.Text))
xpath.AppendFormat("@Vorname='{0}' or ", textBox1.Text);
// last name
if (!string.IsNullOrEmpty(textBox2.Text))
xpath.AppendFormat("@Nachname='{0}' or ", textBox1.Text);
xpath.Append("1=1]");
var result = dom.XPathSelectElements(xpath.ToString());
... allerdings finde ich den Ansatz von Gü auch recht cool.
Grüße
Flo
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.
Ja beides echt super Möglichkeiten, genau nach sowas habe ich gesucht 😃
Vielen dank euch 2n
Gruß Sythus
Gute Morgen,
Die XPath Abfrage funktioniert wunderbar. Ich musste lediglich das or in ein and umwandeln, da die abfrage sonst immer "alle" Datensätze findet.
Eine letzte Frage bleibt allerdings noch - ist es auch möglich zu überprüfen ob der Text den man sucht in dem Attribute enthalten ist?
lastName="HansMüller"
suche nach hans (am besten unabhängig von der groß und kleinschreibung)
ergibt den Datensatz HansMüller
Danke und beste Grüße
Hallo Sythus
Geht, dürfte aber ziemlich auf die Performance gehen, wenn du eine große XML Datei hast.
Schau dir mal die XPath Funktionen lower-case() und contains() an. Bei W3CSchools gibt's eine recht hilfreiche Referenz:
XPath, XQuery, and XSLT Function Reference
Grüße
Flo
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.
Danke schön, ja die XML Datei ist extrem groß, ca. 50 000 Datensätze
hoffe das mit der Performance geht noch so einigermaßen
Gruß
Sythus
PS: Gibt es Alternativen in sachen Performance?
Du kannst statt eines XML Files auch eine Datenbank wie SQL Server CE (oder natürlich auch was größeres) verwenden.
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.
Das Problem daran ist nur, das dass Programm bei jedem Start erstmal eine Exportdatei im CSV Format in dieses XML Umwandelt (nur im Speicher) - damit ich die Abfragesprache und damit eine schnellere Suchfunktion nutzen kann.
Jedesmal eine Datenbankdatei zu schreiben (beim Programmstart) ist einiges mehr Aufwand oder?
Ich muss diese Daten aber immer neu schreiben, da der Export (die CSV Datei) jeden Tag aktualisiert wird.
Gruß
Sythus
Ich hab gerade mal testweise 50,000 Datensätze in eine SQL Server CE Datenbank importiert. Dauerte ca. 6 Sekunden, mit zwei Indizes zusätzlich auf insgesamt auf knapp 10 Sekunden. Reicht das?
Wenn RAM kein Problem ist wäre eine Andere Option die Daten statt in ein XML eher in eine DataTable zu laden, dürfte noch ein ganzes Stück schneller sein und du bekommst komplexe Suchen geschenkt.
Wenn RAM ein Problem ist und 10 Sekunden zu lange sind aber ein SQL Server 2008 (Expresss/Standard/Enterprise) vorhanden ist kannst du die Daten über einen Bulk-Insert in die Datenbank schreiben. Ist ein komplett anderes Konzept und etwas mehr Aufwand, dürfte dann aber inklusive aller Indizes deutlich weniger als 1 Sekunde dauern.
Grüße
Flo
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.
WoW!!! Danke für die Mühe
Das sind wirklich tolle Möglichkeiten.
10 Sekunden für ein Laden beim starten des Programmes finde ich okay... man darf nur nicht vergessen das bei einer langsameren CPU gerne mal das doppelte oder dreifache der Ladezeit rauskommen kann.
Gruß
Sythus
Beispiel für XPath (benötigt "using System.Xml.XPath")
// first name if (!string.IsNullOrEmpty(textBox1.Text)) xpath.AppendFormat("@Vorname='{0}' or ", textBox1.Text); // last name if (!string.IsNullOrEmpty(textBox2.Text)) xpath.AppendFormat("@Nachname='{0}' or ", textBox1.Text); xpath.Append("1=1]"); var result = dom.XPathSelectElements(xpath.ToString());
So darf dann die Usereingabe aber keine ' enthalten, oder?
So darf dann die Usereingabe aber keine ' enthalten, oder?
Korrekt. War ein kurz hingehacktes Beispiel 😉
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.
Kleiner Nachtrag
Die XPathAbfrage ist auch bei 50 000 Datensätzen und zusätzen wie "starts-with" und "lower_case" noch recht Perfomant.
Problematisch wird es nur die Daten alle in einer ListView anzuzeigen.
Das Problem ist also nicht die XPathAbfrage sondern das anzeigen der Ergebnisse.
Gibt es da performantere Controls?
Gruß
Sythus
Beste Lösung
Kein ListView verwenden 😉. 50.000 Datensätze sind für ein ListView definitiv zu viel. Diese Menge an Daten macht eigentlich in gar keiner Client-Anzeige Sinn. Wenn's aber wirklich sein muss würde ich dringend ein DataGridView empfehlen.
Das Einzige das man beim ListView noch machen kann ist BeginEdit()/EndEdit() wird aber auch nicht wirklich den Tiger in den Tank packen 8)
Grüße
Flo
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.
Danke Florian,
ich zeige nun einfach maximal 500 Suchergebnisse an und die Information das der Sucher seine Suche doch etwas spezifizieren soll, da es mehr als 500 Suchergebnisse gibt.
Damit kommt das Listview Control ganz gut klar - 50 000 Suchergebnisse anzeigen wär eh wenig Sinnvoll.
Gruß
Sythus
Ich muss leider nochmal ne kleine Frage stellen, hast du zufällig ne Idee wie man die Abfrage ohne beachtung der Groß- und Kleinschreibung regeln könnte?
Habs nun sehr umständlich gemacht - und das is auch sehr performance lastig.
Beim Einlesen in das XML File wandel ich alle Strings zu lower-case Strings um...
Anschließend das gleiche bei der XPath Abfrage mit dem Text des Inputfeldes.
Das ist Irre - gibts da ne bessere Möglichkeit?
Alles am Anfang alles konvertieren kostet natürlich einmalig sehr viel Performance, hat jedoch den Vorteil, dass die Suchen später schneller sein sollten.
Ansonsten kannst du innerhalb der XPath-Anweisung mit lower-case arbeiten:
// first name
if (!string.IsNullOrEmpty(textBox1.Text))
xpath.AppendFormat("lower-case(@Vorname)='{0}' or ", textBox1.Text);
Wie sich das auf die Geschwindigkeit auswirkt musst du aber testen.
Grüße
Flo
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.
dann müsste ich die Strings der Inputfelder aber auch ToLowerCase wandeln oder?
if (!string.IsNullOrEmpty(stNumber.Text))
xpath.AppendFormat("starts-with(lower-case(@stNumber) , '{0}') and ", stNumber.Text);
Geht leider schonmal nicht ->
{"Namespace-Manager oder 'XsltContext' erforderlich. Diese Abfrage hat einen Präfix, eine Variable oder eine benutzerdefinierte Funktion."}
Ja so ein Misst...
Habe gerade mal versucht und gegoogelt. .NET unterstützt nur XPath 1.0. lower-case ist eine Funktion aus XPath 2.0. Witziger Weise sind die Funktionen für XSLT schon vorhanden...
Dann kannst du wohl nur "translate()" als XPath 1.0 Krücke verwenden:
string xml = @"<?xml version='1.0' encoding='iso-8859-2'?><root><child attr1='BLA'></child></root>";
XmlDocument dom = new XmlDocument();
dom.LoadXml(xml);
XmlNodeList list = dom.SelectNodes("root/child [@attr1=translate('bla', 'abcdefghijklmnopqrstuvwxyzäöü', 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ')]");
Grüße
Flo
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.
Hallo Flo,
na das probier ich gleich mal aus - danke^^ bin mal gespannt inwiefern das die performance beeinflusst - ich meld mich dann wieder
thx!
Nachtrag:
Ich kriege das leider nicht mit starts-with zum laufen, es spuckt zwar keine Fehlermeldung aus, aber finden tut er auch nichts
/Root/Dataset[starts-with(@stNumber=translate('s', 'abcdefghijklmnopqrstuvwxyzäöü', 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ'), 's') and 1=1]
Die Performance nur mit "translate" war aber voll in Ordnung
Gruß
Sythus
Du transformierst mit "translate()" deinen Text erst von "s" nach "S" und prüfst den Wert dann auf "s" 😉
Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+
Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.