Entwickler-Ecke
Datenbanken (inkl. ADO.NET) - SQL Freundschaftstabelle
Csharp-programmierer - So 30.10.16 21:34
Titel: SQL Freundschaftstabelle
Hallo Forum,
in der C# Applikation ist es jetzt möglich, Freundschaftsanfragen zu versenden. Wird eine Freundschaftsanfrage versendet, wird in einer Tabelle `Freundschaftsanfragen` ein Datensatz angelegt, der in etwa so aussieht. Wenn der Benutzer, an den die FA gesendet wurde, sich einloggt, kann er diese Anfrage annehmen oder ablehnen. Nimmt er diese an, dann wird der Datensatz aus der Tabelle `Freundschaftsanfragen` gelöscht. Nun wird aber ein neuer Datensatz in die Tabelle `Freundesliste` eingetragen. Diese Tabelle sieht ungefähr so aus:
ID | One | Two |
--------------------------------
1 | User1 | User2 |
--------------------------------
Soo das funktioniert schon mal. Wenn sich jetzt User 1 einloggt, muss User2 als Freund dastehen. Aber wenn User2 sich einloggt, muss User1 als Freund auftauchen.
Wie kann man diese SQL Abfrage verallgemeinern, dass die alle Freunde von dem jeweiligen eingeloggten Benutzer ausgibt?
Mit freundlichen Grüßen :)
Delete - So 30.10.16 22:08
- Nachträglich durch die Entwickler-Ecke gelöscht -
Csharp-programmierer - So 30.10.16 22:31
Oder man erzeugt pro Freundschaft 2 Datensätze. Beispielsweise so:
ID | UserEingeloggt | UserAnderer |
-------------------------------------------------------
1 | Cshrp-programmierer | User2 |
-------------------------------------------------------
2 | User2 | Csharp-programmierer |
-------------------------------------------------------
Dann könnte man diese Abfrage durchführen:
SQL-Anweisung
1:
| SELECT * FROM `Freundschaften` WHERE `UserEingeloggt`=VALUE |
Aber dann würde ja mein 80 GB Webspace Speicher sicher irgendwann überlasten :(
Christian S. - So 30.10.16 22:50
Csharp-programmierer hat folgendes geschrieben : |
Aber dann würde ja mein 80 GB Webspace Speicher sicher irgendwann überlasten :( |
Auf Grund welcher Abschätzung der benötigten Datenmengen vermutest Du das?
Csharp-programmierer - So 30.10.16 22:53
Naja...
Sagen wir mal 1000 User sind befreundet, dann sind in der Datenbank 2000 Datensätze. Ist bestimmt schon relativ viel
Christian S. - So 30.10.16 23:20
Wie kommst Du da drauf, dass das viel ist?
Du speicherst (höchstens) drei Zahlen. Eine ID für den Datensatz, eine ID für den ersten Benutuzer, eine ID für den zweiten Benutzer. Drei Zahlen mit je 32 Bit = 4 Byte. Sind also 12 Byte pro Datensatz. Sagen wir, es gibt noch einen gigantisch unrealistischen Overhead und es sind pro Datensatz 1 KB (also das 85-fache der eigentlichen Daten!). Bei 2000 Datensätzen wärst Du also bei 2 MB. Wo genau siehst Du die Grenze von 80 GB erreicht?
Delete - Mo 31.10.16 00:22
- Nachträglich durch die Entwickler-Ecke gelöscht -
Christian S. - Mo 31.10.16 00:27
Frühlingsrolle hat folgendes geschrieben : |
[...]oder du erstellst für jeden Namen eine eigene Tabelle, in der dessen Freunde aufgelistet werden. |
Was wäre der Vorteil davon?
Delete - Mo 31.10.16 00:29
- Nachträglich durch die Entwickler-Ecke gelöscht -
Christian S. - Mo 31.10.16 00:36
Du meinst, wenn ich dann bei 1000 Nutzern 1000 Tabellen habe?
Und wie frage ich dann z.B. ab, wer alles Benutzer X "gefreundet" hat?
Palladin007 - Mo 31.10.16 00:45
In der heutigen Zeit ist der Datenspeicher die billigste Resource, um die würde ich mir als letztes Gedanken machen ^^
Klar, solltest Du einige Grundregeln beachten, aber solange Du nicht zig Daten an zig verschiedenen Stellen redundant hältst, sollten deine 80GB eigentlich locker und mit links ausreichen.
Kommt natürlich drauf an, was Du noch so für Daten sammelst
Und was die Freundschaften angeht:
Variante 1:
Wie Du schon gesagt hast:
Für jede neue Freundschaft bekommt jeder User einen Freundschaft-Eintrag.
Das hätte den Vorteil, dass pro Person eigene Daten pro Freundschaft gespeichert werden können.
Einen Vorteil in der Abfrage sehe ich nicht.
Variante 2:
Jede Freundschaft zwischen zwei Personen ist "eindeutig"
Pro Freundschaft gibts dann genau einen Datensatz.
Gemeinsame Daten pro Freundschaft sind dann leichter zu verwalten, eine Person kann aber keine eigenen Daten für eine Freundschaft haben.
In SQL ungefähr so:
SQL-Anweisung
1: 2: 3:
| SELECT * FROM Freundschaften WHERE User1Id = @userId OR User2Id = @userId |
Die wichtigste Frage bleibt also:
Was genau willst Du machen?
Braucht eine Freundschaft pro Person eigene Daten?
Oder soll jede Freundschaft eigene Daten gemeinsam für beide Beteiligten haben?
Je nachdem, was Du brauchst, musst Du die Variante wählen.
Die Komplexität der SQL-Abfrage, der Umfang der Daten oder Performance sind bei einem so kleinen Rahmen eher drittrangig.
Aber mach bitte nicht so Spielereien wie pro Freund eine neue Spalte oder pro Person eine neue Freundes-Tabelle.
Potentielle Probleme dabei:
- ORMs kann man sich damit eigentlich sparen, es gibt ja keine statische Klassenstruktur mehr, in die gängige ORMs das mappen können. Oder ich weiß von kenem ORM, was das im laufenden Betrieb kann.
- Die SQL-Abfragen lassen sich nicht mehr über einfache Parameter zusammenbauen, einfache WHERE-Klauseln reichen nicht mehr, andere Dinge wie Group By dürften schwierig werden, etc.
- Eine Freundschafts-Anfrage braucht auf einmal DDL-Rechte auf dem Server, was ich für fragwürdig halte
- Mittelfristig dürfte das auch ein Performance-Problem darstellen, wenn Du nicht nur tausende Daten (was echt wenig ist), sondern tausende Tabellen hast. Und tausende Tabellen, das IST viel :D
Hab ich was vergessen?
Delete - Mo 31.10.16 01:11
- Nachträglich durch die Entwickler-Ecke gelöscht -
Th69 - Mo 31.10.16 10:27
Also das mit den eigene Tabellen paßt nun wirklich nicht zu einer normalisierten Datenbank.
Ich würde die Abfrage ähnlich wie Palladin007 machen, nur daraus zwei machen und diese beiden vereinen:
SQL-Anweisung
1: 2: 3: 4: 5: 6: 7: 8: 9:
| SELECT User2Id FROM Freundschaften WHERE User1Id = @userId
UNION
SELECT User1Id FROM Freundschaften WHERE User2Id = @userId |
Palladin007 - Mo 31.10.16 11:10
Zitat: |
1000 Benutzer / 1000 Tabellen, oder 1 Tabelle und ein vielfaches an Datensatzen, also mehr als 1000! |
Wenn 1000 Benutzer 1000 Freunde haben, dann brauchst Du 1000 Tabellen mit 1000 Datensätzen.
1000 Tabellen tun weh in der Performance
Alles in einer Tabelle würde bedeuten, dass für jede Person und für jede Freundschaft zwei Datensätze angelegt werden.
Sprich: 1000 * 1000 * 2 = 2000000 Datensätze
Das wäre in der Tat viel, allerdings hast Du die Datenmenge bei 1000 Tabellen auch plus die Daten, die der Server braucht um die Tabellen zu verwalten.
Und bei 1000 Tabellen kann der Server nicht über alle Daten indizieren.
Und DAS, der letzte Punkt, der sollte eigentlich ein KO-Kriterium sein ;)
Wenn Du 2 Millionen Datensätze in einer Tabelle hast, kann der Server auf alle Datensätze indizieren, was einen deutlichen Performance-Vorteil bedeuten kann.
Wenn Du diese 2 Millionen Datensätze auf 1000 Tabellen auf teilst, dann indiziert der Server über 1000 Datensätze pro Tabelle, was witzlos wäre.
Csharp-programmierer - Mo 31.10.16 12:55
Jetzt habe ich gestern Abend noch bin tief in die Nacht an dem PHP Script rumgewerkelt, dass die Usernames in IDs verwandelt werden. Jetzt haut alles so hin, dass die C# Anwendung die Usernames zur PHP Datei schickt und diese demnach die zugehörige ID raussucht und in die Datenbank schreibt. Im Moment läuft es noch so, dass der doppelte Datensatz eingetragen wird.
Was soll noch passieren?
Die eine Datenbank soll lediglich alle Freundschaften speichern. Heute versuche ich noch einen PHP Script zu basteln, der dann die ganzen Freunde, samt anderen Daten ausgibt...
Im Moment habe ich ja noch den doppelten Datensatz für eine Freundschaft.
SQL-Anweisung
1: 2: 3: 4: 5: 6: 7: 8: 9:
| SELECT User2Id FROM Freundschaften WHERE User1Id = @userId
UNION
SELECT User1Id FROM Freundschaften WHERE User2Id = @userId |
Und mit diesem SQL Befehl bekommt man dann alle Freundschaften anhand eines Datensatzes?
Delete - Mo 31.10.16 14:44
- Nachträglich durch die Entwickler-Ecke gelöscht -
jfheins - Mo 31.10.16 15:04
Was meinst du damit? Die Abfrage?
Das liegt halt daran, dass die Freundschaft kommutativ ist. Man kann auch ein System implementieren, bei dem man Leute einseitig de-frienden kann. Dann hätte die Reihenfolge wieder eine Bedeutung.
Aber so ist es egal, und bevor du einfach die doppelte Menge an Daten in die DB kippst, ist es doch einfach eine Abfrage wie "... WHERE User1Id = @userId OR User2Id = @userId" abzusetzen.
Delete - Mo 31.10.16 19:06
- Nachträglich durch die Entwickler-Ecke gelöscht -
Csharp-programmierer - Mo 31.10.16 21:08
Ich habe es jetzt so gemacht, dass pro Freundschaft nur ein Datensatz angelegt wird. Ich habe folgenden Code zusammengestellt:
PHP-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| $CurrentUser = mysqli_real_escape_string($conn, $_POST["currUsr"]); $sqlQuery = "SELECT `To_ID` FROM `MainlysoftFreundesliste` WHERE `To_ID`='$CurrentUser' OR `From_ID`='$CurrentUser'"; $result = mysqli_query($conn, $sqlQuery); if($result) { $newQuery = "SELECT `name`, `vorname`, `language`, `gender` FROM `MainlysoftBenutzer` WHERE `ID`='$CurrentUser'"; if ($result->num_rows > 0) { while($row = $result->fetch_assoc()) { echo $row["name"]. ";" . $row["vorname"]. ";" .$row["language"]. ";" .$row["gender"]. ""; } } else { echo "0 results"; } } else { echo "Error in SQL statement"; } |
Jetzt wird mir ja nur "To_ID" rausgesucht. Aber wenn ich jetzt auch "Von_ID" raussuchen lass, weiß ich ja nicht, welcher von den beiden Usern sich eingeloggt hat. Verseht ihr den logischen Fehler?
Ralf Jansen - Mo 31.10.16 21:21
Zitat: |
Jetzt wird mir ja nur "To_ID" rausgesucht. Aber wenn ich jetzt auch "Von_ID" raussuchen lass, weiß ich ja nicht, welcher von den beiden Usern sich eingeloggt hat. Verseht ihr den logischen Fehler? |
Nein. Warum solltest du, weil das SQL änderst, ~vergessen~ wer in $CurrentUser steht?
Th69 - Di 01.11.16 10:00
Ich möchte noch mal auf meinen UNION-Vorschlag hinweisen ;-)
Aber mit einer einfachen if-Abfrage läßt sich natürlich auch die OR-Query auflösen...
Csharp-programmierer - Di 01.11.16 15:19
Okay. Ich habe jetzt diesen Code:
PHP-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
| $CurrentUser = mysqli_real_escape_string($conn, $_POST["currUsr"]); $sqlQuery = "SELECT `To_ID` FROM `MainlysoftFreundesliste` WEHRE `From_ID`='$CurrentUser' UNION SELECT `From_ID` FROM `MainlysoftFreundesliste` WHERE `To_ID`='$CurrentUser'"; $result = mysqli_query($conn, $sqlQuery); if($result) { if($result->num_rows > 0) { while($row1 = $result->fetch_assoc()) { $zwischenVariable = $row1["To_ID"]; $newQuery = "SELECT `name`, `vorname`, `language`, `gender` FROM `MainlysoftBenutzer` WHERE `ID`='$zwischenVariable'"; $newResult = mysqli_query($conn, $newQuery); if($newResult) { if ($newResult->num_rows > 0) { while($row = $newResult->fetch_assoc()) { echo $row["name"]. ";" . $row["vorname"]. ";" .$row["language"]. ";" .$row["gender"]. ""; } } else { echo "0 results"; } } } } } else { echo "Error in SQL statement"; } |
Aber nun bekomme ich den Fehler: Error in SQL statement...
Die Datenbanktabelle sieht jetzt so aus:
ID | From_ID | To_ID |
------------------------------
1 | 1 | 2 |
jfheins - Di 01.11.16 16:03
Der Fehler ist so natürlich wenig hilfreich.
Informativer wird's, wenn du die Zeile im else-Zweig so änderst:
PHP-Quelltext
1: 2: 3: 4:
| else { echo "Error in SQL statement: " . mysqli_error($conn); } |
Bitte mal probieren und rückmelden ;-)
Th69 - Di 01.11.16 16:16
'WEHRE't den Anfängen. :lol:
Csharp-programmierer - Di 01.11.16 16:47
Hupps. WHERE ist aber auch ein doofes Wort :(
PHP-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32:
| $CurrentUser = mysqli_real_escape_string($conn, $_POST["currUsr"]); $sqlQuery = "SELECT `To_ID` FROM `MainlysoftFreundesliste` WHERE `From_ID`='$CurrentUser' UNION SELECT `From_ID` FROM `MainlysoftFreundesliste` WHERE `To_ID`='$CurrentUser'"; $result = mysqli_query($conn, $sqlQuery); if($result) { if($result->num_rows > 0) { while($row1 = $result->fetch_assoc()) { $zwischenVariable = $row1["To_ID"]; $newQuery = "SELECT `name`, `vorname`, `language`, `gender` FROM `MainlysoftBenutzer` WHERE `ID`='$zwischenVariable'"; $newResult = mysqli_query($conn, $newQuery); if($newResult) { if ($newResult->num_rows > 0) { while($row = $newResult->fetch_assoc()) { echo $row["name"]. ";" . $row["vorname"]. ";" .$row["language"]. ";" .$row["gender"]. ""; } } else { echo "0 results"; } } else { echo "error here"; } } } else { echo "now here error"; } |
Jetzt kommt nur noch der Error: now error here. Das heißt ja, dass die Datenbank keine passenden Datensätze findet, oder wie?
Moderiert von Christian S.: C#- durch PHP-Tags ersetzt
Delete - Di 01.11.16 17:18
- Nachträglich durch die Entwickler-Ecke gelöscht -
Palladin007 - Mi 02.11.16 01:22
Th69 hat folgendes geschrieben : |
Ich möchte noch mal auf meinen UNION-Vorschlag hinweisen ;-) |
Wo genau wäre überhaupt der Vorteil, die zwei Selects per Union zu verknüpfen anstatt die UserId mit beiden IDs in der Tabelle zu vergleichen?
Also entweder das:
SQL-Anweisung
1: 2: 3:
| SELECT * FROM Friends WHERE FromUserId = 123 OR ToUserId = 123 |
Oder:
SQL-Anweisung
1: 2: 3: 4: 5: 6: 7: 8: 9:
| SELECT * FROM Friends WHERE FromUserId = 123
UNION
SELECT * FROM Friends WHERE ToUserId = 123 |
Am Ende kommen alle Datensätze raus, bei denen sowohl FromUserId oder ToUserId gleich 123 sind.
Oder übersehe ich da was?
Th69 - Mi 02.11.16 11:38
Hallo Palldadin007,
bei der OR-Query müßtest du aber noch explizit die beiden Felder FromUserId und ToUserId überprüfen - bei der UNION-Version (in meiner Variante!) hat man sofort in einer Spalte alle Freunde stehen.
Abfragen mit * sollte man generell im Code vermeiden.
Palladin007 - Mi 02.11.16 13:07
Stimmt, damit bekomme ich dann eine flache Liste mit nur einer Spalte.
Endlich hab ich auch mal ein Beispiel, wo Union sinnvoll ist, hab ich bisher noch nie verwendet :D
Zitat: |
Abfragen mit * sollte man generell im Code vermeiden. |
Klar, war auch nur ein Beispiel weil wegen spät und so :D
Csharp-programmierer - Do 03.11.16 17:53
Also ich verstehe den Fehler jetzt nicht ganz.
PHP-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31:
| $CurrentUser = mysqli_real_escape_string($conn, $_POST["currUsr"]); $sqlQuery = "SELECT `To_ID` FROM `MainlysoftFreundesliste` WHERE `From_ID`='$CurrentUser' UNION SELECT `From_ID` FROM `MainlysoftFreundesliste` WHERE `To_ID`='$CurrentUser'"; $result = mysqli_query($conn, $sqlQuery); if($result) { while($row1 = $result->fetch_array()) { $zwischenVariable = $row1["To_ID"]; $newQuery = "SELECT `name`, `vorname`, `language`, `gender` FROM `MainlysoftBenutzer` WHERE `ID`='$zwischenVariable'"; $newResult = mysqli_query($conn, $newQuery); if($newResult) { if ($newResult->num_rows > 0) { while($row = $newResult->fetch_assoc()) { echo $row["name"]. ";" . $row["vorname"]. ";" .$row["language"]. ";" .$row["gender"]. ""; } } else { echo "0 results"; } } else { echo "error here"; } } } else { echo "Error in SQL statement"; } |
Wenn ich diesen Script jetzt ausführe, bekommt ich keine Antwort (oder eine leere). Ich möchte nur, dass die Tabelle "Freundesliste" durchsucht wird. Und für jeden Freund, der mit dem aktuell eingeloggten User befreundet ist, soll nochmal in der Tabelle "Benutzer" gesucht werden und dann Daten rausgegeben werden.
Th69 - Do 03.11.16 20:57
Nur eine Vermutung: passen die Datentypen?
Stehen in den Datenbankspalten From_ID und To_ID denn wirklich Strings drin (oder Integer) - du verwendest ja Hochkommata?
Und bei einer UNION bin ich mir nicht sicher, was der genauer Spaltenname der Query ist (d.h. laß dir doch mal $zwischenVariable ausgeben) - evtl. nimm mal $row1[0].
Csharp-programmierer - Fr 04.11.16 18:04
In der Datenbank `Freundesliste` sind nur Datentypen vom Typ INT.
Ich habe mir mal die
zwischenVariable und
row[0] geechoed. Aber die Serverantwort war leer :(
Moderiert von Th69: PHP-Tags hinzugefügt
Csharp-programmierer - Fr 04.11.16 20:18
Ups, mir ist ein kleiner großer Fehler passiert. In der C# Anwendung habe ich der NameValueConnection einen falschen Parametername gegeben. Jetzt funktioniert es halbwegs.
Wenn sich jetzt der User einloggt, bekommt dieser eingeloggte User sich selbst als Freund, das liegt wahrscheinlich daran, dass dass der eingeloggte User mit einem Freund an einem Datensatz hängt. SQL gibt jetzt wahrscheinlich den einen User aus, der in dem Fall der eingeloggte User ist.
Wie kann man das so programmieren, dass genau der andere Datensatz als der eingeloggte User ausgegeben wird?
Delete - Fr 04.11.16 20:41
- Nachträglich durch die Entwickler-Ecke gelöscht -
Th69 - Fr 04.11.16 20:53
Bei der UNION-Query kann das aber nur passieren, wenn in einem Datensatz derselbe Wert in beiden Spalten steht.
Csharp-programmierer - Fr 04.11.16 21:10
Vielen Dank Leute,
jetzt funktioniert es. Der Fehler war, dass ich in der SQL Abfrage * statt `From_ID` genommen habe. Jetzt funktionierts :)
Vielen Dank, an die Helfer :)
Csharp-programmierer - Fr 04.11.16 21:36
Ups, das Thema ist doch noch offen. Ich habe noch ein kleines Problem.
In der Datenbank `Freundesliste` sind nun 3 Datensätze. Jetzt ist aber nur das Problem, dass die PHP Datei nur einen von 2 Freunden raussendet. Wieso?
PHP-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:
| if($conn) { $CurrentUser = mysqli_real_escape_string($conn, $_POST["currUsr"]); $sqlQuery = "SELECT `To_ID` FROM `MainlysoftFreundesliste` WHERE From_ID='$CurrentUser' UNION SELECT `From_ID` FROM `MainlysoftFreundesliste` WHERE To_ID='$CurrentUser'"; $result = mysqli_query($conn, $sqlQuery); if($result) { if ($result->num_rows > 0) { while($row1 = $result->fetch_array()) { $zwischenVariable = $row1["To_ID"]; $newQuery = "SELECT `name`, `vorname`, `language`, `gender` FROM `MainlysoftBenutzer` WHERE `ID`='$zwischenVariable'"; $newResult = mysqli_query($conn, $newQuery); if($newResult) { if ($newResult->num_rows > 0) { while($row = $newResult->fetch_assoc()) { echo $row["name"]. ";" . $row["vorname"]. ";" .$row["language"]. ";" .$row["gender"]. ""; } } else { echo "0 results"; } } else { echo "0 results"; } } } else { echo "o results"; } } else { echo "Error in SQL statement"; } } |
Delete - Sa 05.11.16 03:56
- Nachträglich durch die Entwickler-Ecke gelöscht -
Csharp-programmierer - Sa 05.11.16 15:02
Das Problem war, dass ich im PHP Script | vergessen habe. Die C# Applikation splittet die Serverantwort dann an dem |. Jetzt funktioniert aber wirklich alles :)
Delete - Sa 05.11.16 15:21
- Nachträglich durch die Entwickler-Ecke gelöscht -
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!