vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Tag 18

Komplexe Techniken: Komplexe Abfragesysteme

Viele der schwierigsten Aufgaben auf Ihren Websites werden einfacher, wenn Sie Ihrem SQL-Server die Arbeit überlassen. Die Möglichkeiten Ihres Datenbankservers erlauben Ihnen, wesentlich komplexere Applikationen zu entwickeln. In diesem Kapitel lernen Sie eine solche Applikation kennen, wo die Abfrage eine scheinbar unlösbare Aufgabe ganz einfach macht. Die Themen des heutigen Tages:

18.1 Komplexe Suchen

Bei der Entwicklung von Websites, die Benutzerinformationen sammeln, werden Sie feststellen, dass es unabdingbar ist, Suchfunktionen für die Datenbank bereitzustellen. SQL stellt per se ein ausgezeichnetes System für die Abfrage von Tabellen dar. Aber was passiert, wenn der Benutzer das System außerhalb einer direkten SQL-Abfrage nach Parametern abfragen will? Und noch schlimmer: Was passiert, wenn die Suche mit einigen der Einträge, aber nicht mit allen übereinstimmen muss?

Angenommen, wir durchsuchen eine Gebrauchtwagendatenbank. Der Benutzer sucht nach einem blauen Kabrio, aber er würde es auch in violett nehmen - und wenn es keines in violett gibt, dann würde er auch schwarz nehmen. Diese Art Logik ist in einer Suche, die nicht speziell für den abfragenden Benutzer geschrieben wurde, sehr schwierig darzustellen.

Wie also kann ein System entwickelt werden, das solche »Abstufungen« einer Antwort zulässt?

Betrachten wir dazu unterschiedliche Situationen - wobei eine davon die Komplexität sogar noch steigert. Ich fange beim einfachsten Fall an, sodass Sie während der Beschreibung hoffentlich erkennen, worauf ich hinauswill. Sie können das Ganze gegebenenfalls in MySQL nachvollziehen.

Als erstes untersuchen wir die Situation, in der Sie versuchen, eine Zahl in einer Tabelle zu finden, die möglichst nahe an der von Ihnen eingegebenen Zahl liegt. Eine Beispieltabelle könnte wie folgt aussehen:

create table tblNumber (
a int
);

Jetzt fügen wir Beispieldaten ein:

insert into tblNumber values (5);
insert into tblNumber values (6);
insert into tblNumber values (1);
insert into tblNumber values (15);
insert into tblNumber values (9);
insert into tblNumber values (8);
insert into tblNumber values (21);
insert into tblNumber values (500);
insert into tblNumber values (10);
insert into tblNumber values (50);

Nun brauchen wir eine Zahl, nach der wir suchen wollen. Denken Sie sich einfach eine Zahl zwischen 1 und 500 aus.

Angenommen, wir suchen nach Datensätzen, die der Zahl 6 so nahe wie möglich kommen. Die erste Abfrage, die wir ausprobieren, verwendet eine direkte Auswahl:

mysql> select * from tblNumber where a=6;
+------+
| a |
+------+
| 6 |
+------+
1 row in set (0.05 sec)

Das ist nicht sehr effizient für die Suche nach Zahlen, die nahe an 6 liegen. Wie können wir es also bewerkstelligen? Wir könnten die Auswahl durch eine Bedingung ergänzen, um Zahlen zu finden, die neben der 6 liegen:

mysql> select * from tblNumber where a=6 or a=5 or a=7;
+------+
| a |
+------+
| 5 |
| 6 |
+------+
2 rows in set (0.02 sec)

Dies ist ein besserer Ansatz - aber auch noch nicht perfekt. Die Suche ist immer noch darauf beschränkt, was direkt in der Abfrage angegeben werden kann. Wir könnten das Ganze ein bisschen verbessern, indem wir die Auswahl auf die Zahlen erweitern, die um 6 herum liegen, aber damit lösen wir das eigentliche Problem immer noch nicht.

Wir sollten einen anderen Denkansatz wagen. Die WHERE-Klausel der Abfrage setzt eine Bedingung, die die Suche auf die vorgegebenen Parameter beschränkt, aber wie können wir feststellen, wie sehr ein Benutzer seine Suche beschränken will? Sucht er nach einer Zahl im Bereich 6 +/-5, oder 6 +/-500? Durch die Konzentration auf die Auswahl der Daten haben wir eine ganz einfache Lösung übersehen.

Der korrekte Ansatz wäre, die Beziehung zwischen 6 und allen anderen Zahlen in der Tabelle zu betrachten. Nehmen wir beispielsweise die Zahl 7. In welcher Beziehung steht sie zu der 6? Sie liegt eine Stelle von der 6 entfernt. Wie nahe liegt 6 an der Zahl 10? Sie liegt vier Stellen von 10 entfernt. Die Antwort auf alle unsere Probleme ist die Subtraktion. Durch Subtrahieren der Werte in der Tabelle von dem angegebenen Wert richten wir eine Beziehung zwischen einer vorgegebenen Zahl und den Einträgen in einer Tabelle ein.

Das ist praktisch, aber wie genau wollen wir dieses Wissen in einer Abfrage darstellen? Es ist nicht ganz so schwierig, wie Sie es sich vielleicht vorstellen. Sie müssen nur erkennen, dass alle Zahlen in einer bestimmten Beziehung zu der vorgegebenen Zahl stehen - wobei es nur Unterschiede darin gibt, wie eng diese Beziehung ist. Verstehen Sie das System bereits? Es ist keine Frage, wie wir die Auswahl der Datensätze beschränken, sondern wie wir beschränken, wie die ausgewählten Datensätze angezeigt werden. Der Teil der Abfrage, der die eigentliche Arbeit übernimmt, wird vollständig von einer benutzerdefinierten ORDER BY-Klausel gesteuert, die der Abfrage hinzugefügt wird.

Skizzieren wir einen ersten neuen Ansatz für die Abfrage:

mysql> SELECT *,(6-a) as 'difference' FROM tblNumber ORDER BY difference;
+------+------------+
| a | difference |
+------+------------+
| 500 | -494 |
| 50 | -44 |
| 21 | -15 |
| 15 | -9 |
| 10 | -4 |
| 9 | -3 |
| 8 | -2 |
| 6 | 0 |
| 5 | 1 |
| 1 | 5 |
+------+------------+
10 rows in set (0.04 sec)

Diese Abfrage definiert eine variable Differenz, die den Ausdruck (6-a) enthält. Diese Auswertung erfolgt für jeden der Datensätze in der Datenbank - die Ergebnisse werden nach diesem Parameter sortiert. Das Problem dabei ist, dass Zahlen kleiner der angegebenen Zahl ein positives Ergebnis erzeugen, während die größeren Werte negative Ergebnisse produzieren. Das funktioniert also nicht. Nach diesem Ergebnis sind die Zahlen 1 und 9 der Zahl 6 gleich ähnlich. In der Realität liegt 9 näher bei 6 als 1. Die negativen und positiven Abstände müssen also gleich behandelt werden. Wir müssen für die Abfrage also die Funktion ABS verwenden, um den Absolutwert zu berechnen. Der Absolutwert einer negativen Zahl ist das positive Gegenstück. Nachdem wir der Abfrage die Funktion hinzugefügt haben, erhalten wir Folgendes:

mysql> SELECT *,ABS(6-a) as 'difference' FROM tblNumber ORDER BY difference;
+------+------------+
| a | difference |
+------+------------+
| 6 | 0 |
| 5 | 1 |
| 8 | 2 |
| 9 | 3 |
| 10 | 4 |
| 1 | 5 |
| 15 | 9 |
| 21 | 15 |
| 50 | 44 |
| 500 | 494 |
+------+------------+
10 rows in set (0.05 sec)

Das ist schon besser. Die Ergebnisliste zeigt, wie nahe die Zahlen aus der Tabelle der Zahl 6 sind. Je weiter unten ein Wert liegt, desto weiter ist er von unserer vorgegebenen Zahl entfernt.

Aber wie nutzen wir das in der Praxis - normalerweise fordern wir den Benutzer nicht auf, eine Zahl auszuwählen! Das lässt sich einfach erklären - der Benutzer wählt zwar in der Regel eine nicht-numerische Antwort aus einem Popup-Menü o.Ä. aus, aber diese Antwort könnte durch eine Zahl dargestellt werden.

