SQL匹配所有关键字的开头

时间:2014-07-26 03:51:09

标签: php mysql sql jquery-ui

我将设置一个场景来最好地描述我想要完成的任务。

有一个自动填充字段。自动完成功能适用于电视节目。用户输入“The Wal”希望找到“行尸走肉”。

数据库:

CREATE TABLE `shows` (
  `id` int(10) unsigned NOT NULL,
  `name` varchar(250) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `tags` (
  `tag` varchar(50) NOT NULL DEFAULT '',
  `sid` int(10) unsigned NOT NULL,
  KEY `sid` (`sid`),
  KEY `alphabetizer` (`tag`),
  CONSTRAINT `tags_ibfk_1` FOREIGN KEY (`sid`) REFERENCES `shows` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

其中shows是所有电视节目的表格,tags是与每个电视节目相关的所有标签的表格。

每个节目标题中的每个单词都作为自己的小写标记插入tags表中。

shows表中:

  • (id:1)(姓名:行尸走肉)
  • (id:2)(姓名:流浪企鹅)

tags表中:

  • (sid:1)(tag:the)
  • (sid:1)(tag:walking)
  • (sid:1)(tag:dead)
  • (sid:2)(tag:the)
  • (sid:2)(标签:徘徊)
  • (sid:2)(tag:penguin)

目标:用户输入“The Wal”,用户获取:“行尸走肉”。查询应返回符合条件的所有结果,而不仅仅是一个。因此,如果“The Walking Alive”也是带有相应标签的节目,它也应该出现。

我的问题:用户输入“The Wal”,用户获得两个节目。这是由LIKE语句的OR子句引起的。尝试2天后,我不知道如何解决这个问题。

我当前的查询:

SELECT name
    FROM shows s
    JOIN tags t ON s.id = t.sid
    WHERE t.tag LIKE "The%" OR t.tag LIKE "Wal%"

4 个答案:

答案 0 :(得分:1)

一种方法是使用and代替or。但是,您需要使用聚合来获得所需内容:

SELECT name
FROM shows s JOIN
     tags t
     ON s.id = t.sid
WHERE t.tag LIKE 'The%' OR t.tag LIKE 'Wal%'
GROUP BY name
HAVING sum(t.tag LIKE 'The%') > 0 AND
       sum(t.tag LIKE 'Wal%') > 0;

但是,我不认为这可以解决您的问题,因为您不知道所有关键字都会匹配。相反,按匹配的关键字数量排序并选择最匹配的关键字:

SELECT name
FROM shows s JOIN
     tags t
     ON s.id = t.sid
WHERE t.tag LIKE 'The%' or t.tag LIKE 'Wal%'
GROUP BY name
ORDER BY (MAX(t.tag LIKE 'The%') +
          MAX(t.tag LIKE 'Wal%')
         ) DESC
LIMIT 1;

答案 1 :(得分:0)

运行此查询的另一种方法是为每个标记添加EXISTS语句。此查询可以利用tag(sid,tag)

上的复合索引
SELECT name
FROM shows s 
WHERE EXISTS (
    SELECT 1 FROM tags t
    WHERE t.sid = s.id
    AND tag LIKE 'The%'
) AND EXISTS (
    SELECT 1 FROM tags t
    WHERE t.sid = s.id
    AND tag LIKE 'Wal%'
)

答案 2 :(得分:0)

我不认为你的方法是合理的。但这里是对正在发生的事情的解释。

当您加入showstags时,您会为到目前为止匹配的每个字词获得一条记录。

根据您的示例,使用

1. The Walking Dead
2. The Wandering Penguin

这个查询

SELECT *
FROM shows s
JOIN tags t ON s.id = t.sid
WHERE t.tag LIKE "the%" OR t.tag LIKE "wa%" /* note lower-case query */

你会得到结果

ID  NAME                  TAG
 1  The Walking Dead      the
 1  The Walking Dead      walking
 2  The Wandering Penguin the
 2  The Wandering Penguin wandering

如果您的查询中有三个匹配的单词,则会看到不同的结果

  

查询:Wa Dead

SELECT *
FROM shows s
JOIN tags t ON s.id = t.sid
WHERE t.tag LIKE "the%" OR t.tag LIKE "wa%" OR t.tag LIKE "dead"

会给出

ID  NAME                  TAG
 1  The Walking Dead      the
 1  The Walking Dead      walking
 1  The Walking Dead      dead
 2  The Wandering Penguin the
 2  The Wandering Penguin wandering

您可以使用GROUP BY消除重复项,并使用COUNT(*)匹配的字词数对结果进行评分

SELECT s.name, COUNT(*)
FROM shows s
JOIN tags t ON s.id = t.sid
WHERE t.tag LIKE "the%" OR t.tag LIKE "wa%" OR t.tag LIKE "dead"
GROUP BY NAME
ORDER BY COUNT(*) DESC

给出

 NAME                COUNT(*)
 The Walking Dead      3
 The Wandering Penguin 2

你应该做什么

我认为从长远来看,这种索引方法可能不会很好。现代数据库具有内置功能。 Here is a link to MySQL's full-text index feature。这种情况下,当tags表达到数百万行并且showstags的JOIN变得无法管理时,使用数据库的本机功能可能会让您无后顾之忧。 。

答案 3 :(得分:0)

我认为接受的答案过于复杂。只需添加输入参数" tags_count"并使用它:

SELECT sid
  FROM tags t 
  WHERE t.tag LIKE "The%" OR t.tag LIKE "Wal%"
  GROUP BY sid
  HAVING count(distinct tag) = 2;

因此我们只查询包含所有指定标签的节目