MySQL - 文本搜索和数据库结构

时间:2015-12-31 16:34:26

标签: mysql regex search text full-text-search

这是我目前的数据库结构:

CREATE TABLE `books` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  `year` year(4) NOT NULL DEFAULT '0000',
  `author` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  UNIQUE KEY `title` (`title`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;

CREATE TABLE `chapters` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `book_id` int(10) unsigned NOT NULL DEFAULT '0',
  `number` int(10) unsigned NOT NULL DEFAULT '0',
  `title` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `book_id` (`book_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;

ALTER TABLE `chapters`
  ADD CONSTRAINT `chapters_ibfk_1` FOREIGN KEY (`book_id`) REFERENCES `books` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

CREATE TABLE `pages` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `book_id` int(10) unsigned NOT NULL DEFAULT '0',
  `chapter_id` int(10) unsigned NOT NULL DEFAULT '0',
  `number` int(10) unsigned NOT NULL DEFAULT '0',
  `text` text COLLATE utf8_unicode_ci NOT NULL,
  `words` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `book_id` (`book_id`),
  KEY `chapter_id` (`chapter_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;

ALTER TABLE `pages`
  ADD CONSTRAINT `pages_ibfk_1` FOREIGN KEY (`book_id`) REFERENCES `books` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
  ADD CONSTRAINT `pages_ibfk_2` FOREIGN KEY (`chapter_id`) REFERENCES `chapters` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;

结构很简单......基本上我是逐页提取书籍文本并将所有内容存储到我的数据库中,该数据库被组织成一本书> chapter>页面系统。我试图让它尽可能灵活,这样我就可以轻松地在整本书的观点或章节中汇总数据......但如果你认为我可以做得更好,我会接受任何建议!

现在,我想允许用户在书籍中执行关键字搜索...这样他们就可以在他们从下拉列表中选择的书中搜索单个单词甚至短语的所有出现。 / p>

我的网络服务器不在存储MySQL数据库的同一台机器上(我在短期内无法摆脱的技术问题)...所以为了避免两台机器之间的巨大数据流量,我更喜欢运行文本搜索SQL查询。检索所有页面并使用PHP分析它们每次都会转化为5-10 Mb的数据。

现在我的问题是:

  • 是否可以仅使用查询命令(LIKEMATCHREPLACE等来执行此类流程?)?
  • 我将通过以下方式获取按页面格式化的结果:[第1页| 0次出现],[第2页| 1次发生],[第3页| 1次发生],[第4页| 2次出现] ......可能吗?
  • 您认为在将文本存储到字段pages.text之前从页面文本中删除间距字符(换行符,制表符等)和标点符号是个好主意吗?

感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

试试这个:

SELECT p.number, ROUND (   
        (
            LENGTH(p.text)
            - LENGTH( REPLACE ( p.text, "{your string here}", "") ) 
        ) / LENGTH("{your string here}")        
    ) AS count  
FROM pages p
JOIN
chapters c ON c.id = p.chapter_id
JOIN
books b on b.id = c.book_id
WHERE b.title = {your book title}
GROUP BY p.number

-- solution inspired by:
-- http://stackoverflow.com/questions/12344795/count-the-number-of-occurences-of-a-string-in-a-varchar-field

你可以删除空格,但是你可能会遇到一些问题:

  • 您还需要删除搜索查询(不是真正的问题,只是额外的工作)

  • 更重要的是,如果您想向用户显示页面的全文,或者甚至只是一个摘录,您将无法知道空白字符的位置。

答案 1 :(得分:1)

不要担心MySQL和应用程序在不同的计算机上。 (当然,你需要使用TCP,而不是" localhost"。)"大玩家"这样分开。

FULLTEXT(text)pages,然后说

FROM pages AS p
JOIN book AS b ON ...
WHERE MATCH (p.text) AGAINST (...)
  AND b.id = ...

其他说明:

使用合适的GROUP BY,您可以接近页面+计数,就像您要求的那样。

你真的想对某些页面说" 0次出现?如果'预订'是500页长;你真的想要500行输出吗?

请注意FULLTEXT次搜索的限制(仅限单词,词干,最小字数,"停止"单词等),如果用户未能遵循这些限制,请处理它们。

有时我这样做:如果用户在查询中输入*%,我会使用REGEXPLIKE代替FULLTEXT,警告用户它会慢一些。

FULLTEXT出现时,MATCH索引将首先用于 ,然后它会在书籍上过滤。使用LIKEREGEXP,它会在执行繁琐的搜索之前过滤book_id。注意:这意味着"构建"用PHP(或任何语言)动态查询。

完成所有(或大部分)工作可以减少网络流量,这是您关注的问题之一。

否"剥离"需要。 FULLTEXT 需要字边界。

每页计数:

SELECT p.number AS 'PageNumber',
       COUNT(*) AS 'Occurrences'
    ...
    GROUP BY p.id