[gelöst]Abfrage über 3 Tabellen: Performance optimieren
-
Hi zusammen,
bin bezüglich der Performance einer SQL Abfrage ziemlich skeptisch, ich kann mir nicht vorstellen, dass es nicht wesentlich schneller ginge.
Mein DBMS benutze ich mySQL 5.051b, das Schema der betroffenen Tabellen sieht so aus:Tabelle files: id int(10) PRIMARY KEY, file_type smallint( 5 ) NOT NULL, file_name TEXT NOT NULL, start_time DATETIME NOT NULL, stop_time DATETIME NOT NULL Tabelle feature_names: id int(10) PRIMARY KEY, feature_type smallint(5) NOT NULL, alias_id int(10) NOT NULL, // Fremdschlüssel für aliases.id feature_name TEXT Tabelle aliases: id int(10) PRIMARY KEY, alias_name TEXT NOT NULL Tabelle counter: id int(10) PRIMARY KEY, file_id int(10) NOT NULL, // Fremdschlüssel für files.id feature1_id int(10) NOT NULL, // Fremdschlüssel für feature_names.id feature2_id int(10) NOT NULL, // Fremdschlüssel für feature_names.id counter int(10) NOT NULL Alle Spalten, die als Fremdschlüssel dienen sind zusätzlich indiziert.
Bedeutung der einzelnen Tabellen:
files enthältoffensichtlich Datensätze mit Dateiinformationen, file_type sei mal egal für dieses Beispiel.feature_names enthält Namen einzelner Attribute und eine ID für einen Alias. Wenn die alias_id ungleich 0 ist soll statt des feature_names der Name aus der Tabelle feature_aliases benutzt werden.
feature_aliases enthält Alias Namen für einzelne Features, damit ist es möglich, unterschiedliche Features zu einem Namen zusammenzufassen
counter enthält Zählerstände je Datei/Merkmalkombination. Jeder Zählerstand besteht aus zwei Feature IDs, die jeweils ein Merkmal referenzieren. Jede Datei hat bis zu 160 Zählerstände.
Anzahl der Einträge je Tabelle:
files: 400 Einträge
feature_names: 13 Einträge
feature_aliases: 0 Einträge
counter: 11640 EinträgeFolgende SQL Abfrage braucht 70(!) Sekunden, sie soll die Summe der Zählerstände je Merkmalkombination/Datei zurückgeben:
select F.file_name as filename, // Dateiname feat1.name as defect_name1, // Name Merkmal 1, Ergebnis eines sub select feat2.name as defect_name2, // Name Merkmal 2, Ergebnis eines sub select sum( D.defect_count) as defects // Summe Zählerstand from files F, defects D, ( select // Union bilden aus Merkmalnamen ohne Alias N.id as id, // und Merkmalnamen mit Alias, je nach id N.name as name // entweder den Merkmalnamen oder den from // Aliasnamen benutzen feature_names N where N.alias_id = 0 AND N.feature_type = 0 // Feature1 betrachten union select N.id as id, A.name as name from feature_names N, feature_aliases A where N.alias_id = A.id ) feat1, ( select // analog zu oben, nur dieses Mal für N.id as id, // Merkmal 2 N.name as name from feature_names N where N.alias_id = 0 AND N.feature_type = 1 // id für Merkmal 2 union select N.id as id, A.name as name from feature_names N, feature_aliases A where N.alias_id = A.id ) feat2 where D.feature1_id = feat1.id AND D.feature2_id = feat2.id group by F.id, D.feature1_id, D.feature2_id
70 Sekunden sind auf keinen Fall zumutbar, ich denke, dass die Anweisung eventuell schlecht formuliert ist.
Bin für jede Hilfe dankbar.
-
Mit der GROUP BY kann das IMO gar nicht funktionieren, denn du selektierst Spalten die nicht in GROUP BY auftauchen, und auch ohne Aggregatfunktion verwendet werden -> darf normal nicht gehen.
Und das ganze GROUP BY ist IMO schmarrn.
-
In mysql geht das durchaus. Aber das Ergebnis einer solchen Gruppierung ist in den allermeisten Fällen nicht das, was gewünscht ist. Vor dem folgenden Code-Quote eine Frage: Was genau möchtest du mit deiner 70-Sekundenabfrage erreichen, und wie sieht dein 70-Sekundenergebnis (auszugsweise) aus?
mysql> CREATE DATABASE test2; Query OK, 1 row affected (0.00 sec) mysql> use test2; Database changed mysql> CREATE TABLE toast ( -> id INT UNSIGNED NOT NULL PRIMARY KEY auto_increment COMMENT 'Primaerschluessel', -> wertA INT DEFAULT 0 COMMENT 'Erste Zahl', -> wertB INT DEFAULT 0 COMMENT 'Zweite Zahl', -> INDEX `zahlen`(`wertA`,`wertB`) -> ) ENGINE InnoDB COMMENT 'Nur ein toastertest'; Query OK, 0 rows affected (0.26 sec) mysql> INSERT INTO toast (wertA,wertB) VALUES (1,1),(1,2),(1,3),(2,2),(2,7),(2,1); Query OK, 6 rows affected (0.08 sec) Datensätze: 6 Duplikate: 0 Warnungen: 0 mysql> SELECT * FROM toast; +----+-------+-------+ | id | wertA | wertB | +----+-------+-------+ | 1 | 1 | 1 | | 2 | 1 | 2 | | 3 | 1 | 3 | | 6 | 2 | 1 | | 4 | 2 | 2 | | 5 | 2 | 7 | +----+-------+-------+ 6 rows in set (0.00 sec) mysql> SELECT wertA,wertB FROM toast GROUP BY wertA; +-------+-------+ | wertA | wertB | +-------+-------+ | 1 | 1 | | 2 | 1 | +-------+-------+ 2 rows in set (0.00 sec) mysql> SELECT wertA,wertB FROM toast GROUP BY wertB; +-------+-------+ | wertA | wertB | +-------+-------+ | 1 | 1 | | 1 | 2 | | 1 | 3 | | 2 | 7 | +-------+-------+ 4 rows in set (0.00 sec) mysql> SELECT id FROM toast GROUP BY wertB; +----+ | id | +----+ | 1 | | 2 | | 3 | | 5 | +----+ 4 rows in set (0.00 sec)
Wie gesagt, solche "GROUP-BY-ohne Aggregatsfunktion"-Geschichten liefern eher nicht das, was man haben möchte.
Aber noch zwei Andere Fragen:
- Welche Datenbank-Engine benutzt du?
- Würdest du mir die Ausgabe von
SHOW CREATE TABLE <tabellenname>;
(ohne Header) posten?
70 Sekunden sind wirklich nicht annehmbar bei der Datenmenge.
-
Was bitte ist das Ziel?
-
*facepalm*
Was das Vergessen einer where-Klausel an Performanceeinbruch nach sich ziehen kann ist schon erstaunlich.
Da Hustbär aber sagte, dass das ganze Statement nicht SQL konform sei beschreib´ ich das Problem mal ausführlicher.
Ein Messgerät erzeugt Messdaten und schreibt sie in eine Datei, wobei jedes Messdatum aus zwei Elementen aus jeweils einer Menge besteht. Nennen wir Eigenschaft 1 mal F und Eigenschaft 2 G. F kann einen der Werte {f1,f2,..., f10} annehmen, G kann einen der Werte {g1, g2,..., g16} annehmen. Während der Messung können Messwerte der gleichen Merkmalskombination mehrfach auftreten, also wird noch ein Zählerstand benötigt. In der Praxis werden es einige tausend Dateien werden.
Das db Schema besteht aus einer Tabelle für die Dateidaten (id, name, start, ende), Merkmalnamen (id, alias_id, name), Aliasnamen für Merkmale (id, name) und Zählerstände (id, file_id, feature1_id, feature2_id, counter).
Die Anforderung ist, die Summe aller Merkmalkombinationen je Datei zu bilden, allerdings kommt hinzu, dass statt des tatsächlichen Merkmalnamens ein Name aus einer Aliastabelle benutzt werden soll. Die Bestimmung des effektiven Merkmalnamens erfolgt in den beiden sub-selects feat1 und feat2.Hier die gewünschten CREATE TABLE Anweisungen:
CREATE TABLE `files` ( `id` int(10) unsigned NOT NULL auto_increment, `file_type` int(10) unsigned NOT NULL, `file_name` text NOT NULL, `start_time` datetime NOT NULL, `stop_time` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC CREATE TABLE `feature_names` ( `id` int(10) unsigned NOT NULL auto_increment, `feature_type` smallint(5) NOT NULL, `alias_id` int(10) unsigned NOT NULL, `name` text NOT NULL, PRIMARY KEY (`id`), KEY `FEATURE_ALIAS_ID` (`alias_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC CREATE TABLE `feature_aliases` ( `id` int(10) unsigned NOT NULL auto_increment, `name` text NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=DYNAMIC' CREATE TABLE `defects` ( `id` int(10) unsigned NOT NULL auto_increment, `file_id` int(10) unsigned NOT NULL, `feature1_id` smallint(5) unsigned NOT NULL, `feature2_id` smallint(5) unsigned NOT NULL, `defect_count` int(10) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `DEFECT_FILE_ID` (`file_id`), KEY `DEFECT_FEATURE2_ID` (`feature2_id`), KEY `DEFECT_FEATURE1_ID` (`feature1_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1
Die folgende Abfrage läuft in 0.05 Sekunden und scheint die richtigen Ergebnisse zu liefern
select F.id, F.file_name, F1.name, F2.name, sum( D.defect_count ) from files F, defects D, ( select N.id, N.name from feature_names N where N.alias_id = 0 AND N.feature_type = 0 union select N.id, A.name from feature_names N, feature_aliases A where N.alias_id = A.id AND N.feature_type = 0 ) F1, ( select N.id, N.name from feature_names N where N.alias_id = 0 AND N.feature_type = 1 union select N.id, A.name from feature_names N, feature_aliases A where N.alias_id = N.id AND N.feature_type = 1 ) F2 where [b]D.file_id = F.id AND[/b] D.feature1_id = F1.id AND D.feature2_id = F2.id group by F.id, F.file_name, F1.name, F2.name
Ist daran noch was auszusetzen? Weitere Erklärungen notwendig? Ich bedanke mich auf jeden Fall schon einmal für die Hinweise.
-
DocShoe schrieb:
Die Anforderung ist, die Summe aller Merkmalkombinationen je Datei zu bilden, allerdings kommt hinzu, dass statt des tatsächlichen Merkmalnamens ein Name aus einer Aliastabelle benutzt werden soll.
Irgendwie überkommt mir das Gefühl, dass du mir nicht alles erzählt hast, schielen auf deine Abfrage - wie auch immer deine Abrage enträtseln ich nicht!
Meinst du Summe oder Liste?
Bei Summe versteh ich es so, dass du zu jeder Dateiname die Anzahl der Kombinationen ausgeben willst, sind also aggregierte Daten.
Datei1 6
Datei2 5Dazu ist die SQL Anfrage recht leicht.
SELECT file_name AS Dateiname, COUNT(SELECT * FROM defects WHERE file_id = f.id ) AS AnzahlKombination FROM files f
Wenn du jetzt noch die Featurenamen einbauen willst, sind die Daten nicht mehr aggregierte, aber als Liste dargestellt.
SELECT (SELECT id FROM files WHERE id = d.file_id) AS Id, (SELECT file_name FROM files WHERE id = d.file_id) As Dateiname, (SELECT name FROM feature_aliases WHERE id = (SELECT alias_id WHERE feature_name.id = d.feature1_id)) AS Featurename1, (SELECT name FROM feature_aliases WHERE id = (SELECT alias_id WHERE feature_name.id = d.feature2_id)) AS Featurename2 FROM defects d
Hoffe es hilft etwas!
-
Habe mich in meinem letzten Posting nicht ganz klar ausgedrückt, die Sache mit den Merkmalnamen ist folgende:
Wenn in der Tabelle feature_names.alias_id 0 ist soll feature_names.name zurückgegeben werden, ansonsten feature_aliases.name wo feature_aliases.id = feature_names.alias_id ist. Das hatte ich im ersten Posting schon erklärt und dachte, das sei ersichtlich. Hätte ich wohl besser noch dazugeschrieben.
Der Thread läuft langsam aus dem Ruder, da es inzwischen nicht mehr um die Performance der Abfrage geht, sondern um die Abfrage selbst. Ich denke, ich beenden das hier besser und ich starte einen neuen Thread, der sich mit der Abfrage selbst beschäftigt. Das Performanceproblem habe ich ja lösen können.
-
Seit wann ist das trennbar?
-
Zeus schrieb:
Seit wann ist das trennbar?
Was ist nicht trennbar?