Wählen Sie beispielsweise die Rauchgewohnheiten Ihres bevorzugten Reisebegleiters aus:

  1. Nichtraucher
  2. Gelegenheitsraucher
  3. Raucher
  4. Kann vor lauter Rauch nicht mehr aus den Fenstern sehen

Hier werden die möglichen Auswahlmöglichkeiten numerisch in eine Relation zueinander gesetzt. Die Abfrage einer Datenbank kann, um mögliche Übereinstimmungen zu Ihren bevorzugten Rauchgewohnheiten zu finden, diese Werte übernehmen und die oben beschriebene Technik nutzen, um ähnliche Antworten in einer Ergebnisliste anzuzeigen. Auf diese Weise können Sie Menschen finden, die mit Ihren Vorstellungen »in etwa« übereinstimmen, aber nicht unbedingt dieselben Gewohnheiten wie Sie haben müssen. Die Welt ist nicht schwarz und weiß - und Ihre Abfragen sollten das berücksichtigen.

Das hilft uns jedoch immer noch nicht wirklich weiter, wenn mehrere Fragen beantwortet werden müssen - und darauf wollen wir letztlich hinaus.

Das System muss erweitert werden, sodass Sie mehrere Antworten daraufhin auswerten können, in welcher Relation sie zu anderen Antworten stehen. Für diese Aufgabe verwenden wir eine etwas erweiterte Tabelle, die zwei Werte aufnimmt, um die beiden Antworten darzustellen, die miteinander verglichen werden:

create table tblTwoNumber (
a int,
b int
);

Tragen Sie Werte in die Tabelle ein:

INSERT INTO tblTwoNumber VALUES (2,5);
INSERT INTO tblTwoNumber VALUES (10,5);
INSERT INTO tblTwoNumber VALUES (9,1);
INSERT INTO tblTwoNumber VALUES (15,50);
INSERT INTO tblTwoNumber VALUES (1,2);
INSERT INTO tblTwoNumber VALUES (20,5);
INSERT INTO tblTwoNumber VALUES (2,5);
INSERT INTO tblTwoNumber VALUES (11,2);
INSERT INTO tblTwoNumber VALUES (3,7);
INSERT INTO tblTwoNumber VALUES (6,6);

Wählen Sie zwei Zahlen aus, für die eine möglichst genaue Übereinstimmung gefunden werden soll. Ich habe für a und b die Werte 8 und 12 ausgewählt.

Wenn wir dieselbe Prozedur wie für einzelne Antworten verwenden, erhalten wir zwei Ergebnislisten, die die Rangfolge der anderen Datensätze in Relation zu den ausgewählten Zahlen zeigen:

mysql> SELECT *,ABS(8-a) as 'difference' FROM tblTwoNumber ORDER BY difference;
+------+------+------------+
| a | b | difference |
+------+------+------------+
| 9 | 1 | 1 |
| 10 | 5 | 2 |
| 6 | 6 | 2 |
| 11 | 2 | 3 |
| 3 | 7 | 5 |
| 2 | 5 | 6 |
| 2 | 5 | 6 |
| 15 | 50 | 7 |
| 1 | 2 | 7 |
| 20 | 5 | 12 |
+------+------+------------+
10 rows in set (0.02 sec)

und

mysql> SELECT *,ABS(12-b) as 'difference' FROM tblTwoNumber ORDER BY difference;
+------+------+------------+
| a | b | difference |
+------+------+------------+
| 3 | 7 | 5 |
| 6 | 6 | 6 |
| 2 | 5 | 7 |
| 10 | 5 | 7 |
| 20 | 5 | 7 |
| 2 | 5 | 7 |
| 1 | 2 | 10 |
| 11 | 2 | 10 |
| 9 | 1 | 11 |
| 15 | 50 | 38 |
+------+------+------------+
10 rows in set (0.01 sec)

Beachten Sie, dass die Auflistung der ausgewählten Übereinstimmungen bei der Betrachtung des Feldes »a« sehr viel anders aussieht als bei der Betrachtung von »b«. Daran erkennen Sie das Problem, mit dem wir es zu tun haben. Wie können Sie bei der Ausführung der Abfrage beide Variablen berücksichtigen? Kein Problem - wir ändern die Abfrage einfach ab, sodass sie nach dem kombinierten Fehler (oder der Distanz der ursprünglichen Zahlen) in den beiden Abfragen sortiert:

mysql> SELECT *,(ABS(8-a)+ABS(12-b)) as 'difference' 
FROM tblTwoNumber ORDER BY difference;
+------+------+------------+
| a | b | difference |
+------+------+------------+
| 6 | 6 | 8 |
| 10 | 5 | 9 |
| 3 | 7 | 10 |
| 9 | 1 | 12 |
| 2 | 5 | 13 |
| 2 | 5 | 13 |
| 11 | 2 | 13 |
| 1 | 2 | 17 |
| 20 | 5 | 19 |
| 15 | 50 | 45 |
+------+------+------------+
10 rows in set (0.00 sec)

Durch die Addition der beiden Absolutwerte erhalten wir einen Maßstab dafür, wie weit die verschiedenen Datensätze von den beiden ausgewählten Zahlen entfernt liegen.

Dieses Modell kann erweitert werden, um beliebig viele Werte zu überprüfen - dazu gehen wir nach demselben Muster vor und verwenden die kombinierten Differenzen zwischen den gewünschten Werten und den tatsächlichen Werten.

Schwellenwerte

Warten Sie... Sie wollen nicht wirklich alles anzeigen, das auch nur im Entferntesten Ähnlichkeit mit den gewünschten Werten hat. Diese Abfragen unterscheiden sich von den bisher für Suchen verwendeten Abfragen, weil sie alle Datensätze in der durchsuchten Tabelle zurückgeben. Deshalb sollten Sie eine Begrenzung für die Anzahl der angezeigten Datensätze einfügen. Die Datensätze am Ende der Liste haben für den Benutzer vermutlich nur noch wenig Bedeutung.

Es gibt zwei Möglichkeiten, die beschränken, was der Benutzer bei der Ausführung der Abfrage sieht - entweder eine Beschränkung der zurückgegebenen Datensätze oder die Formulierung einer Obergrenze für die Ergebnisse.

Die Beschränkung der Ergebnisse auf eine bestimmte Anzahl an Datensätzen ist normalerweise ein guter Ansatz, aber der Benutzer hat auf diese Weise wenig Kontrolle darüber, was er sieht. Angenommen, Sie wollen eine Top-10-Liste für den Benutzer anlegen, sodass er die ersten 10 Übereinstimmungen erhält. Leider macht das wenig Sinn für den Benutzer, wenn die ersten 50 Datensätze perfekte Übereinstimmungen darstellen. Ihm fehlen dann 40 Ergebnisse, die seine Abfrage genauso gut erfüllt haben wie die ersten 10. Die Beschränkung der Ergebnisse auf einen Wertebereich ist dennoch häufig mehr als ausreichend für die Aufgabenstellung und kann durch das UltraDev-Verhalten Wiederholter Bereich einfach realisiert werden.

Ein komplexerer Ansatz, die angezeigten Ergebnisse zu beschränken, ist die Vorgabe eines Schwellwerts für die Suche, wie beispielsweise eine Obergrenze für den Fehlerbetrag, der für die zurückgegebenen Datensätze vorhanden sein darf. Angenommen, wir suchen nach einem kombinierten Fehler kleiner 20, dann könnten wir die letzte Abfrage mithilfe einer WHERE-Klausel wie folgt abändern:

mysql> SELECT *,(ABS(8-a)+ABS(12-b)) as 'difference' 
FROM tblTwoNumber WHERE (ABS(8-a)+ABS(12-b))<20 ORDER BY difference;
+------+------+------------+
| a | b | difference |
+------+------+------------+
| 6 | 6 | 8 |
| 10 | 5 | 9 |
| 3 | 7 | 10 |
| 9 | 1 | 12 |
| 2 | 5 | 13 |
| 2 | 5 | 13 |
| 11 | 2 | 13 |
| 1 | 2 | 17 |
| 20 | 5 | 19 |
+------+------+------------+
9 rows in set (0.00 sec)

