[PHP/MySQL] Mehrere IDs in einem DB-Feld speichern und resourcensparend verarbeiten

mh1001

Lt. Commander
Registriert
Nov. 2003
Beiträge
2.039
Hallo zusammen,

wo ich nun wieder an der Arbeit meines CMSs bin, komme ich nun zu den etwas unangenehmerern Teilen, welche ich mir zum Ende aufgehoben habe. ;)
Aber lange Rede, kurzer Sinn - Zum Thema:

Ich besitze in einer MySQL-Datenbank zwei Tabellen, rbg_mitglieder und rbg_raenge, welche auf das Wichtigste gekürzt folgendermaßen aufgebaut sind:

Code:
rbg_raenge
 
id I--------I rang
--------------------
1	 Mitglied
2	 Administrator
3	 Manager
....
 
rbg_mitglieder:
 
id I--------I rang
--------------------
1	 [1]
2	 [1]
3	 [1] [2]
4	 [2]
5	 [1] [2] [3]
....

Nun werden, wie in dem oberen Codeabschnitt zu sehen ist, die Ränge im Tabellenfeld in der Form [$ID] gespeichert. Die Umwandlung einer solchen Rang-ID-Kette geschieht dann mit dieser Funktion.

PHP:
function id2rang($rang)
{
$abfrage = mysql_query("SELECT id, rang FROM rbg_raenge");
 
$raenge = array();
 
while($ds = mysql_fetch_object($abfrage))
{
	$raenge["[".$ds->id."]"] = $ds->rang;
}
 
$format = strtr($rang, " ", ", ");
$format = strtr($format, $raenge);
 
return $format;
}
Wobei es beim Schreiben einer solchen Rand-ID-Kette, welche vorher per Checkboxen als Array übergeben wurden, so aussieht:
PHP:
$rang = "[".maskieren(implode("] [", $HTTP_POST_VARS["rang"]))."]";

Nun aber genug des Codes und zu den Problemen:

Beim bearbeiten der Mitglieder, soll der Rang über Checkboxen, wie auch schon beim Hinzufügen diser, ausgewählt weden können.
Allerdings sollen dabei die bisher zugewiesenen Ränge schon vorselektiert sein. Der Code hierfür sieht folgendermaßen aus:

PHP:
<?php
require("../../daten/#administration_sessions.php");
 
include("../../daten/#funktionen.php");
include("../../daten/#konfiguration.php");
 
$mitgliedsauswahl = zahlcheck($HTTP_GET_VARS["mitgliedsauswahl"]);
 
$abfrage = mysql_query("SELECT nickname, name, status, rang, email, icq, aim, msn, yahoo, geburtsdatum, wohnort, squad, sonstiges FROM rbg_mitglieder WHERE id = '$mitgliedsauswahl'");
$ds = mysql_fetch_array($abfrage);
 
$abfrage2 = mysql_query("SELECT id, rang FROM rbg_raenge ORDER BY rang");
 
// ....
 
while($ds2 = mysql_fetch_object($abfrage2))
{
 
$rang = substr($ds2->rang, 1, -1)
?>
<input type="checkbox" name="rang[]" value="<?php echo $ds2->id; ?>" <?php if(array_search($rang, explode("] [", $ds["rang"])) != 0) { echo "selected"; } ?> > <?php echo $ds2->rang; ?><br>
<?php
}
?>
Leider läuft dies aber ins Leere und ich erhalte nur leere Checkboxen. Doch wo liegt der Fehler?

Aber nun auch davon genug und zu der Hauptfrage:

Wie speichere ich in "rbg_mitglieder" die Rang-IDs am besten ab, um Möglichst leicht auf die entsprechenden Ränge zugreifen zu können und eine gute Performance zu gewährleisten können, was bei letzterem ganz und gar nicht der Fall ist?

Ich habe auch schon in diversen Portal- und Forensystemen nach einer Lösung gesucht, doch trat weder in Mambo OS, phpBB, noch vBulletin eine ähnliche Handhabung auf.
Deswegen hoffe ich das ihr mir hierbei weiterhelfen könnt.

Vielen Dank im Vorraus

mh1001
 
Zuletzt bearbeitet:
Hi,

wenn ich Dich richtig verstanden habe, möchtest Du, dass die Mitglieder mehrere Ränge haben können, richtig? Für diesen Fall bieten sich eigentlich nur 2 Lösungen an.

1.) Wenn für das gesamte Projekt keine weiteren Ränge vorgesehen sind, speichere sie halt als TINYINT, also quasi BOOLEAN, direkt in rbg_mitglieder als separate Spalten.

