3 Stimmen

MySQL-Optimierung beim Filtern von Schlüssel-Wert-Paaren als Datensätze

Ich habe eine DB-Struktur, die entworfen ist, um Attribute für bestimmte Objekte in einer leicht erweiterbaren Weise zu speichern.
Es gibt eine Tabelle "Objekte".

+----+----------------------
| id | ....(name, type, etc)
+----+----------------------

Als nächstes habe ich eine Tabelle "Attribute".

+----+------+
| id | Name |
+----+------+

Und schließlich eine Tabelle "Beziehungen", in der alle Daten als Attribut-Objekt-Paare (als Primärschlüssel) mit den entsprechenden Werten gespeichert werden.

+--------+---------+-------+
| id_obj | id_attr | value |
+--------+---------+-------+

Ich muss IDs für Objekte abrufen, die mehrere Bedingungen auf einmal erfüllen. Ich habe z. B. Attribute mit den Namen "Typ" und "Stadt", und ich muss IDs für die Objekte abrufen, bei denen die entsprechenden Werte für diese Attribute "Wohnung" und "Stadt b" sind.

Die beste Lösung, die mir eingefallen ist, nachdem ich seit gestern mit dem Kopf gegen die Wand geschlagen habe (das einzig Gute an dieser Abfrage ist, dass sie tatsächlich funktioniert und die benötigten Datensätze abruft):

SELECT objects.id
FROM (attributes INNER JOIN relations ON attributes.id = relations.id_attr) 
  INNER JOIN objects ON relations.id_obj = objects.id
WHERE objects.id
IN (
 SELECT objects.id
 FROM (attributes INNER JOIN relations ON attributes.id = relations.id_attr) 
  INNER JOIN objects ON relations.id_obj = objects.id
 WHERE attributes.name = 'Type' AND relations.value = 'Apartment'
)
AND objects.id
IN (
 SELECT objects.id
 FROM (attributes INNER JOIN relations ON attributes.id = relations.id_attr) 
  INNER JOIN objects ON relations.id_obj = objects.id
 WHERE attributes.name = 'City' AND relations.value = 'City b'
)
GROUP BY objects.id ASC
LIMIT 0 , 20

Die Sache ist, die Menge der gespeicherten Daten kann möglicherweise etwas groß werden, und ich fürchte, dass alle diese Unterabfragen (es könnte Notwendigkeit, einige 10-15 Filter angeben), jede die ganze DB analysiert, ernsthafte Leistungsprobleme verursachen kann (nicht zu sagen, dass sogar mit meiner begrenzten SQL-Kenntnisse ich ziemlich sicher, dass es einen besseren Weg zu tun, was ich tun müssen).
Andererseits sind drastische Änderungen an der DB nicht wirklich eine Option, da der Code, der damit arbeitet, stark von der aktuellen DB-Struktur abhängt.
Gibt es eine Möglichkeit, die Attribute so zu überprüfen, wie ich es in einer einzigen Abfrage benötige, mit einer begrenzten Menge oder ohne Änderungen an der gespeicherten Datenstruktur?


Die Arbeitsabfrage, die der obigen Abfrage entspricht, aber viel besser optimiert ist, lautet DRapp :

SELECT STRAIGHT_JOIN
      rel.id_obj
   from 
      relations rel
         join attributes atr
            on rel.id_attr = atr.id
   where 
         ( rel.value = 'Apartment' AND atr.name = 'Type'  )
      or ( rel.value = 'City b' AND atr.name = 'City' )
   group by 
      rel.id_obj
   having
      count(*) = 2
   limit
      0, 20

4voto

DRapp Punkte 45486

Damit sollten Sie bekommen, was Sie brauchen... Jede der "OR "d Where-Klausel Bedingungen, können Sie einfach weiter als qualifiziertes Element hinzufügen. Dann passen Sie einfach die "Having"-Klausel an, um die gleiche Anzahl von Kriterien zu erfüllen, die Sie zulassen... Ich habe die Relationstabelle an die erste Stelle gesetzt, da diese eine kleinere übereinstimmende Menge für den "Wert" von Stadt- oder Typwerten hat... Stellen Sie sicher, dass Sie einen Index auf der Relationstabelle auf der Spalte "VALUE" haben.

SELECT STRAIGHT_JOIN
      rel.id_obj
   from 
      relations rel
         join attributes atr
            on rel.id_addr = atr.id
   where 
         ( rel.value = 'Apartment' AND atr.name = 'Type'  )
      or ( rel.value = 'Some City' AND atr.name = 'City' )
   group by 
      atr.id_obj
   having
      count(*) = 2
   limit
      0, 20

Wenn Sie alle tatsächlichen Objektdaten AUS diesen Ergebnissen haben wollen, würden Sie sie etwa so verpacken...

select obj.*
   from 
      ( complete SQL statement above ) PreQuery
         join Object obj on PreQuery.id_obj = obj.id

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X