Die Redundanz der Differenzberechnung in den Klauseln ORDER BY und WHERE ist in MySQL zwingend erforderlich. Ihr Datenbankserver bietet möglicherweise eine effizientere Methode, die Abfrage auszuführen, wobei der Fehlerwert nicht zweimal berechnet wird.

Durch die Vorgabe eines Schwellenwerts wird die Anzahl der dem Benutzer angezeigten Ergebnisse nicht beschränkt - statt dessen wird damit eine bestimmte Qualität der Antworten erzwungen.

Wenn Sie dem Benutzer erlauben, einen Schwellenwert anzugeben, wollen Sie die Auswahl sicherlich benutzerfreundlich gestalten. Die Aufforderung »Geben Sie einen Schwellenwert ein« ist dabei nicht besonders nett. Eine Lösung für die Praxis würde beispielsweise einen Prozentsatz der erlaubten Fehler verwenden, den der Benutzer festlegen kann. Damit stellt sich jedoch die Frage, wie der Fehlerbetrag als Prozentwert dargestellt werden kann.

Für jede der Fragen zur Definition Ihrer Abfrage addieren Sie den Maximalfehlerbetrag zwischen der ersten und der letzten Antwort. Angenommen, wir haben die beiden folgenden Fragen:

Wie mögen Sie Ihren Toast?

  1. Golden
  2. Braun
  3. Dunkelbraun
  4. Verbrannt

und

Essen Sie Eier?

  1. Nie
  2. Einmal in der Woche
  3. Täglich

Wir haben zwei Fragen, wobei es auf die erste vier mögliche Antworten gibt, auf die zweite drei.

Dieses gesamte Abfragesystem basiert darauf, dass Antworten bereitgestellt werden, die von einem Ende des Spektrums zum anderen reichen. Auf diese Weise können wir abschätzen, wie weit entfernt von einer gewünschten Antwort wir uns befinden. Außerdem erhalten die Antwortlisten damit eine strenge Reihenfolge. Jeder möglichen Option muss ein Wert folgen, der nur eine leichte Änderung gegenüber der naheliegendsten Variante darstellt. Für die Toastliste können wir beispielsweise nicht »Verbrannt« als Nummer 2 darstellen. Damit würde impliziert, dass verbrannter Toast naheliegend zu goldenem Toast ist - und das ist definitiv nicht der Fall.

Das Worst-Case-Szenario für die erste Frage ist, dass der Benutzer eine Übereinstimmung für die erste Antwort »Golden« sucht, die mit der Antwort »Verbrannt« verglichen wird. Der Abstand zwischen diesen beiden Optionen beträgt 3 - das ist der maximale Fehlerbetrag, der in der ersten Frage möglich ist.

Für die zweite Aufgabenstellung ist der maximale Fehlerbetrag ebenfalls die Differenz zwischen der ersten und der letzten Antwort - also 2.

Durch die Kombination der beiden erhalten wir insgesamt einen möglichen Fehler von 5 - 100% Fehler ist 5, 50% Fehler ist 2,5, 0% ist 0 usw. Nachdem wir diese Werte berechnet haben, können Sie dem Benutzer ein Popup-Menü bereitstellen, das einen Prozentwert und einen zugehörigen Schwellenwert anbietet - womit die Anzahl der zurückgegebenen Werte bestimmt werden kann.

Wenn Sie sich fragen, ob Sie eine prozentuale Übereinstimmung statt eines prozentualen Fehlers angeben können: natürlich! Durch die Invertierung der Werte erhalten Sie genau, wonach Sie suchen. Wenn wir beispielsweise für das oben beschriebene Szenario eine Übereinstimmung von 100 % fordern, dann geben Sie den Schwellenwert 0 an. Wenn Sie dagegen 0 % Zuverlässigkeit brauchen, verwenden Sie als Schwellenwert den Maximalbetrag möglicher Fehler - in diesem Fall 5.

Gewichtung von Antworten

Ein weiteres praktisches Werkzeug für unser Abfragesystem ist eine Methode zur Gewichtung von Antworten - also ob sie eine größere Bedeutung besitzen als andere. Damit wird den Antworten auf eine Frage eine höhere Bedeutung zugeordnet als den Antworten auf andere Fragen oder anderen Optionen innerhalb einer Antwort. Angenommen, wir haben eine Frage, die nach jemandem sucht, der in Ihrer Nähe wohnt:

Wo wohnen Sie?

  1. Ohio
  2. Kalifornien
  3. Jupiter

Diese Werte stehen in keiner engen Beziehung zueinander, aber wir haben keine anderen Optionen. Statt sie sequentiell zu nummerieren, können wir eine »Strafe« für alles andere als die direkte Übereinstimmung einführen, indem wir die Optionen einfach neu nummerieren:

1. Ohio

10. Kalifornien

3000. Jupiter

Diese Nummerierung drückt die Beziehung zwischen den Orten aus. Ohio und Kalifornien sind zwar weit voneinander entfernt, aber der Jupiter liegt irgendwo im Weltraum. Anhand dieser Nummerierung würde für eine Suche nach jemandem, der in Ohio oder Kalifornien wohnt, ziemlich sicher die Antwort »Jupiter« ganz unten in der Antwortliste erscheinen.

Das Abfragesystem, das wir auf den letzten Seiten entwickelt haben, kann auch mit sehr viel Fantasie nicht als der Weisheit letzter Schluss in Bezug auf Suchtechnologien betrachtet werden. Es handelt sich dabei nur um eine Vorlage und einen Ausgangspunkt für die Entwicklung eigener komplexer Abfragesysteme.

Sie können jedoch zusätzliche Bedingungen und andere Funktionen in das SQL einfügen, um Ihren Anforderungen gerecht zu werden. Sie wollen schließlich, dass Ihr Datenbankserver so viel Arbeit wie möglich für Sie erledigt. Je mehr benutzerdefinierte Programmierung Sie vermeiden können, desto besser.

Aber wie können wir das alles in einer Site nutzen? Wir entwickeln hier eine »Reisebegleiter«-Site, um anderen zu helfen, Reisebegleiter und Autobesitzer in einer fiktiven Stadt zusammenzubringen.

18.2 Die Sitemap

In unserem heutigen Projekt wollen wir eine Site entwickeln, die unser zuvor entwickeltes komplexes Abfragesystem nutzt, um Menschen als Reisepartner zusammenzubringen. Wir werden dafür ein sehr einfaches Layout verwenden - ein Anmeldungs- und Registrierungssystem sowie einen Abfrage/Ergebnisbildschirm.

Die Bildschirme für die Anmeldung und die Registrierung sind denen aus dem Projekt in Kapitel 17 sehr ähnlich, außer dass sie ein für die Benutzer-Authentifizierung eingebautes Serververhalten verwenden. Außerdem hat der Benutzer die Möglichkeit, seine Einstellungen jederzeit zurückzusetzen.

Die restliche Site besteht aus einem Bildschirm für die Abfrage und einem für die Anzeige der übereinstimmenden Ergebnisse. Abbildung 18.1 zeigt eine Darstellung der Sitemap.

Abbildung 18.1:  Dieses Kapitel demonstriert die Konzepte einer Fuzzy-Suchmaschine.

18.3 Die Tabellendefinitionen

Die Tabellen für diese Site sind wirklich einfach. Ein Großteil der benötigten Informationen wird in einer einzigen Tabelle abgelegt. Eine zweite Tabelle bringt die Antworten auf die Frage in eine Beziehung zu den Werten, mit denen der Fehler zwischen den verschiedenen Profilen berechnet wird.

Für dieses Projekt wurde mehr Theorie besprochen als für die anderen, deshalb werden wir weniger Zeit für seine Entwicklung benötigen. Als Erstes brauchen wir eine Tabelle mit Benutzerinformationen (UserInfo) - ähnlich denen, die wir bereits verwendet haben. Diese Tabelle enthält den Benutzernamen, das Kennwort und alle Einstellungen. Aber welche Einstellungen gibt es?

Die Tabelle UserInfo

Die Einstellungen eines Benutzers werden in einem Profil implementiert, das die anderen Benutzer durchsuchen können, und das er nutzen kann, um das Suchsystem zu »füttern«, sodass er nicht alle Optionen manuell eingeben muss.

