2016-11-10 7 views
1

Ich habe drei Tabellen:Leistung der Volltextsuche in verknüpften Tabellen

CREATE TABLE `dp_organisation` (
    `OrganisationId` bigint(32) NOT NULL AUTO_INCREMENT, 
    `Name` text COLLATE utf8mb4_unicode_ci NOT NULL, 
    `ShortName` text COLLATE utf8mb4_unicode_ci, 
    PRIMARY KEY (`OrganisationId`), 
    FULLTEXT KEY `fulltext` (`Name`,`ShortName`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 

CREATE TABLE `dp_organisation_member` (
    `OrganisationId` bigint(32) NOT NULL, 
    `UserId` bigint(32) NOT NULL, 
    PRIMARY KEY (`OrganisationId`,`UserId`), 
    UNIQUE KEY `UserId` (`UserId`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 

CREATE TABLE `dp_user` (
    `UserId` bigint(32) NOT NULL AUTO_INCREMENT, 
    `Alias` varchar(125) COLLATE utf8mb4_unicode_ci DEFAULT NULL, 
    `Firstname` text COLLATE utf8mb4_unicode_ci NOT NULL, 
    `Surname` text COLLATE utf8mb4_unicode_ci, 
    `Email` varchar(125) COLLATE utf8mb4_unicode_ci NOT NULL, 
    PRIMARY KEY (`UserId`), 
    FULLTEXT KEY `fulltext` (`Alias`,`Firstname`,`Surname`,`Email`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; 

dp_organisation alle Organisationen enthält, während dp_users alle Benutzer enthält. dp_organisation_member ist die Beziehung zwischen Benutzern und Organisationen. Jeder Benutzer ist Mitglied von höchstens einer Organisation.

Jetzt möchte ich nach Benutzern suchen, die zu einer bestimmten Zeichenfolge passen. Ich möchte sowohl die Informationen des Benutzers als auch die Informationen des Benutzers zur Organisation der Suche überprüfen, daher sollten die Volltextindizes für dp_users und dp_organisation verwendet werden. Ich habe die folgende Abfrage erstellt, um dies zu erreichen:

SELECT * 
FROM dp_user u 
LEFT JOIN dp_organisation_member m ON m.`UserId` = u.`UserId` 
LEFT JOIN dp_organisation o ON o.`OrganisationId` = m.`OrganisationId` 
WHERE MATCH(u.`Alias`, u.`Firstname`, u.`Surname`, u.`Email`) AGAINST ('foo') 
OR MATCH(o.`Name`, o.`ShortName`) AGAINST ('foo') 

Aber die Abfrage führt wirklich schlecht. Nur um zu testen, habe ich versucht, die folgenden, das nur in den Informationen des Benutzers sucht:

SELECT * 
FROM dp_user u 
LEFT JOIN dp_organisation_member m ON m.`UserId` = u.`UserId` 
LEFT JOIN dp_organisation o ON o.`OrganisationId` = m.`OrganisationId` 
WHERE MATCH(u.`Alias`, u.`Firstname`, u.`Surname`, u.`Email`) AGAINST ('foo') 

Es läuft rund 30-mal schneller.

Wenn ich suche nur in den Informationen der Organisation:

SELECT * 
FROM dp_user u 
LEFT JOIN dp_organisation_member m ON m.`UserId` = u.`UserId` 
LEFT JOIN dp_organisation o ON o.`OrganisationId` = m.`OrganisationId` 
WHERE MATCH(o.`Name`, o.`ShortName`) AGAINST ('foo') 

Die Abfrage wieder langsam ist.

Um zu überprüfen, dass es in dp_organisation kehrte ich die Anfragen von dp_organisation und kommen dp_user auszuwählen nichts falsch mit dem Volltextindex ist:

SELECT * 
FROM dp_organisation o 
LEFT JOIN dp_organisation_member m ON m.`OrganisationId` = o.`OrganisationId` 
LEFT JOIN dp_user u ON u.`UserId` = m.`UserId` 
WHERE MATCH(u.`Alias`, u.`Firstname`, u.`Surname`, u.`Email`) AGAINST ('foo') 
OR MATCH(o.`Name`, o.`ShortName`) AGAINST ('foo') 

Die obige Abfrage ist langsam, und so ist das eine einzige Suche in den Benutzerinformationen:

SELECT * 
FROM dp_organisation o 
LEFT JOIN dp_organisation_member m ON m.`OrganisationId` = o.`OrganisationId` 
LEFT JOIN dp_user u ON u.`UserId` = m.`UserId` 
WHERE MATCH(u.`Alias`, u.`Firstname`, u.`Surname`, u.`Email`) AGAINST ('foo') 

Die Abfrage nur in der Organisation der Informationssuche ist jedoch schnell (ca. 25 mal schneller):

SELECT * 
FROM dp_organisation o 
LEFT JOIN dp_organisation_member m ON m.`OrganisationId` = o.`OrganisationId` 
LEFT JOIN dp_user u ON u.`UserId` = m.`UserId` 
WHERE MATCH(o.`Name`, o.`ShortName`) AGAINST ('foo') 

So scheint es, dass ich nur eine gute Leistung bei der Volltextsuche in der Haupttabelle bekomme, und nicht diejenigen, die in dieser Tabelle verbunden sind. Was kann ich tun, um bei einer Volltextsuche in einer Join-Tabelle eine gute Leistung zu erzielen?

Antwort

0

Die Kombination von FTS und JOIN in Ihren Abfragen führt zu einer Verlangsamung, da mysql im Allgemeinen nur einen Index pro Tabelle verwendet. Wenn Sie einen FTS für eine Tabelle ausführen, verwendet mysql den Volltextindex für diese Tabelle. Daher ist es nicht möglich, einen Index für den Join zu verwenden.

In anderen Nachrichten, die Indizes für die Tabelle dp_organization_member ist nicht sehr sinnvoll. Sie haben das Feld user_id eindeutig gemacht. Das bedeutet, dass ein Benutzer nur zu einer Organisation gehören kann, was bedeutet, dass die Tabelle dp_organisation_member redundant ist. Du hast über normalisiert. Sie können diese Tabelle löschen und eine Organisations-ID zu dp_user hinzufügen und einen Ihrer Joins löschen.

0

Ich empfehle den Wechsel zu InnoDB für Starter. Ab 5.6.4 war FULLTEXT damit verfügbar. Es sind few differences zu beachten.

Wenn der Optimierer zwischen einer MATCH und einer anderen Art von Filter wählen kann, wird es die FULLTEXT tun, nicht die andere.

WHERE MATCH... OR MATCH... ist schlecht wegen der OR. Und FULTEXT macht hier schlecht. Es ist eine mögliche Problemumgehung, es in (SELECT ... MATCH) UNION (SELECT ... MATCH) zu drehen.

LEFT JOIN gerne zuerst auf der 'linken' Tabelle filtern. Es ist also in Ordnung, dass diese Tabelle FULLTEXT verwendet, aber nicht die 'richtige' Tabelle. Im Allgemeinen verwenden Sie LEFT nicht, wenn Sie es nicht benötigen.

Verwandte Themen