2.) Diese Lösung ist etwas komplizierter aber dafür auch leichter erweiterbar. Erstelle eine Hilfstabelle, die die n:n-Beziehung darstellt. d.h. du speicherst darin lediglich die ID der Mitglieder und die der entsprechenden Ränge in einem jeweils eigenem Datensatz.

Code:
rbg_mitglieder_2_raenge

mtig_id I-------I rang_id
------------------------------
1                       1
2                       1
3                       1
3                       2
4                       1
5                       1
5                       2
5                       3
etc.

Damit umgehst du die aufwändige Aufarbeitung des Strings.

Ciao
 
Vielen Dank schon mal für deine Antwort. ;)

Leider sind beide Möglichkeiten nicht ganz das Wahre. ;)

Zu 1.) Da die Anzahl der verschiedenen Ränge jederzeit variabel sein muss ist dies leider nicht möglich. Vor allem wäre eine Tabelle mit etwa 50 TINYINT-Rang-Feldern auch nicht sonderlich Übersichtlich und die Performance würde auch wieder darunter leiden.

Zu 2.) Auch dies scheint leider nicht die optimale Lösung des Problems zu sein. Wenn ich nun nämlich zum Beispiel die Mitgliederliste nach dem Rang sortieren will oder nach Mitgliedern mit einem bestimmten Rang suchen will, bin ich wieder bei ähnlichen verschachtelten Abfragen und "Umformarbeiten". ;)

Trotzdem besten Dank für deine Vorschläge. :)

MfG mh1001
 
Zuletzt bearbeitet:
Ich weiß nicht, ob das ideal ist, aber ich realisiere das auf einer meiner Seiten über je eine Tabelle pro Rang

Also eine Tabelle "user" mit user_id und den ganzen anderen angaben, wie nick, adresse, etc.
und dann für jeden Rang eine extra Tabelle, wo ein Eintrag alleine bestehenden aus der id des users angelegt wird und so die verknüpfung herstellt.

Vorteil, du verstößt nicht gegen die 1. NF, hast also immer nur einen Wert in den Zellen der Tabelle stehen! Zudem Speicherst du nicht mehr wie nötig!

Dafür sind die Abfragen etwas komplexer, wobei ich nicht weiß wie der Aufwand von diesen ist:

"SELECT a.user_id, a.user_nick, [...] , b.id, c.id
FROM user a
LEFT JOIN user_adminflag b ON b.id = a.user_id
LEFT JOIN user_modflag c ON c.id = a.user_id
[...];


Wenn es da noch was einfacheres gibt, dann wäre ich jedenfalls auch daran interessiert, wobei das so eigentlich ganz gut funktioniert, da ich in der Regel nie alle Flags/Rechte gleichzeitig brauche...

MfG Meolus
 
Die Idee mit einer Tabelle für jeden Rang wäre zwar an sich nicht schlecht, jedoch bei mir leider nicht anzuwenden, da die Anzahl der Ränge zur Zeit zwar 9 beträgt, jedoch bald auf etwa 50 steigen wird, was der Datenbank wohl kaum mehr zumutbar wäre.

So wies es aussieht scheint es schwieriger als erwartet zu sein, einen vernünftigen Zwischenweg dabei zu finden. ;)

Trotzdem aber auch dir einen herzlichen Dank für deinen Tipp. ;)

MfG mh1001
 
Wie wäre es denn dann wirklich mit einem Mittelweg zwischen den beiden Vorschlägen?
In dem du die Einträge gruppierst und auf mehrere Tabellen verteilst! Ich denke, dass du ja nicht alle Einträge gleichhäufig oder für die gleichen Aufgaben benötigt, oder?
 
Mir ist auch nicht ganz klar: Willst Du tatsächlich, das ein Mitglied mehrere Ränge haben kann? Ich meine, das Wort Rang signalisiert eigentlich, dass man davon nur einen hat. Wenn dem jedoch so ist, musst Du entweder bei deiner Variante bleiben oder eben die 2. Version von mir verwenden, da es imho in einer relationalen Datenbank keinen anderen Weg gibt, eine n:n-Beziehung darzustellen.

Ciao
 
@Meolus

Mit einen Mittelweg hatte ich eher im Blick, die IDs zwar in einem Feld im Mittgliedsdatensatz zu, jedoch schon möglichst weit überarbeitet zu speichern, so dass möglichst wenig Verarbeitungsroutinen anfallen.
Sollte wirklich gar keine sinvolle Lösung zu finden sein, so könnte ich alternativ auch in der Tabelle zum Beispiel 5 Felder für je eine Rang-ID anlegen, wobei aber dann auch die Anzahl der maximal möglichen Ränge auf diese Anzahl beschränkt wäre, was aber im Normalfall auch genügen würden.

@S.Giny