Um die Einstellungen einzurichten, müssen wir festlegen, wie viele Fragen es geben soll. Die Benutzertabelle enthält für jede Frage ein Feld - dieses Feld enthält eine Antwort-ID, mit deren Hilfe der Wert für diese Antwort gesucht wird.

Angenommen, wir haben für die Abfragen fünf Fragen. Außerdem nehmen wir die E-Mail-Adresse des Benutzers auf, sodass andere Menschen Kontakt zu möglichen Übereinstimmungen (also den Reisebegleitungen) aufnehmen können.

create table tblUserInfo (
userID int not null auto_increment,
username varchar(50) not null,
password varchar(50) not null,
email varchar(200),
fullname varchar(200),
q1Response int,
q2Response int,
q3Response int,
q4Response int,
q5Response int,
primary key (userID)
);

Gestern haben wir die Einstellungen des Benutzers in einer separaten Tabelle vorgehalten, sodass sie für zukünftige Anforderungen besser erweitert werden können. Das ist praktisch, wenn Sie Wert darauf legen, dass Ihr System einfach erweitert werden kann, aber UltraDev ist nicht in der Lage, eine benutzerfreundliche Bedienung zu realisieren. Weil jeweils nur ein einziger Datensatz gleichzeitig eingefügt werden kann, ist die einzige Methode, alle Einstellungen auf einem Bildschirm gleichzeitig anzuzeigen bzw. zu setzen, sie alle in einen einzigen Datensatz aufzunehmen.

In dem heutigen Projekt wird das eingebaute Serververhalten Benutzerauthentifizierung von UltraDev verwendet. Dieses Verhalten implementiert das Feld mit dem Benutzernamen als Primärschlüssel und bietet anderweitig nur sehr wenig Flexibilität. Ich bevorzuge das bisher verwendete Benutzer-ID-Schema, weil es ermöglicht, Systeme zu entwickeln, in denen der Benutzer seinen Namen ändern kann (da es sich dabei nicht um den Primärschlüssel handelt). Weil wir aber in diesem Projekt den Benutzer zwingen werden, einen eindeutigen Benutzernamen zu wählen, können wir davon ausgehen, dass das Benutzernamenfeld in der Datenbank eindeutig ist, sodass wir das Authentifizierungssystem für unsere existierende Tabellendefinition verwenden können.

Wenn Sie ein System entwickeln wollen, in dem der Benutzer seinen Namen ändern kann, sind die eingebauten Authentifizierungs-Verhalten nicht geeignet. Sie müssen ein eigenes System einrichten, wie in Kapitel 17 beschrieben. Das gilt auch für die Benutzer von UltraDev 1.0, die keinen Zugriff auf die neuen Authentifizierungsverhalten haben.

Die Optionstabelle

Damit das Suchformular anhand der Benutzereinstellungen mit Standardwerten gefüllt werden kann, müssen wir mehr als nur den Wert der von ihm ausgewählten Antwort speichern. Deshalb brauchen wir eine zweite Tabelle, in der alle Informationen über eine Antwort (ihren Titel, ihren Wert und ihre ID) abgelegt werden. Die Antwort-ID wird in den Benutzereinstellungen gespeichert und verweist zurück auf die Optionen.

Die Tabelle könnte wie folgt aussehen:

create table tblOption (
optionID int,
optionName varchar(200),
optionValue int,
primary key (optionID)
);

Leider funktioniert das nicht in Kombination mit der UserInfo-Tabelle, die wir eingerichtet haben. Das liegt daran, dass wir mehrere Optionen zu mehreren Feldern in der Tabelle tblUserInfo in Relation bringen müssen. Diese n:n-Beziehung ist in unserem System nicht möglich. Um es zu umgehen, legen wir eine separate Optionstabelle für jede der Fragen an - dann kann jede Frage in einer separaten Tabelle zugeordnet werden und das Problem ist gelöst. Wir können die Optionstabelle also wie folgt kürzen:

create table tblOption1 (
optionName1 varchar(200),
optionValue1 int not null,
primary key (optionValue1)
);

Legen Sie vier weitere Versionen der Tabelle an, tblOption2 bis tblOption5, wobei auch die Felder entsprechend nummeriert werden müssen. Wir nutzen diese Tabellen, um dynamisch Popup-Menüs für die einzelnen Fragen der Abfrage zu erzeugen.

Alle Felder der fünf Optionstabellen müssen eindeutig benannt werden (optionName1 usw.), damit sie von den Feldern der anderen Optionstabellen unterschieden werden können. Gegen Ende des Projekts werden Sie alle fünf Tabellen gleichzeitig abfragen und brauchen deshalb Zugriff auf alle fünf Werte der Optionsnamen.

Abbildung 18.2 zeigt die endgültige Datenbankstruktur.

Abbildung 18.2:  Mithilfe von zwei Tabellen werden Informationen gesammelt und Werte in eine Rangfolge gebracht.

Beispieldaten einfügen

Im nächsten Schritt werden die Optionen und die Beispieldaten eingerichtet. Bevor die Optionsdaten gespeichert werden können, müssen wir die Fragen definieren, die mit diesen Optionen beantwortet werden. Diese Site sucht Mitfahrgelegenheiten und Reisbegleitungen, deshalb versuchen wir, plausible Fragen und Optionen bereitzustellen. Auf einer echten Site wäre die Liste mit den Fragen und Optionen natürlich sehr viel umfangreicher, aber für unsere Zwecke soll es ausreichend sein:

  1. In welche Richtung wollen Sie reisen?
  2. 315 Süd
  3. 670 Süd
  4. 270 Südwest
  5. 315 Nord
  6. 670 Nord
  7. 270 Nordost

Beachten Sie, dass dazwischen Nummern übersprungen wurden. Auf diese Weise können die Fragen gewichtet werden, weil die Reiseroute relativ wichtig ist.

  1. Darf Ihr Begleiter rauchen?
  2. Nichtraucher
  3. Leichter Raucher
  4. Normaler Raucher
  5. Füll das Auto mit Rauch!
  6. Welche Transportmittel bevorzugen Sie?
  7. Motorrad mit Beiwagen
  8. Kombi
  9. Sportwagen
  10. Van
  11. Wann wollen Sie das Ziel erreichen?
  12. 8:00
  13. 8:30
  14. 9:00
  15. 9:30
  16. Wann wollen Sie wieder nach Hause fahren?
  17. 16:00
  18. 16:30
  19. 17:00
  20. 17:30

Das sind keine besonders interessanten Fragestellungen, aber sie sind ausreichend. Fügen Sie jetzt Optionen in die Optionstabelle ein. Jede Option sollte einer bestimmten Frage zugeordnet werden und eine eindeutige Options-ID besitzen, ebenso wie den numerischen Wert, anhand dessen berechnet wird, wie genau diese Antwort »trifft«.

INSERT INTO tblOption1 VALUES ('315 Süd',1);
INSERT INTO tblOption1 VALUES ('670 Süd',2);
INSERT INTO tblOption1 VALUES ('270 Südwest',3);
INSERT INTO tblOption1 VALUES ('315 Nord',10);
INSERT INTO tblOption1 VALUES ('670 Nord',11);
INSERT INTO tblOption1 VALUES ('270 Nordost',12);

INSERT INTO tblOption2 VALUES ('Nichtraucher',1);
INSERT INTO tblOption2 VALUES ('Leichter Raucher',2);
INSERT INTO tblOption2 VALUES ('Normaler Raucher',3);
INSERT INTO tblOption2 VALUES ('Füllt das Auto mit Rauch!',4);

INSERT INTO tblOption3 VALUES ('Motorrad mit Beiwagen',1);
INSERT INTO tblOption3 VALUES ('Kombi',2);
INSERT INTO tblOption3 VALUES ('Sportwagen',3);
INSERT INTO tblOption3 VALUES ('Van',4);

INSERT INTO tblOption4 VALUES ('8:00',1);
INSERT INTO tblOption4 VALUES ('8:30',2);
INSERT INTO tblOption4 VALUES ('9:00',3);
INSERT INTO tblOption4 VALUES ('9:30',4);

