Her er et par "spilleregler", som du skal huske på for at løse dette problem. Du kender dem sikkert allerede, men at angive dem tydeligt kan være med til at bekræfte for andre læsere.
- Alle indekser i MySQL kan kun referere til kolonner i en enkelt basistabel. Du kan ikke lave et fuldtekstindeks, der indekserer på tværs af flere tabeller.
- Du kan ikke definere indekser for visninger, kun basistabeller.
- En
MATCH()
forespørgsel mod et fuldtekstindeks skal matche mod alle kolonner i fuldtekstindekset i den rækkefølge, der er angivet i indekset.
Jeg ville oprette en tredje tabel til at gemme det indhold, du vil indeksere. Det er ikke nødvendigt at gemme dette indhold redundant - gem det udelukkende i den tredje tabel. Dette låner et koncept om en "fælles superklasse" fra objektorienteret design (i det omfang vi kan anvende det på RDBMS-design).
CREATE TABLE Searchable (
`id` SERIAL PRIMARY KEY,
`title` varchar(100) default NULL,
`description` text,
`keywords` text,
`url` varchar(255) default '',
FULLTEXT KEY `TitleDescFullText` (`keywords`,`title`,`description`,`url`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `shopitems` (
`id` INT UNSIGNED NOT NULL,
`ShopID` INT UNSIGNED NOT NULL,
`ImageID` INT UNSIGNED NOT NULL,
`pricing` varchar(45) NOT NULL,
`datetime_created` datetime NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`id`) REFERENCES Searchable (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `shops` (
`id` INT UNSIGNED NOT NULL,
`owner_id` varchar(255) default NULL,
`datetime_created` datetime default NULL,
`created_by` varchar(255) default NULL,
`datetime_modified` datetime default NULL,
`modified_by` varchar(255) default NULL,
`overall_rating_avg` decimal(4,2) default '0.00',
PRIMARY KEY (`id`),
FOREIGN KEY (`id`) REFERENCES Searchable (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Bemærk, at den eneste tabel med en nøgle til automatisk stigning nu er Searchable
. Tabellerne shops
og shopitems
bruge en nøgle med en kompatibel datatype, men ikke automatisk stigning. Så du skal oprette en række i Searchable
for at generere id
værdi, før du kan oprette den tilsvarende række i enten shops
eller shopitems
.
Jeg har tilføjet FOREIGN KEY
erklæringer til illustrationsformål, selvom MyISAM stille vil ignorere disse begrænsninger (og du ved allerede, at du skal bruge MyISAM for at understøtte fuldtekstindeksering).
Nu kan du søge i tekstindholdet i begge shops
og shopitems
i en enkelt forespørgsel ved hjælp af et enkelt fuldtekstindeks:
SELECT S.*, sh.*, si.*,
MATCH(keywords, title, description, url) AGAINST('dummy') As score
FROM Searchable S
LEFT OUTER JOIN shops sh ON (S.id = sh.id)
LEFT OUTER JOIN shopitems si ON (S.id = si.id)
WHERE MATCH(keywords, title, description, url) AGAINST('dummy')
ORDER BY score DESC;
Selvfølgelig for en given række i Searchable
kun én tabel skal matche, enten butikker eller shopitems, og disse tabeller har forskellige kolonner. Så enten sh.*
eller si.*
vil være NULL i resultatet. Det er op til dig at formatere outputtet i din applikation.
Et par andre svar har foreslået at bruge Sphinx Search . Dette er en anden teknologi, der komplementerer MySQL og tilføjer mere sofistikeret fuldtekst søgefunktion. Den har fantastisk ydeevne til forespørgsler, så nogle mennesker er blevet ret fortryllede af den.
Men det er dyrt at oprette indekser og især tilføje til et indeks trinvist. Faktisk er det så dyrt at opdatere et Sphinx Search-indeks, at den anbefalede løsning er at oprette et indeks for ældre, arkiverede data og et andet mindre indeks for nyere data, der er mere tilbøjelige til at blive opdateret. Derefter skal hver søgning køre to forespørgsler mod de to separate indekser. Og hvis dine data ikke naturligt egner sig til, at mønsteret med ældre data er uændrede, så kan du muligvis ikke drage fordel af dette trick alligevel.
Om din kommentar:Her er et uddrag fra Sphinx Search-dokumentationen om live-opdateringer til et indeks:
Tanken er, at da det er dyrt at opdatere et Sphinx Search-indeks, er deres løsning at gøre det indeks, du opdaterer, så lille som muligt. Så kun de seneste forumindlæg (i deres eksempel), hvorimod den større historie af arkiverede forumindlæg aldrig ændres, så du bygger et andet, større indeks for den samling én gang. Hvis du vil foretage en søgning, skal du naturligvis forespørge i begge indekser.
Med jævne mellemrum, f.eks. en gang om ugen, ville de "seneste" forummeddelelser blive betragtet som "arkiverede", og du skulle flette det aktuelle indeks for seneste indlæg til det arkiverede indeks og starte det mindre indeks forfra. De gør opmærksom på, at sammenlægning af to Sphinx Search-indekser er mere effektiv end genindeksering efter en opdatering af dataene.
Men min pointe er, at ikke alle datasæt naturligt falder ind i mønsteret med at have et arkiveret datasæt, der aldrig ændrer sig, i forhold til nyere data, der opdateres ofte.
Tag din database for eksempel:Du har butikker og butiksvarer. Hvordan kan du adskille disse i rækker, der aldrig ændrer sig, kontra nye rækker? Alle butikker eller produkter i kataloget bør have tilladelse til at opdatere deres beskrivelse. Men da det ville kræve at genopbygge hele Sphinx Search-indekset, hver gang du foretager en ændring, bliver det en meget dyr operation. Måske ville du stille ændringer i kø og anvende dem i en batch og genopbygge indekset en gang om ugen. Men prøv at forklare butiksleverandørerne, hvorfor en mindre ændring af deres butiksbeskrivelse først træder i kraft søndag aften.