Das mit mehreren Rängen kommt daher, dass es sich bei der Seite um die Website meines Clans handelt. Somit kann ein Mitglied zum Beispiel sowohl Leader im CS-Squad als auch zum Beispiel Mitglied im WC3-Squad sein. Somit müsste dieses Mitglied sowohl den Rangnamen "CS-Squad-Leader" als auch "WC3-Squad-Member" tragen. Leider kommt dies in der Praxis häufiger vor als erwartet, was mir als Folge auch dieses Problem einbringt.

MfG mh1001
 
Ähm, du könntest dass dann noch als Liste für jeden User implementieren. Ich weiß aktuell zwar nicht mehr wie das in MySQL genau geht, aber irgendwo habe ich wenigstens mal gelesen, wie man da binäre Bäume implementiert hat, also sollte eine Liste ja wohl einfach sein :D
 
Nochmals herzlichen Dank für die vielen Tipps. ;)

Ich habe nun etwas herumversucht und einige der vorgeschlagenen Möglichkeiten in verschiedenen Varianten versucht. Nach einigem Probieren kam ich dann zur Erkenntnis, dass die Variante mit einer Hilfstabelle für die Rangzuordungen performancemäßig am besten abschnitt. Somit werde ich vorerst wohl bei dieser Möglichkeit bleiben.

MfG mh1001

/Edit: Und wenn wir gerade schon beim Thema sind: Wie prüfe ich am besten ob ein Array nur numerische Werte enthält ohne dabei alle einzelnen Elemente über eine Schleife einzeln abzuarbeiten?
 
Zuletzt bearbeitet:
mh1001 schrieb:
/Edit: Und wenn wir gerade schon beim Thema sind: Wie prüfe ich am besten ob ein Array nur numerische Werte enthält ohne dabei alle einzelnen Elemente über eine Schleife einzeln abzuarbeiten?

Ich weiß zwar nicht wofür Du das brauchst, aber evtl. hilft Dir das hier weiter:

Klick mich, ich bin ein PHP-Manual Link


Ciao
 
Leider hat mir dein Link nicht weitergeholfen oder zumindest weiß ich nicht genau, wie ich diese Funktion für meinen Anwendungszweck "missbrauchen" könnte. ;)
Ich weiß zwar nicht wofür Du das brauchst [...]
Das Anwendungsgebiet ist eigentlich sehr einfach: Auf meiner Homepage kommen öfters Benutzereingaben vor, welche per POST-Variablen als Array übertragen werden sollen, jedoch nur Nummern enthalten sollen. Mit einer solchen Funktion soll die Manipulation solcher Daten verhindert werden. ;)

MfG mh1001
 
mh1001 schrieb:
Leider hat mir dein Link nicht weitergeholfen oder zumindest weiß ich nicht genau, wie ich diese Funktion für meinen Anwendungszweck "missbrauchen" könnte. ;)
Das Anwendungsgebiet ist eigentlich sehr einfach: Auf meiner Homepage kommen öfters Benutzereingaben vor, welche per POST-Variablen als Array übertragen werden sollen, jedoch nur Nummern enthalten sollen. Mit einer solchen Funktion soll die Manipulation solcher Daten verhindert werden. ;)
MfG mh1001

Hallo,

nun ganz einfach, Du müsstest danach nur noch die Schlüssel des entstandenen Arrays auf numerische Werte prüfen. Bei $_POST bzw. $_GET oder $_REQUEST (das nutze ich immer, da es Post und Get verbindet), dürften im Zweifelsfall aber auch noch die PHP-SESSION-ID mitgeliefert werden, also müsstest Du das vorher noch ausklammern indem Du z.B. das Array kopierst und die PHP-SESSION-ID rauslöschst. Der Gedanke dahinter war eben der, dass evtl. einige Werte mehrmals vorkommen und von daher sich die Größe des Arrays und damit die Schleifendurchläufe verringern würden. Im worst case hättest Du ein Array welches genauso groß ist wie das Original, im Günstigsten eben eines mit nur einem oder wenigen Elementen. Zur eigentlichen Prüfung wirst Du nicht umhin kommen, jedes Element einzeln zu prüfen, deshalb hatte ich Dir diese Funktion vorgeschlagen.


Ciao
 
[...] Im worst case hättest Du ein Array welches genauso groß ist wie das Original [...]
Genau dies wäre leider in der Regel der Fall. ;) Dann komme ich wohl um die Prüfung mit Hilfe einer Schleife nicht herum. Aber trotzdem nocheinmal Danke für deine ausführliche Antwort.

MfG mh1001
 

Ähnliche Themen

S
Antworten
13
Aufrufe
1.625
SGD-Daniel
S
Zurück
Oben