INSERT INTO tblOption5 VALUES ('16:00',1);
INSERT INTO tblOption5 VALUES ('16:30',2);
INSERT INTO tblOption5 VALUES ('17:00',3);
INSERT INTO tblOption5 VALUES ('17:30',4);

Jetzt tragen wir ein paar entsprechende Datensätze in die Benutzertabelle ein:

INSERT INTO tblUserInfo VALUES ('','jray','johnpass','jray@poisontooth.com',
'John Ray',2,3,1,1,2);
INSERT INTO tblUserInfo VALUES ('','eking','hairy','eking@poisontooth.com',
'Elvis King',1,4,4,3,3);
INSERT INTO tblUserInfo VALUES ('','ptoe','remote','ptoe@poisontooth.com',
'Paul Toenail',3,1,1,1,2);
INSERT INTO tblUserInfo VALUES ('','jlamp','monitor','jlamp@poisontooth.com',
'Jeff Lamppost',2,2,3,1,3);
INSERT INTO tblUserInfo VALUES ('','tchee','recliner','tchee@poisontooth.com',
'Tuffy Cheese',11,3,2,1,2);
INSERT INTO tblUserInfo VALUES
=>;('','mcliff','pomeranian','mcliff@poisontooth.com',
'Marty Cliffton',12,3,4,1,2);
INSERT INTO tblUserInfo VALUES ('','graey','kitty','graey@poisontooth.com',
'Graeyling Smetana',10,3,4,3,2);
INSERT INTO tblUserInfo VALUES ('','jvuj','johnpass','jvuj@poisontooth.com',
'Jules Vuje',2,4,2,1,1);
INSERT INTO tblUserInfo VALUES ('','lhans','computer','lhans@poisontooth.com',
'Leftie Hans',1,2,4,4,2);
INSERT INTO tblUserInfo VALUES ('','jbow','dasboot','jbow@poisontooth.com',
'Jim Bow',12,1,2,3,2);

Das ist ein guter Ausgangspunkt und wir haben alles, was wir brauchen, um das System aufzubauen und zu testen.

18.4 Das Anmeldesystem

Das Anmeldesystem ist nahezu identisch mit dem System, das wir gestern entwickelt haben. Vier Bildschirme sammeln die Systemdaten, speichern sie und erlauben dem Benutzer dann, sich anzumelden, indem eine Sitzungsvariable erzeugt wird. Ein weiterer Bildschirm, update, erlaubt dem Benutzer, sein Profil zu aktualisieren.

Registrierung und Ausgangsprofil

Ein Besucher, der die Site zum ersten Mal besucht, will sich beim System registrieren und sein Profil einrichten - deshalb soll er als Erstes zur Registrierung gelangen.

Der Bildschirm für die Registrierung verhält sich ganz ähnlich dem gestern gezeigten - er besteht aus einem kleinen Formular, das den Benutzer auffordert, einen neuen Benutzernamen einzugeben, und das diesen zur Verarbeitung weitergibt. In diesem Kapitel verwenden wir jedoch die eingebauten Authentifizierungsmethoden. Falls Sie Kapitel 17 nicht gelesen haben - im ersten Schritt haben wir sichergestellt, dass der zu registrierende Benutzername eindeutig war. Falls er nicht eindeutig ist, muss der Benutzer aufgefordert werden, einen anderen Benutzernamen einzugeben. Dieser gesamte Prozess wird jetzt durch ein Serververhalten automatisiert.

Zunächst entwerfen wir ein Registrierungsformular, das Felder für den Benutzernamen, das Kennwort und die E-Mail-Adresse enthält - die grundlegenden Informationen, die Sie sammeln wollen.

Weil das Benutzerprofil Daten enthalten soll, bevor ihm eine Suche ermöglicht wird, werden als Nächstes fünf verborgene Felder angelegt, die dieses Standardprofil aufnehmen. Diese Felder werden mit q1Response bis q5Response benannt und enthalten den Standardwert 1 (oder welchen Optionswert Sie als Standardwert vorgeben wollen). Es handelt sich dabei nicht um dynamische Felder; es sind nur fünf im Code festgeschriebene Felder, die einen einzigen Wert enthalten.

Abbildung 18.3 zeigt das vollständige Dokument register.asp.

Abbildung 18.3:  Die erste Registrierungsseite überprüft den Benutzernamen und speichert die Registrierungsinformation, falls sie korrekt war.

Schließlich brauchen Sie noch zwei Serververhalten für die Registrierungsseite, um sicherzustellen, dass der Benutzername eindeutig ist, und dann die Registrierungsinformation in die Tabelle tblUserInfo einzutragen. Überraschenderweise fordert UltraDev, dass Sie das Verhalten Datensatz einfügen hinzufügen, bevor Sie das Verhalten zur Überprüfung des Benutzernamens einfügen.

Jetzt fügen wir das Einfügen-Verhalten hinzu:

  1. Öffnen Sie die Palette Serververhalten.
  2. Klicken Sie auf das Plussymbol (+), um ein neues Datensatz einfügen-Verhalten einzufügen.
  3. Wählen Sie die entsprechende Verbindung und stimmen Sie die Formularelemente mit den Feldern in tblUserInfo ab, wie in Abbildung 18.4 gezeigt.

    Abbildung 18.4:  Stellen Sie sicher, dass die Felder aus der Registrierungsseite mit den Feldern der Tabelle tblUserInfo übereinstimmen.

  4. Nachdem Sie Werte eingefügt haben, konfigurieren Sie das Verhalten, um den Benutzer zur Anmeldeseite (login.asp) weiterzuleiten.

Im letzten Schritt für diese Seite wird das Verhalten Neuen Benutzernamen überprüfen eingefügt, um sicherzustellen, dass ein neuer Benutzername in die Datenbank eingetragen wird:

  1. Öffnen Sie die Palette Serververhalten, falls diese noch nicht geöffnet ist.
  2. Klicken Sie auf das Plussymbol (+), und wählen Sie im Untermenü Benutzerauthentifizierung den Eintrag Neuen Benutzernamen überprüfen.
  3. Konfigurieren Sie das Verhalten nach Aufforderung so, dass es das Feld mit dem Benutzernamen auf Eindeutigkeit überprüft.
  4. Falls der Benutzername bereits existiert, soll das Verhalten auf eine Seite führen, die dem Benutzer eine Fehlermeldung anzeigt, wie beispielsweise duplicate.asp. Ihr Dialog sollte jetzt etwa aussehen wie in Abbildung 18.5 gezeigt.
  5. Klicken Sie auf OK, nachdem Sie fertig sind.

    Abbildung 18.5:  Das Feld Benutzername sollte auf Eindeutigkeit überprüft werden. Ist es nicht eindeutig, soll der Benutzer zu duplicate.asp umgeleitet werden.

Wie im Beispiel aus Kapitel 17 bereits gezeigt, müssen wir jetzt noch die Meldung bereitstellen, die den Benutzer informiert, falls der gewählte Benutzername bereits im System existiert. Weil die UltraDev-Verhalten den Benutzer automatisch auf eine neue Seite umleiten, falls ein Duplikat vorhanden ist, brauchen Sie nur eine entsprechende Meldung auf der Seite duplicate.asp sowie einen Link zurück zum Registrierungsbildschirm bereitzustellen, sodass der Benutzer einen erneuten Versuch unternehmen kann. Abbildung 18.6 zeigt ein Beispiel dafür, wie Ihr Bildschirm Bereits vorhandener Benutzername aussehen könnte.

Abbildung 18.6:  Informieren Sie den Benutzer, falls er einen Namen auswählt, der im System bereits vorhanden ist.

18.5 Anmeldebildschirme

Ähnlich dem Registrierungsprozess sind auch für die Anmeldung am System zwei Bildschirme erforderlich - einer, der die Benutzerinformationen sammelt und überprüft, und ein weiterer, der eine Meldung anzeigt, falls eine fehlerhafte Anmeldung vorgenommen wurde. Wie gestern bereits gezeigt, verwenden wir eine Sitzungsvariable, die den Benutzer »begleitet«, nachdem er sich angemeldet hat. Wir wollen diese Variable jedoch nicht manuell setzen! Das Authentifizierungssystem von UltraDev setzt intern eine Sitzungsvariable mit dem aktuellen Benutzernamen des Benutzers - wir können diese Variable benutzen, statt den Code zu bearbeiten!

