mmofacts.com

SQL mit JOIN im IF0

gepostet vor 16 Jahre von Nerosmeel

Hallo zusammen,

ich habe folgendes Problem ich habe für das Inventar in meinem Spiel eine Tabelle für das eigentliche Inventar

Id PlayerId ItemId ItemType Count ...

und Tabellen für die Items je nach Typ, als Beispiel...

Nutzloser Kram

Id Name Price Weight ...

Waffen

Id Name Damage Price Weight ...

Wenn ich mir das Inventar nanzeigen lasse holle ich mittels LEFT JOIN die Namen der Items, das geht auch alles so lange ich eine Tabelle habe wo alle Items drinstehen. 

PHP:

$inventar = $this->_db->fetchAll("SELECT ItemId, ItemType, Count FROM game_inventary 
LEFT JOIN game_items ON game_items.Id = game_inventary.ItemId
WHERE PlayerId =?",$this->_user->Id);

Klappt super!!

Jetzt will ich aber das LEFT JOIN Ziel von Wert in der Spalte ItemType abhänigmachen, das wiederrum geht garnich!

PHP:

$inventar = $this->_db->fetchAll("SELECT ItemId, ItemType, Count FROM game_inventary 
IF(ItemType == 'Weapon', LEFT JOIN game_items ON game_items.Id = game_inventary.ItemId, LEFT JOIN game_weapons ON game_weapons.Id = game_inventary.ItemId)
WHERE PlayerId =?",$this->_user->Id);

 Aus dem Kram bei der mySQL Doku werde ich nicht so ganz schlau, kann es sein das ich das IF nur um SELECT Teil nutzten kann? Wenn ja wie könnte ich das Problem lösen ohne auf PHP zurückzugeifen und alle Items in eine Tabelle zu packen ?

Mit bitte um Hilfe

mfg Nerosmeel

gepostet vor 16 Jahre von Fobby

Hm, was hälst du davon, alle Items in eine Tabelle zu werfen?

id type name price weight damage defence durability ...

Was der Itemtyp nicht braucht, wird mit NULL gefüllt.

gepostet vor 16 Jahre von Nerosmeel

Mein Edit war leider nich schnell genug ^^

Die Idee hat ich zu beginn auch aber dann werden das doch sehr viele spalten da die waffen nachher nicht nut schaden machen sondern auch auswirkungen auf den Charakter haben und es mehrere schadens Arten gibt.

gepostet vor 16 Jahre von Phoscur

Mir fällt jetzt grad nicht ein wie ich das Query formulieren würde, aber das Problem liegt darin, dass du Syntax per IF einzufügen und das is Quatsch. Du kannst nur Werte mit IF austauschen.

gepostet vor 16 Jahre von Tron

Konditionaler Left join kann nichts werden, du hättest ja ein nicht definiertes Ergebnis, was die Spalten des Ergebnises angeht. Du könntest in die WHERE clause " AND ItemType == 'Weapon' " hinzufügen, dann würdest du halt nur Waffenergebnisse bekommen, falls dich das weiterbringt.

Wenn die Anfrage komplexer werden soll gäbe es noch die Möglichkeit mit Temporären Zwischentabellen zu arbeiten, aber wenn ich dein Vorhaben richtig verstanden habe, sollte das nicht nötig sein.

Saludos,

Stefan

gepostet vor 16 Jahre von Kampfhoernchen

Die DB-Struktur ist unsinnig. Eine Tabelle für alle Items und den Item-Typ als Spalte mit dran.

Wenn dus unbedingt so lassen willst, sieh dir mal das Statement UNION an.

Tipp:

LEFT JOIN (SELECT * from i1 UNION SELECT * FROM i2) AS allitems ON ....

gepostet vor 16 Jahre von Nerosmeel

Morgen zusammen,

hab mir das mit dem Union mal angesehen... werds wohl doch alles in eine Tabelle Packen,

hab mir das Item Konzept noch einmal näher betrachtet, habs jetzt so weiter reduziert das ich bei maximal 50 Spalten bin... Hoffe das klappt dann noch alles in einem akzeptablen Tempo ^^

mfg

gepostet vor 16 Jahre von Tron

Pauschalaussagen beim Datenbankmodelling sind riskant. 50 Spalten sagen von der Menge nicht viel über die Performanz aus, wenn zum Beispiel der überwiegende Teil der Werte Integer, Sets oder Enums sind, und die Datensatzgröße nicht allzu groß wird.

Viel interessanter ist bei MySql die Frage, ob du Daten mit häufigem Schreibzugriff trennen kannst von Daten mit überwiegend reinem Lesezugriff, da bei Lesezugriffen die Tabellen nicht gelocked werden müssen.

Beispiel wäre, (kann von deinem Modell abweichen) eine Tabelle in der individuelle Items mit Besitzer, Anzahl, Zustand, Veränderliche Attribute etc. abgelegt werden in der eine uid auf eine Tabelle mit der eher statischen Itemklasse verweist. Ob es lohnt, die letztere Tabelle noch aufzuteilen in Itemklasse und Itemtyp hängt davon ab, ob der Itemtyp wiederum einen Satz statischer Attribute mit sich führt.

Wenn du darauf achtest, daß alle in der WHERE clause verwendeten Spalten indiziert sind, hast du idR da keine Performanzprobleme zu erwarten, myIsam ist bei suche über indizierte Schlüssel sehr schnell.

Hast du dir, bevor du dir die Performanz deiner Datebank anschaust, mal ein Bild von den Anwendungsfällen und damit verbundenen Zugriffen und ihrer statistischen Häufigkeit gemacht?

Es lohnt sich kaum, eine Datenbank nach seltenen Zugriffen zu optimieren, von denen du vielleicht nur eine handvoll am Tag hast, wohingegen es sicher eine paar Zugriffe gibt, die ständig passieren werden. Hier kann man dann sinnvoll über Optimierung nachdenken. Das ermöglicht dir schlussendlich auch die Entscheidung, welcher Tabellentyp (oder vielleicht sogar welche Datenbank) für dein Projekt am geeignetsten sind.

Saludos,

Stefan

gepostet vor 16 Jahre von KEEN

Wie wäre es mit einer Tabelle

id | ItemId | Attribute | Value

Wobei die Tabelle pro ItemId beliebig viele Einträge haben kann. Damit brauchst du keine Tabelle mit 50 Spalten und kommst mit insgesamt zwei Querys aus. In der ersten Query holst du dir alle ItemIds die du im Inventar hast und in der zweiten Query mit WHERE ItemId IN (...) alle Werte und Eigentschaften dazu.

gepostet vor 16 Jahre von Kampfhoernchen

Aus reiner Neugier: Sag uns doch mal die Spalten die du da hast.

gepostet vor 16 Jahre von Nerosmeel

Morgen zusammen

Es wäre ein rein lesender zugriff, ist auch Logisch das, dass immmer schnell ist als schreiben. Geschrieben wird nur in die Inventar Tabelle, die ist auch ensprechend schmall gehalten.

Hab noch nicht alle Spalten (Korrekt) übersetzt.

Das ist alles was nach letzter nach übrig geblieben ist, sieht doch so langsam richtig Human aus.

  1. Id
  2. Name
  3. Type
  4. Weight
  5. Price
  6. Damage
  7. Ammo
  8. Armor
  9. UseMSG
  10. Tarnung
  11. ExplosiveRange
  12. ValidAddons
  13. Volume
  14. Useable
  15. Healing
  16. Strength
  17. Perception
  18. Endurance
  19. Charisma
  20. Intellicence
  21. Agility
  22. Luck
  23. Poison
  24. RateOfFire
  25. Bags
  26. Life
  27. Sucht
  28. Hunger
  29. Durst
gepostet vor 16 Jahre von Kampfhoernchen

Ja, würde ich in dem Falle wirklich so lassen. Ggf. nen Index auf alle felder nach denen gesucht wird (also nehme ich mal an ID und Type) und du dürftest auch bei 100k Einträgen noch keine Probleme bekommen.

gepostet vor 16 Jahre von Nerosmeel

Immer wieder les ich was von Indexes...

Werd mir das jetzt dochmal was genauer anschaun, wo ich schon seit geraumer Zeit immer wieder gutes drüber lese.

gepostet vor 16 Jahre von Kampfhoernchen

Indices ist die Mehrzahl.

Und bringen in jeder Datenbank enorme vorteile, inbesondere wenn die lesenden zugriffe höher sind als die schreibenden.

gepostet vor 16 Jahre von Kallisti

Indexes ist im Englischen genauso richtig.

Aber bitte nicht übertreiben damit, nur auf die Spalten, die wirklich regelmäßig in WHERE statements genutzt werden.

gepostet vor 16 Jahre von Nerosmeel

Hab sie um mom auch nur auf Id und Type, das sollte reichen ich bin mir zu 99% sicher das ich nach den anderen Spalten nie oder nur sehr selten suchen muss.

gepostet vor 16 Jahre von Kallisti

Hat denn jedes Item alle 26 Attribute gesetzt, bzw. benötigt jedes Item diese? Oder ist das eher ein "Flickenteppich" in dem im Grunde nur jedes zweite Feld valide ist? Falls letzteres zutrifft, dann solltest Du mal über Keens Vorschlag nachdenken. In dem Fall sollte auf Attribute natürlich ebenfalls ein Index.

In Sachen Normalisierung und sauberem Design sähe das auf jeden Fall ordentlicher aus... Sofern ItemID und Attribute in Kombination einzigartig sind, kannst Du auch diese als Primary Key nutzen und Dir die inkrementelle ID sparen. Der Name sollte sinnigerweise dann mit den Eingenschaften, die wirklich jedes Item hat, in einer anderen Tabelle liegen, um Redundanz zu vermeiden.

gepostet vor 16 Jahre von Klaus

Außer name wird doch jede Spalte Int oder Float sein, sprich eine feste Länge haben. Wenn du für name dann noch ein statisches Char verwendest, ist die ganze Tabelle statisch und MySQL kann vorher schon berechnen welcher Wert an welcher Stelle steht. Somit sparst du dir Indizies und musst trotzdem keine Tabellenscans fahren.

gepostet vor 16 Jahre von Kallisti

Original von Klaus

Außer name wird doch jede Spalte Int oder Float sein, sprich eine feste Länge haben. Wenn du für name dann noch ein statisches Char verwendest, ist die ganze Tabelle statisch und MySQL kann vorher schon berechnen welcher Wert an welcher Stelle steht. Somit sparst du dir Indizies und musst trotzdem keine Tabellenscans fahren.

... was bei "WHERE" oder "ORDER BY" dennoch nichts daran ändert, dass es mit Index schneller ist, oder? (wenngleich in geringerem Masse) Bin kein Datenbank-Detail-Spezialist, aber ich versteh es so, dass ohne varchar/text/etc.. bedeutet, dass ich einfacher über eine Spalte iterieren kann - macht Sinn. Aber bei Suche und Sortierung ist es zwar besser, jedoch immer noch der Lösung mit Indizes unterlegen, oder täusche ich mich/überseh etwas?

gepostet vor 16 Jahre von Kampfhoernchen

Indices machen die Datei die durchsucht werden muss halt nochmal kleiner, weil die Werte nicht dabei stehen. Je weniger Bytes ich durchsuchen muss, um so schneller wirds. Mit char statt varchar weiß das System aber schonmal wie viele Bytes es überspringen kann, durchsucht also insgesamt weniger.

gepostet vor 16 Jahre von Kallisti

Jo, aber der indexierte Bereich wird in log(n) durchsucht bzw. ist bereits sortiert, waehrend man ohne Index n/2 sucht und n*log(n) sortiert (wobei dann die Aggregation der Daten schneller geht, als mit variabler Feldgroesse in der Tabelle), oder?

gepostet vor 16 Jahre von exe

Original von Klaus

Außer name wird doch jede Spalte Int oder Float sein, sprich eine feste Länge haben. Wenn du für name dann noch ein statisches Char verwendest, ist die ganze Tabelle statisch und MySQL kann vorher schon berechnen welcher Wert an welcher Stelle steht. Somit sparst du dir Indizies und musst trotzdem keine Tabellenscans fahren.

Das ist nicht ganz richtig. Für die Berechnung der Position in der Tabelle wird die Zeilennummer benötigt. Und die Zeilennummer steht im Index. Hast du keinen machst du auch bei MyISAM einen ganz normalen Tablescan. Der ist dann aber schneller als bei InnoDB ;)

gepostet vor 16 Jahre von Nerosmeel

Hmm

ich hab zwar erst in einem Jahr Datenbanken, aber hab schonmal mit dem Prof gereindet.

Der meinte auch, wenn alle Spalten von Typ int, float, char sind und ich nich zu viele Indexe habe, zwei scheinen ok zu sein.

Werd heute erstmal den CSS kram fertig machen (Bin mehr Programmierer als Designer), und mir morgen mal die Tabelle und das Inventar angehen.

mfg

gepostet vor 16 Jahre von Nerosmeel

Hab mir das ganze nochmal durch denn Kopf gehen lassen.

Wenn ich jetzt noch hingehen würde und sagen das manche Waffen und Rüstungen lvl und Werte voraussetzten kämmen nochmal wieder mindesten 10 Spalten dazu.

Da wäre zu überlegen die Inventar Tabelle noch um den Itemnamen und Typ zu erweitern. Da damit wäre das Laden des inventar vom Db Tempo her kein Problem, da ja nur Name und Anzahl angzeigt werden. Wenn dann das Item benutzt wird, kannman die Tabelle ja über nen Switch ermitteln.

Außerdem wären die Tabellen für Waffen Rüstung etc. wesentlich übersichtlicher.

Wie ist eure meinung zu der Idee?

mdf Nerosmeel

Auf diese Diskussion antworten