【问题标题】:How to restrict query set to a subset of IDs in MySQL?如何将查询集限制为 MySQL 中的 ID 子集?
【发布时间】:2020-10-10 03:38:52
【问题描述】:

我有一个 USERSEARCH 表,应该用于快速搜索用户的子字符串。此功能适用于在有人输入用户名或名称时发生的自动完成搜索。但是,我感兴趣的查询只会显示该人关注的用户的匹配项。

USERSEARCH
-----------------------------------------------
user_id(FK)    username_ngram          name_ngram
1              "AleBoy leBoy eBoy..."  "Ale le e"
2              "craze123 raze123 ..."  "Craze raze aze ze e"
3              "john1990 ohn1990 ..."  "John ohn hn n"
4              "JJ_1 J_1 _1 1"         "JJ"


USERRELATIONSHIP
-----------------------------------------------
user_id(FK)    follows_id(FK)
2              1
2              3

以下查询在某人刚刚输入“Al”时执行:

SELECT * FROM rage.usersearch where username_ngram like 'Al%' --1
UNION DISTINCT
SELECT * FROM rage.usersearch where name_ngram like 'Al%'  --2
UNION DISTINCT
SELECT * FROM rage.usersearch                             --3
WHERE MATCH (username_ngram, name_ngram) AGAINST ('Al')  
LIMIT 10

指数

index(user_id)
index(username_ngram)
index(name_ngram)
FULLTEXT(username_ngram, name_ngram)

有没有办法限制上述查询只查看 user_ids 的这个子集(每个子查询不查询 3 次)?

SELECT follows_id FROM rage.userrelationship WHERE user_id={user_id of user doing the searching} 

【问题讨论】:

  • 全文索引不适用于 LIKE。搜索“UNION 优化”。
  • @PaulSpiegel 像我提到的两个正常指数怎么样?我知道他们帮助 LIKE {string}%
  • 这可能会有所帮助:sql-performance-union-vs-or
  • LIKE '{string}%' 是范围搜索。 MySQL 无法对一个索引执行两次(有效)范围搜索。
  • 此 LIMIT 作用于完整的 UNION 集。请参阅UNION Clause“要将 ORDER BY 或 LIMIT 子句应用于单个 SELECT,请将 SELECT 括起来并将子句放在括号内”。你没有 - 所以它作用于全套。

标签: mysql sql database database-design database-indexes


【解决方案1】:

MySQL 每个表引用只能使用一个 INDEX。每个索引也只能使用一次范围扫描。因此,两列上的两个单独索引或两列上的复合索引都不会阻止全表扫描。 FULLTEXT 索引不适用于LIKE。优化此查询的最佳方法是在一个 UNION 查询中合并两个单独的搜索:

SELECT user_id FROM myapp.usersearch WHERE username_ngram LIKE '{string}%'
UNION DISTINCT
SELECT user_id FROM myapp.usersearch WHERE name_ngram LIKE '{string}%'

引擎现在可以将INDEX(username_ngram) 用于第一个查询部分,将INDEX(name_ngram) 用于第二个查询部分。

【讨论】:

  • 这更有意义,感谢您链接文章。上面的查询会很快,因为它会使用: index(username_ngram) 和 index(name_ngram) 是否正确?
  • 对,-各个部分将使用一个索引。
  • 如果表有一个 FULLTEXT(username_ngram, name_ngram) 索引并且查询是:SELECT id, username, name FROM table WHERE MATCH (username, name) AGAINST ({string});跨度>
  • 全文索引是全词索引,所以只能查找全词,不能用于自动补全。例如。当您输入“John”时,它不会找到“Johnson”。
  • 没错,但是看看我的 ngram 专栏,他们利用了这个。
【解决方案2】:

如果您将收到字符串的前几个字母,我看不到 ngram 的实用性。

此时,这是最佳选择:

SELECT ... WHERE name LIKE 'Al%'
    LIMIT 10;

INDEX(name).

如果您需要将UNION ALLLIMIT 一起使用,请执行以下操作:

( SELECT ... ORDER BY .. LIMIT 10 )
UNION ALL
( SELECT ... ORDER BY .. LIMIT 10 )
ORDER BY .. LIMIT 10

如果您不重复 LIMIT,每个子查询都会收集所有相关行,从而创建一个比必要更大的临时表。

如果您将使用OFFSET(可能不适用于此应用),请参阅以下内容了解如何使用:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#or

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-31
    • 2013-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多