Die UltraDev-Verhalten für die Benutzerauthentifizierung speichern den Benutzernamen einer Person mit gültiger Anmeldung in einer Sitzungsvariablen. Das Speichern der Benutzer-ID wäre sinnvoller, weil man damit einem Benutzer erlauben kann, später seinen Anmeldenamen zu ändern (Primärschlüssel können nicht geändert werden!) Weil wir unter der Annahme arbeiten, dass alle Benutzernamen eindeutig sind, gibt es kein Problem.

Der erste Anmeldebildschirm (login.asp) macht genau dasselbe wie der erste Registrierungsbildschirm. Er sammelt Informationen (in diesem Fall den Benutzernamen und das Kennwort) und analysiert diese Daten mithilfe eines Serververhaltens. Anschließend leitet er den Benutzer abhängig von dem Ergebnis der Eingabe entsprechend weiter.

Entwerfen Sie die Anmeldeseite ganz nach Belieben. Die einzige Bedingung ist, dass sie Felder für den Benutzernamen und ein Kennwort sowie eine Senden-Schaltfläche bereitstellt. Abbildung 18.7 zeigt ein Beispiel für diesen Bildschirm.

Abbildung 18.7:  Der Anmeldebildschirm sollte Felder für den Benutzernamen und das Kennwort enthalten, deren Inhalt vom Verhalten Benutzerauthentifizierung  verarbeitet wird.

Nachdem Sie das Formular fertig haben, öffnen Sie die Palette Serververhalten und fügen das Verhalten Benutzer anmelden ein, um die Überprüfung des Benutzernamens und des Kennworts vorzunehmen.

  1. Klicken Sie in der Palette Serververhalten auf die Schaltfläche mit dem Plussymbol (+).
  2. Wählen Sie im Untermenü Benutzerauthentifizierung den Eintrag Benutzer anmelden.
  3. Wählen Sie das Formular aus, das die Anmeldeinformation übergibt, ebenso wie die Felder für den Benutzernamen und das Kennwort im Formular.
  4. Legen Sie die Datenbankverbindung fest und wählen Sie die Tabelle tblUserInfo sowie die Spalten username/password aus dieser Datenbanktabelle.
  5. Bei einer erfolgreichen Anmeldung soll der Benutzer auf die Seite search.asp weitergeleitet werden, sodass er anfangen kann, das System zu durchsuchen. Wenn Sie möchten, können Sie die Option Zum vorherigen URL gehen (falls vorhanden) verwenden.
  6. Falls eine Anmeldung fehlschlägt, wird der Benutzer auf die Seite badlogin.asp weitergeleitet.
  7. Stellen Sie sicher, dass der Zugriff durch einen Benutzernamen und ein Kennwort geschützt wird.
  8. Nachdem Sie fertig sind, sollte Ihr Formular für die Benutzeranmeldung ähnlich wie in Abbildung 18.8 gezeigt aussehen. Klicken Sie auf OK, wenn Sie mit Ihrer Arbeit zufrieden sind.

    Abbildung 18.8:  Wählen Sie die Felder username/password sowohl aus dem Anmeldeformular als auch aus der Datenbank.

Wie bei dem Registrierungssystem können Sie jetzt Ihre Seite badlogin.asp anlegen, auf die der Benutzer gelangt, wenn er nicht die richtigen Informationen eingegeben hat. Sie könnten beispielsweise auch Links auf die Anmelde- und Registrierungsseiten für diesen Bildschirm bereitstellen.

Ein Beispiel für die Seite badlogin.asp sehen Sie in Abbildung 18.9.

Abbildung 18.9:  Die zweite Seite des Anmeldesystems sollte eine Fehlermeldung für eine ungültige Kombination aus Benutzername und Kennwort anzeigen.

Damit sind wir mit den Anmelde- und Registrierungssystemen fertig. Jetzt geht es mit dem wirklich Spannenden weiter - der Entwicklung der Seite für die Profilverwaltung und die Suchmaschine.

18.6 Die Seite für die Profilaktualisierung

Die Seite für die Profilaktualisierung ist die schwierigste Seite des heutigen Projekts - nicht weil die Entwicklung so schwierig ist oder weil sie so komplexe Verhalten benötigt, sondern weil die Codierung so müßig ist. Dieses Seite ermöglicht dem Benutzer, die Informationen in seinem Profil zu aktualisieren - Kennwort, E-Mail-Adresse und die Antworten auf die fünf Fragen.

Wir richten zunächst ein Formular mit den fünf Fragen und fünf entsprechenden Popup- Menüs, q1Response bis q5Response, ein - ebenso wie Felder für die Aktualisierung der E-Mail-Adresse und des Kennworts. Schließlich fügen wir noch eine Senden-Schaltfläche ein, womit der Benutzer die Änderungen speichern kann, die er an seinem Profil vorgenommen hat.

Alle Felder müssen zu den Daten in der Datenbank gebunden werden - und hier wird es aufwändig. Zur Steuerung der Seite sind insgesamt sechs Datensatzgruppen erforderlich. Die erste Datensatzgruppe holt die Benutzerinformationen aus der Tabelle tblUserInfo. Sie wird benötigt, um die Felder für die E-Mail-Adresse und das Kennwort auszufüllen. Definieren Sie diese Abfrage, rsUserInfo, jetzt. Sie können sie als einfache Abfrage anlegen, die alle Felder in tblUserInfo auswählt - basierend auf einem Filter, wobei die Sitzungsvariable MM_Username gleich dem Benutzernamenfeld in der Datenbank sein muss.

Die Sitzungsvariable MM_Username wird automatisch gesetzt, wenn der Benutzer das Serververhalten Anmeldung erfolgreich abgeschlossen hat. Sie können sie wie selbst angelegte Sitzungsvariablen nutzen.

Jetzt binden Sie die E-Mail-Adresse zu dem Formularfeld für die E-Mail-Adresse, wobei Sie dieselbe Technik anwenden wie bereits beschrieben. Markieren Sie in der Entwurfsansicht die E-Mail-Adresse und wählen Sie dann in der Datensatzgruppe rsUserInfo das E-Mail-Feld. Setzen Sie den Bindetyp unten im Fenster für die Datenbindung auf input.value und klicken Sie auf die Schaltfläche Binden. Wenn Sie die alternative Methode zum Binden von Attributen vorziehen (nämlich über die Eigenschaftenpalette), verwenden Sie diese. Wiederholen Sie diese Prozedur für das Kennwortfeld.

Jetzt erstellen Sie fünf Datensatzgruppen, die die Optionen für die fünf Fragen enthalten. Definieren Sie diese Datensatzgruppen, rsOption1 bis rsOption5, als einfache Abfragen, die alles aus der Tabelle laden. Die Implementierung sollte nur ein paar Minuten dauern.

Nachdem Sie die fünf Options-Datensatzgruppen erstellt haben, müssen Sie sie zu den fünf Popup-Menüs im Dokumententwurfsfenster binden. Gehen Sie dabei nach Ihrer bevorzugten Bindemethode vor und binden Sie die Elementbeschriftung des Menüs zum Feld OptionName# und das Elementwertattribut des Menüs zum Feld OptionValue# in der Abfrage. Wiederholen Sie diese Prozedur für alle fünf Popup-Menüs. Als Ergebnis erhalten Sie fünf dynamische Menüs, die zu den entsprechenden Optionstabellen in der Datenbank gebunden sind. Abbildung 18.10 zeigt das Fenster für die korrekte Datenbindung von rsOption1 zu dem Popup-Menü.

Abbildung 18.10:  Ihre Optionsdatensätze müssen zu den Popup-Menüs gebunden werden.

Nachdem Sie die Bindungen für die Popup-Menüs angelegt haben, geht es von Neuem los. Um die Einträge anzuzeigen, die der Benutzer ausgewählt hat, müssen Sie das Elementauswahlattribut jedes der Optionsmenüs zum entsprechenden Antwortfeld in der Datensatzgruppe rsUserInfo binden. Beispielsweise sollte das Elementauswahlattribut des Popup-Menüs zum Feld q1Response in rsUserInfo gebunden werden usw. Damit wird sichergestellt, dass die Einträge, die der Benutzer in seinem Profil vorgenommen hat, als Standardauswahl in der Datenbank angezeigt werden.

