【问题标题】:Postgresql query to search by first and last name, what indexes should I create?Postgresql 查询按名字和姓氏搜索,我应该创建哪些索引?
【发布时间】:2020-10-10 15:29:04
【问题描述】:

我正在使用此查询按“名字和姓氏”或“姓氏和名字”进行搜索:

SELECT
    "id",
    "firstname",
    "lastname"
FROM
    "plyers"
WHERE
  lower( REPLACE ( CONCAT ( lastname, firstname ), ' ', '' ) ) LIKE '%joh%'
  OR
  lower( REPLACE ( CONCAT ( firstname, lastname ), ' ', '' ) ) LIKE '%joh%'
ORDER BY "id" DESC

鉴于

  1. 我正在使用 Postgresql 13

  2. 用户可以使用“John Belushi”或“Belushi John”或“lushi”或“lushi jo”进行搜索

  3. 搜索文本(在这种情况下为joh)始终为lowered(),并清除了每个空格:每个单词之前、期间和之后。例如。如果我写“ lushi JO ”,最终的搜索文本是:“lushijo

  4. 此查询经常用于搜索列表中的玩家并自动完成某些选择

问题

  1. 你认为这是一个很好的查询吗?

  2. 您认为有什么方法可以避免CONCAT(lastname,firstname)CONCAT(firstname,lastname)

  3. 我应该创建哪些索引?我读到了这个:https://stackoverflow.com/a/2709967/10088259,但我不太明白如何使用它。

【问题讨论】:

  • @MarkByers 我引用了你的答案,你能举个例子更好地解释一下吗?
  • 在任何数据库中,索引仅在 WHERE(或 JOIN 条件,就此而言)对直接与列中的值进行比较的表达式起作用时才有帮助。一旦您与列中的值的派生进行比较,任何可能存在的索引都只是烤面包。并且将几个谓词组合在一起增加了不可优化的复杂性......
  • @marcothesane,谢谢。您如何看待这个查询?
  • 您是否允许人们(成功地)使用“ushijo”或“hnbel”进行搜索。如果不是,那么在没有空格的情况下连接它们似乎是错误的做法。
  • 您的问题 2 似乎直接由您给定的数字 3 回答。那给定的不是真的给定的吗?

标签: sql postgresql performance


【解决方案1】:

使用generated columns,并在上面放置索引。

ALTER TABLE test
    ADD COLUMN first_last text GENERATED ALWAYS AS (lower(REPLACE(first || last, ' ', ''))) STORED;

CREATE INDEX IF NOT EXISTS players_first_last_idx ON test USING gin (first_last gin_trgm_ops);

ALTER TABLE test
    ADD COLUMN last_first text GENERATED ALWAYS AS (lower(REPLACE(last || first, ' ', ''))) STORED;

CREATE INDEX IF NOT EXISTS players_first_last_idx ON test USING gin (last_first gin_trgm_ops);

现在WHERE 子句将能够使用索引:

SELECT
    "id",
    "firstname",
    "lastname"
FROM
    "plyers"
WHERE
  first_last LIKE '%joh%'
  OR
  last_first LIKE '%joh%'
ORDER BY "id" DESC

LIKE 查询创建索引的好解释:https://niallburkley.com/blog/index-columns-for-like-in-postgres/

【讨论】:

    【解决方案2】:

    让我用“答案”而不是“评论”来回答...

    如果您想考虑到您建议的多功能性 -

    1. 只有在字符串开头允许使用区分大小写的搜索参数时,才能实现快速数据访问——利用任何现有索引。我这种类型的最大表从未在 PostreSQL 中,而是在 3 或 4 个其他品牌中。但是所有这些在使用索引时,使用WHERE fname >='Johnny' AND fname < 'Johnz' 比使用WHERE NAME LIKE 'Johnny%' 做得更好。实际上,我曾经编写过一个前端代码来为我做这个改变......
    2. 如果可以的话,在表格中实际添加一列 fullname VARCHAR(128) DEFAULT firstname||' '||lastname 会有所帮助(猜测的长度大约是 firstnamelastname 长度的两倍 - 在不截断字符串的情况下保持尽可能短.
    3. firstnamelastname 和您的新fullname 添加非唯一的单列索引。
    4. 最后,坦率地向最终用户开放,告诉他们“开始于”谓词总是比“包含”谓词快得多 - 更不用说不区分大小写的谓词了。

    如果您的前端是普通的 SQL 客户端,则最终用户必须对其查询进行编码以遵守上述几点。

    如果您构建自己的前端应用程序,请在搜索功能中允许“等于,区分大小写”、“以.开头。区分大小写”以及与 firstnamelastname 的比较,以及,如果这是有道理的,@ 987654331@ - 作为首选搜索方法,但也适用于“包含”、“开始于” - “等于”和“包含”,均不区分大小写 - 但带有警告,在对话或文档中,后者的速度要慢得多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-28
      • 2011-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多