2010-07-16 9 views
7

Tôi có ba bảng trong một cơ sở dữ liệu MySQL được sử dụng trong một ứng dụng thư viện âm nhạc:MySQL từ khóa tìm kiếm trên nhiều bảng

Bảng Genre có các cột:

  • id
  • title (string)

Bảng Album có các cột:

  • id
  • genre_id (chính nước ngoài để Genre.id)
  • title (chuỗi)
  • artist (chuỗi)

Track bảng có các cột:

  • id
  • album_id (khoá ngoại để Album.id)
  • title (chuỗi)

Mỗi Album có thể có bất kỳ số lượng Tracks, mỗi Track có một Album, và mỗi Album có một Genre.


Tôi muốn thực hiện một tìm kiếm từ khóa đó cho phép người dùng nhập bất kỳ số lượng từ khóa và tìm thấy tất cả Tracks rằng:

  • có một phù hợp với title,
  • đang ở trên một Album với một khớp với title hoặc artist,
  • hoặc đang ở trên Album với một Genre với một kết hợp title.

Kết quả phải được sắp xếp theo mức độ liên quan. Nó sẽ là tuyệt vời nếu mỗi lĩnh vực có một thứ hạng cho sự liên quan. Ví dụ: title của một số Track có thể quan trọng hơn số title của số Genre.

Ngoài ra, giải pháp nên sử dụng một số dạng tìm kiếm một phần. Một tìm kiếm của rubber đầu tiên phải phù hợp với tất cả Tracks với một title của Rubber, sau đó kết hợp với một Trackstitle phù hợp với *rubber* (* = wildcard), sau đó chuyển sang Albums, và vân vân. Tuy nhiên, tôi không đặt những chi tiết này. Tôi chỉ đang tìm một giải pháp tổng quát hơn mà tôi có thể tinh chỉnh để phù hợp với nhu cầu cụ thể của mình.

Tôi cũng nên đề cập rằng tôi đang sử dụng LAMP stack, Linux, Apache, MySQL và PHP.


Cách tốt nhất để triển khai tìm kiếm từ khóa này là gì?


Cập nhật: Tôi đã cố gắng để thực hiện điều này thông qua một tìm kiếm văn bản đầy đủ, và đã đưa ra các câu lệnh SQL sau.

CREATE TABLE `Genre` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `title` text NOT NULL, 
    PRIMARY KEY (`id`), 
    FULLTEXT KEY (`title`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; 

INSERT INTO `Genre` VALUES(1, 'Rock'); 

CREATE TABLE `Album` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `genre_id` int(11) NOT NULL, 
    `title` text NOT NULL, 
    `artist` text, 
    PRIMARY KEY (`id`), 
    FULLTEXT KEY (`title`, `artist`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; 

INSERT INTO `Album` VALUES(1, 1, 'Rubber Soul', 'The Beatles'); 

CREATE TABLE `Track` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `album_id` int(11) NOT NULL, 
    `title` text NOT NULL, 
    PRIMARY KEY (`id`), 
    FULLTEXT KEY (`title`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; 

INSERT INTO `Track` VALUES(1, 1, 'Drive My Car'); 
INSERT INTO `Track` VALUES(2, 1, 'What Goes On'); 
INSERT INTO `Track` VALUES(3, 1, 'Run For Your Life'); 
INSERT INTO `Track` VALUES(4, 1, 'Girl'); 
+0

Bạn đang tìm kiếm kết hợp chính xác hoặc khớp một phần? –

+0

Cảm ơn bạn đã trả lời, tôi đã thêm thông tin thêm vào câu hỏi. –

Trả lời

4

Tôi sẽ sử dụng Apache Solr. Sử dụng Data Import Handler để xác định truy vấn SQL nối tất cả các bảng của bạn với nhau, tạo chỉ mục toàn văn từ kết quả của dữ liệu được nối.


Các cột có tên args đến MATCH() phải là (các) cột bạn đã xác định cho chỉ mục, theo thứ tự bạn đã xác định trong chỉ mục. Nhưng bạn không thể xác định bất kỳ chỉ mục (fulltext hoặc cách khác) trên nhiều bảng trong MySQL.

Vì vậy, bạn không thể làm điều này:

WHERE MATCH (g.title, a.title, a.artist, t.title) AGAINST ('beatles') 

Nó không quan trọng cho dù bạn đang sử dụng chế độ boolean hay chế độ ngôn ngữ tự nhiên.

Bạn cần phải làm điều này:

WHERE MATCH (g.title) AGAINST ('beatles') 
    OR MATCH (a.title, a.artist) AGAINST ('beatles') 
    OR MATCH (t.title) AGAINST ('beatles') 

Bạn cũng có thể quan tâm đến việc trình bày của tôi Practical Full-Text Search in MySQL.

1

Xác định một chỉ số toàn văn trên bốn cột mà bạn muốn tìm kiếm và sau đó làm:

SELECT * FROM genre AS g 
    LEFT JOIN album AS a ON g.id = a.genre_id 
    LEFT JOIN tracks AS t ON a.id = t.album_id 
    WHERE MATCH (g.title, a.title, a.artist, t.title) AGAINST ('searchstring'); 

Các resullt sẽ được sắp xếp theo độ phù hợp. Xem tại đây để biết thêm chi tiết về tìm kiếm toàn văn bản: http://dev.mysql.com/doc/refman/5.0/en/fulltext-natural-language.html

+0

Vấn đề là, OP muốn khớp một phần. Ký tự đại diện hoạt động nhưng chỉ dành cho các ký tự theo chuỗi tìm kiếm. Bạn không thể áp dụng ký tự đại diện * vào đầu chuỗi tìm kiếm –

+0

Tôi nghĩ cách duy nhất để thực hiện đối sánh chính xác trước tiên và sau đó kết hợp THÍCH * abc * trong MySQL là sử dụng hai truy vấn. Giải pháp Fulltext, tôi nghĩ, là gần nhất bạn có thể nhận được chỉ trong một truy vấn. – JochenJung

+0

Công trình này, mặc dù nó phải ở chế độ boolean. Truy vấn đã sửa đổi của tôi là: 'SELECT * FROM Thể loại AS g LEFT THAM GIA Album AS a ON g.id = a.genre_id LEFT JOIN Theo dõi AS t ON a.id = t.album_id WHERE MATCH (g.title, a.title, a .artist, t.title) AGAINST ('beatles' trong chế độ BOOLEAN) ' Khi tôi chạy truy vấn mà không có' IN BOOLEAN MODE', tôi nhận được lỗi '# 1210 - Đối số sai đối với MATCH'. –

0

Tôi sẽ sử dụng thứ gì đó như Sphinx, u có thể tạo chỉ mục trong truy vấn của bạn và sau đó truy vấn. Đó là một chút khó khăn để có được đầu của bạn xung quanh nhưng kết quả là 10 lần tốt hơn so với mysql AGAINST và bạn sẽ không có vấn đề sau này với tốc độ.