Im letzten Schritt zur Einrichtung der Seite fügen Sie das Serververhalten zur Aktualisierung des Benutzerprofils ein, die erfolgt, nachdem er auf die Schaltfläche Senden geklickt hat. Fügen Sie dieses Verhalten der Seite profile.asp hinzu:

  1. Öffnen Sie die Palette Serververhalten.
  2. Klicken Sie auf das Plussymbol (+), um das Verhalten Datensatz aktualisieren einzufügen.
  3. Wählen Sie Ihren Verbindungstyp aus.
  4. Geben Sie tblUserInfo als die zu aktualisierende Tabelle an.
  5. Legen Sie den Benutzernamen als den eindeutigen Spaltenschlüssel fest. Unsere Standard-Datenbanktabellen verwenden zwar die Benutzer-ID als eindeutige Spalte, aber das hier verwendete Benutzerauthentifizierungssystem geht von eindeutigen Benutzernamen aus.
  6. Stellen Sie sicher, dass im Abschnitt Werte abrufen aus alle Werte übereinstimmen.
  7. Setzen Sie den Wert für die URL, die nachfolgend aufgesucht werden soll, auf search.asp.
  8. Klicken Sie auf OK.

Gut gemacht! Damit haben Sie die aufwändigste Seite für heute fertig! Meinen endgültigen Entwurf für die Profilseite sehen Sie in Abbildung 18.11.

Abbildung 18.11:  Eine einfache Seite, die aber unzählige Datensatzgruppen unterstützt.

Als letzten Teil der Site müssen Sie noch die Suchseite und das Ergebnisformular erstellen. Die Suchseite wird angelegt wie die Profilseite, aber ohne die zusätzlichen Felder für E-Mail, Kennwort und Benutzer-ID. Um sich Arbeit zu ersparen, legen Sie eine Kopie des Dokuments profile.asp an und legen diese unter dem Namen search.asp ab - damit haben Sie die Grundlage für die erste Suchseite.

18.7 Die Such- und Ergebnisseiten

Die Such- und Ergebnisseiten sind ganz einfach einzurichten - insbesondere, weil Sie schon so viel Arbeit auf der Profilseite geleistet haben. Legen Sie eine Kopie der Profilseite an, und speichern Sie sie unter dem Namen search.asp. Öffnen Sie diese neue Seite in der Entwurfsansicht. Weil Sie für die Suchseite die meisten Verhalten benötigten, die bereits auf der Profilseite implementiert wurden, brauchen wir nur die bereits existierende Seite entsprechend anzupassen.

Zunächst bereinigen Sie das Formular, sodass es nur noch die fünf Fragen und ihre Antworten enthält. Sie brauchen die verborgenen Felder für E-Mail-Adresse, Benutzername und Kennwort nicht mehr. Anschließend öffnen Sie die Palette Serververhalten und wählen das Verhalten Datensatz aktualisieren aus. Klicken Sie auf die Schaltfläche mit dem Minussymbol (-), um sie aus dem Projekt zu entfernen. Im Formular sollen keine verborgenen Felder mehr vorhanden sein - wenn Sie also noch welche sehen, hat UltraDev etwas vergessen - entfernen Sie sie einfach manuell.

Am besten verhindern Sie, dass sich UltraDev »verwirrt« fühlt, wenn Serververhalten entfernt werden, indem Sie alles, was möglich ist, aus den Paletten Datenbindungen und Serververhalten entfernen, bevor Sie das Dokument manuell bearbeiten. Und selbst dann wird es noch ein paar Ausreißer geben, die Ihrer speziellen Aufmerksamkeit bedürfen.

Jetzt markieren Sie das Formular im Dokument und setzen seine Aktion auf searchresults.asp. Außerdem können Sie einen Link wie beispielsweise Aktualisieren Sie Ihr Profil auf der Seite anlegen, der den Benutzer zu profile.asp führt, sodass er einfach auf seine Profilinformation zugreifen kann - weil die Sitzungsvariable MM_Username die Person identifiziert, die die Site verwendet, müssen der Seite keine weiteren Werte übergeben werden. Abbildung 18.12 zeigt die endgültige Suchseite.

Abbildung 18.12:  Der Suchbildschirm ist einfach nur eine abgespeckte Version der Seite für die Verwaltung des Benutzerprofils.

Nehmen Sie den letzten Bildschirm des Projekts in Angriff, searchresults.asp, indem Sie ein grundlegendes Layout für Ihre Ergebnisse entwickeln. Ich will beispielsweise alle Ergebnisse in einer Tabellenansicht anzeigen, deshalb habe ich eine Tabelle mit den folgenden Spalten angelegt:

Außerdem habe ich eine zweite Zeile der Tabelle leer gelassen, sodass ich die Suchergebnisse einfügen kann.

Nur eine einzige Abfrage in diesem Dokument kann die Suche vornehmen, aber sie ist wirklich elegant!

Erinnern Sie sich an das grundlegende Suchmuster, das wir heute entwickelt haben? Wir wollen die Absolutwerte der Differenzen zwischen der gesuchten Antwort und der tatsächlichen Antwort kombinieren. Das SQL für die Abfrage sieht wie folgt aus:

SELECT *,abs('varQ1'-tblOption1.OptionValue1)+abs('varQ2'-=>;tblOption2.OptionValue2)+
abs('varQ3'-tblOption3.OptionValue3)+abs('varQ4'-tblOption4.OptionValue4)+
abs('varQ5'-tblOption5.OptionValue5) as 'difference' FROM
tblUserInfo,tblOption1,tblOption2,tblOption3,tblOption4,tblOption5 WHERE
tblUserInfo.q1Response=tblOption1.OptionValue1
AND tblUserInfo.q2Response=tblOption2.OptionValue2
AND tblUserInfo.q3Response=tblOption3.OptionValue3
AND tblUserInfo.q4Response=tblOption4.OptionValue4
AND tblUserInfo.q5Response=tblOption5.OptionValue5 ORDER BY difference

Beachten Sie, dass eine WHERE-Klausel angelegt wurde, um die Antworten für alle UserInfo-Datensätze mit den entsprechenden Datensätzen in jeder der Optionstabellen zu vergleichen. Das ist zwingend erforderlich, damit wir das OptionName-Feld aus den tblOption-Tabellen nutzen können, um die Antwort anzuzeigen, die der Benutzer bei der Befragung gegeben hat.

Legen Sie die erweiterte Abfrage rsResults an, die dieses SQL beinhaltet. Außerdem müssen Sie die UltraDev-Variablen varQ1 bis varQ5 definieren und auf die Laufzeitwerte Request("q1Response") bis Request("q5Response") setzen. Überprüfen Sie unbedingt, ob die Abfrage korrekt definiert wurde, bevor Sie auf OK klicken. Klicken Sie auf die Schaltfläche Testen, um sicherzustellen, dass die Abfrage funktioniert.

Der Grund für diese Sicherheitsmaßnahmen ist, dass diese Abfrage nach ihrer Eingabe in UltraDev nicht mehr bearbeitet werden kann. Ältere Versionen von UltraDev (und auch die allerneueste) haben Probleme bei der Bearbeitung bestimmter Abfragen.

Vorausgesetzt, Ihre Version von UltraDev hat kein Problem mit der Abfrage, öffnen Sie das Fenster Datenbindungen und ziehen die gewünschten Felder in Ihre Entwurfsansicht.

Falls Sie alle dynamischen manuell in das Dokument einfügen müssen, ist das nicht ganz so schwierig, wie es sich anhört - nur ein bisschen aufwändiger, als Sie vielleicht hoffen.

Beispielsweise sehen Sie hier die Felder, die ich eingefügt haben, und den ASP-Code, den Sie brauchen, um sie anzuzeigen:

