Select 包含所有指示值的 ID(通配符冲突问题)MariaDB

Select id's containing all indicated Values (Wildcard collisions problem) MariaDB

有问题的 table (10.1.29-MariaDB):

  [Table] => blog_search
  [Create Table] => CREATE TABLE `blog_search` (
  `bkey` varchar(28) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `blog_id` int(10) unsigned NOT NULL,
  `follows` int(10) unsigned NOT NULL,
  UNIQUE KEY `bkey` (`bkey`,`blog_id`),
  UNIQUE KEY `blog_id` (`blog_id`,`bkey`) USING BTREE,
  KEY `bkey_follows` (`bkey`,`follows`,`blog_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin

bkey 是博客标题中使用的一个词。 follows 数据将在每个博客的每个词中重复。

我有一个 Select 在没有通配符的情况下工作得很好(数据库选择的键=>bkey_follow):

SELECT blog_id
FROM blog_search
WHERE bkey = 'news' OR bkey = 'usa'
GROUP BY blog_id
HAVING COUNT(*) > 1
ORDER BY FOLLOWS DESC
LIMIT 30

但是当我使用通配符时(key=>bkey 由数据库选择):

WHERE bkey LIKE 'news%' OR bkey LIKE 'usa%'

它仍然可以进行索引搜索,但是“news”和“newsletter”关键字将匹配为 COUNT( *) = 2,但它不会找到 'usa'

我做了这个新查询,它按我想要的方式工作,但是如果最后一个词有很多结果,查询就会变得很慢(因为查询是从后到前执行的,并且必须读取 100% 的结果index) (key=>blog_id 由数据库选择):

SELECT blog_id
FROM blog_search
WHERE  
  blog_id IN (SELECT blog_id FROM blog_search WHERE bkey LIKE 'news%') 
  AND blog_id IN (SELECT blog_id FROM blog_search WHERE bkey LIKE 'usa%')
GROUP BY blog_id
ORDER BY follows DESC
LIMIT 30

如果我 FORCE INDEX (bkey) 甚至更好 FORCE INDEX (bkey_follows) 它会提高一点点,比如 35%,但仍然比我认为应该可能的速度慢 10 倍。

我想要一个没有 sub-queries 的更优化的查询。像这样的 WRONG 查询,(但正确的版本):

SELECT blog_id, 0 AS dist1, 0 AS dist2
FROM blog_search
WHERE ( bkey LIKE 'news%' AND 1=dist1=1 )
   OR ( bkey LIKE 'usa%' AND 1=dist2=1 )
GROUP BY blog_id
HAVING (dist1=1 AND dist2=1)
ORDER BY follows DESC
LIMIT 30

其他数据:我正在开发 blogs/posts 标题的搜索。我尝试了很多东西:Regex、LIKE %...% 和 FULLTEXT 索引,我 "approved" 将其用于关联排序结果。更有前途的搜索是这个问题中讨论的搜索,因为它的速度和按 'follows'.

排序结果的速度是最好的

您可以将 having 子句表述为:

SELECT blog_id
FROM blog_search
WHERE bkey LIKE 'news%' OR bkey = 'usa%'
GROUP BY blog_id
HAVING SUM( bkey LIKE 'news%' ) > 0 AND
       SUM( bkey LIKE 'usa%' ) > 0
ORDER BY FOLLOWS DESC
LIMIT 30

在聚合之前进行过滤的一种便捷方式是使用正则表达式:

SELECT blog_id
FROM blog_search
WHERE bkey REGEXP 'news|use' 
GROUP BY blog_id
HAVING SUM( bkey LIKE 'news%' ) > 0 AND
       SUM( bkey LIKE 'usa%' ) > 0
ORDER BY FOLLOWS DESC
LIMIT 30

您不关心有多少行匹配。许多其他解决方案会导致 "explode-implode" 综合症。那是您收集大量行的地方,只是后来通过 GROUP BY 归结为几行。这通过使用 "semi-join"、EXISTS.

避免了一半
SELECT DISTINCT blog_id
    FROM blog_search AS o
    WHERE WHERE bkey LIKE 'news%'
      AND EXISTS ( SELECT blog_id FROM blog_search
                      WHERE bkey LIKE 'usa%'
                        AND blog_id = o.blog_id )
ORDER BY follows DESC
LIMIT 30