Um sie manuell einzugeben, wechseln Sie einfach von der Entwurfsansicht in die Codeansicht und geben alles ein, was zwischen den ASP-Tags <% und %> steht. Das ist alles! Nachdem Sie diesen Code in Ihr Dokument eingegeben haben, erscheinen sie als Serververhalten Dynamischer Text. Auf diese Weise können Sie damit arbeiten, als wären sie dem Entwurf automatisch hinzugefügt worden. Sie können sie an die entsprechenden Positionen in der Tabelle ziehen, um die endgültige Ansicht Ihrer Ergebnisse zu betrachten. Hoffentlich ist dieses Verschieben der Felder ausreichend und Sie müssen keine weiteren manuellen Aktionen vornehmen. Jedenfalls wissen Sie, was auf Sie zukommt!

Der letzte Schritt beim Anlegen des Bildschirms ist, die Zeile der dynamischen Daten auszuwählen, die Sie in das Dokument eingefügt haben, und das Serververhalten Bereich wiederholen hinzuzufügen, sodass mehrere Übereinstimmungen gezeigt werden. Wenn Sie wollen, können Sie alle Datensätze oder nur eine bestimmte Anzahl von Datensätzen anzeigen. Die Beschränkung der Antworten auf einen bestimmten Fehlerschwellenwert bleibt dem Leser als Übung überlassen, aber die Technik wurde bereits vorgestellt und ist nicht allzu kompliziert.

Gehen Sie jetzt auf Ihre Website und testen Sie ihre Funktionalität. Wenn alles korrekt funktioniert, erhalten Sie eine Ergebnisliste wie in Abbildung 18.14 gezeigt. Damit ist Ihre Site fertig.

Ein Schnelltest, der zeigt, dass alles funktioniert, ist, zu überprüfen, ob das Profil, unter dem Sie sich angemeldet haben, oben in den Suchergebnissen erscheint.

Abbildung 18.14:  Ihre dynamischen Suchergebnisse sollten wie hier gezeigt aussehen.

18.8 Schützen Sie Ihre Seiten

In einem letzten Schritt für Ihre Site könnten Sie die Seiten search.asp, searchresults.asp und profile.asp schützen. Schließlich sollten diese niemandem zur Verfügung stehen, der sich nicht beim System angemeldet hat. Weil es sich aber um keine Hochsicherheits-Website handelt, halten Sie das möglicherweise nicht für notwendig. Der Prozess ist jedoch ganz einfach.

  1. Öffnen Sie eine Seite, die Sie schützen wollen, wie beispielsweise profile.asp.
  2. Öffnen Sie die Palette Serververhalten.
  3. Klicken Sie auf das Plussymbol (+), und wählen Sie im Untermenü Benutzerauthentifizierung den Eintrag Zugriff auf die Seite beschränken.
  4. Konfigurieren Sie das Verhalten wie in Abbildung 18.15 gezeigt, um den Zugriff abhängig von Benutzername und Kennwort zu beschränken.
  5. Falls der Zugriff verweigert wird, leiten Sie den Benutzer auf die Seite login.asp weiter.
  6. Klicken Sie auf OK, nachdem Sie fertig sind.
  7. Wiederholen Sie diesen Prozess für alle Seiten, die Sie schützen wollen.

    Abbildung 18.15:  Schränken Sie den Zugriff für Seiten ein, die nicht berechtigte Benutzer nicht sehen sollen.

Wenn ein Benutzer versucht, auf eine geschützte Seite zuzugreifen, bevor er sich angemeldet hat, wird er unmittelbar auf die Seite login.asp weitergeleitet. Einfach, aber effektiv.

18.9 Zusammenfassung

Dieses Kapitel hat eines der leistungsfähigsten Werkzeuge vorgestellt, die Ihnen zur Verfügung stehen - das Authentifizierungssystem von UltraDev und Ihren Datenbankserver! Die Authentifizierungs-Verhalten von UltraDev beschleunigen die Entwicklung geschützter Seiten ganz wesentlich, während die Datenbank kleine Wunder möglich macht, indem komplexe SQL-Abfragen entwickelt werden.

Das hier entwickelte Abfragesystem ist praktisch, aber dennoch beschränkt, weil es alle Abfragen gleich gewichtet. Sie können den heute vorgestellten Prozess verfeinern, indem Sie den einzelnen Abfrageelementen Gewichtungen zuordnen. Außerdem können traditionelle Suchelemente verwendet werden, um die Datensatzgruppe auf Werte zu beschränken, die genaue Übereinstimmungen aufweisen. Statt beispielsweise alle Datensätze zu vergleichen, könnten Sie die Abfrage in der WHERE-Klausel abändern. Wenn Leute genaue Übereinstimmungen in Hinblick auf die Rauchgewohnheiten fordern, könnten diese aus der Absolutwertberechnung in den WHERE-Abschnitt des SQL verschoben werden.

Ich hoffe, Sie haben in diesem Kapitel neue Ideen gesammelt, um eigene Suchen entwickeln zu können. Zu leicht glaubt man, dass alle Abfragen nur Datensätze finden können, die abhängig von den in WHERE verwendeten Attributen gefiltert werden. Durch den Ausbruch aus dem traditionellen Abfragemuster ergeben sich Manipulationen Ihrer Website-Daten, die ein ganzes Universum komplexer Abfragen eröffnen.

18.10 Fragen und Antworten

Frage:
Wie viele Fragen kann diese Abfrage verarbeiten?

Antwort:
Sie könnten dieser Datensatzgruppe beliebig viele Fragen hinzufügen, die Ihr SQL-Server in einer einzigen Abfrage verarbeiten kann. Leider hat UltraDev immer wieder Probleme mit langen Abfragen. Die hier verwendete Abfrage hat beim ersten Versuch Probleme im SQL-Eingabefeld bereitet. Bleibt zu hoffen, dass das in zukünftigen Versionen von UltraDev nicht mehr der Fall sein wird.

Frage:
Für welche Arten von Applikationen kann dies genutzt werden?

Antwort:
Für alle. Dieses Kapitel hat die Suchtechnik genutzt, um Menschen miteinander zu vergleichen, aber Sie können sie wirklich für die unterschiedlichsten Situationen einsetzen. Beispielsweise könnten Sie Attributrangfolgen für Produkte in einem Einkaufskatalog bereitstellen, die abgefragt werden können. Überall wo eine Suche möglich ist, können Sie auch diese Techniken nutzen.

Frage:
Muss ich immer separate Optionstabellen anlegen?

Antwort:
Diese Tabellen werden nur benötigt, weil wir die Namen der Optionen anzeigen wollten, die die Benutzer ausgewählt haben. Falls diese Information nicht wichtig ist, können wir einfach auch nur die Tabelle tblUserInfo verwenden.

18.11 Workshop

Der Workshop dient dazu, den gelesenen Stoff mithilfe von gezielten Fragen und Übungen zu vertiefen. Die Antworten finden Sie in Anhang A, »Quiz-Antworten«.

Quiz

  1. Was ist der Absolutwert?
  2. Warum addieren wir die Absolutwerte?
  3. Wie können Abfrageelemente gewichtet werden?
  4. Was ist ein Schwellenwert?
  5. Warum ist es wichtig, die Master-Suchabfrage gleich beim ersten Mal völlig korrekt zu formulieren?

Übungen

  1. Fügen Sie der Suche einen Schwellenwert hinzu. Dazu müssen Sie den in der Abfrage möglichen Gesamtfehlerbetrag berechnen.
  2. Korrigieren Sie die Suche, sodass die Ergebnisse Ihren eigenen Datensatz nicht mehr enthalten. Dazu müssen Sie in die WHERE-Klausel Ihrer Suchabfrage eine Bedingung einfügen.
  3. Falls Sie ein Abenteurer sind, können Sie noch komplexere Suchmöglichkeiten einführen, indem Sie mithilfe von UNIONs mehrere SELECT-Anweisungen kombinieren. Auf diese Weise können Sie den Fehler in Kategorien einordnen und die Ergebnisse dieser Abfragen dann durch JOIN verknüpfen. Das könnte sinnvoller sein, als alle Fehler innerhalb einer einzigen Abfrage zu kombinieren.
  4. Um mehr Erfahrung mit dem Authentifizierungssystem zu sammeln, bearbeiten Sie das Projekt aus Kapitel 17, sodass es statt der manuell entwickelten Methode die eingebauten Verhalten benutzt.


vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbackKapitelanfangnächstes Kapitel


© Markt+Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH