【问题标题】:Performance of an SQL query for finding users in database用于在数据库中查找用户的 SQL 查询的性能
【发布时间】:2018-12-20 14:02:46
【问题描述】:

我正在开发一个应用程序,我正在使用 Spring Data JPA 进行数据库操作。

我正在实现一个使用搜索栏查找朋友的功能。它应该工作的方式是,当您输入一些字符时,会在数据库中执行搜索以查找并显示与字符匹配的 10 个用户(名字以它开头,姓氏以它开头,它也应该如果有人输入全名,则工作)。

我已经编写了代码,它的工作方式如下:

@Query(nativeQuery = true,
        value="SELECT * FROM users u WHERE (" +
                "UPPER(first_name) like CONCAT(UPPER(:query),'%') OR " +
                "UPPER(last_name) like CONCAT(UPPER(:query),'%') OR " +
                "UPPER(CONCAT(first_name,' ', last_name)) like CONCAT(UPPER(:query),'%')" +
                ") LIMIT 10")
List<User> findByQueryLikeName(@Param("query") String query);

但是,当我查看此查询时,它确实似乎不是最好的性能。我对 sql 性能了解不多,但我认为这是因为它结合了 3 个 where 语句和一个 OR 运算符,并且还大量使用了 UPPERCONCAT 等一些函数。我正在使用 PostgreSQL。

您能否评估一下这种查询是否会在大量记录上表现良好?你能试着解释一下为什么/为什么不?你有什么改进的小窍门吗?

非常感谢!

【问题讨论】:

  • 名字和姓氏的连接将排除您的 SQL 使用索引的任何机会。但是,即使在此之前,将列包装在 UPPER 中也可能会导致无法使用索引。
  • @TimBiegeleisen:除非您使用该表达式创建索引。
  • Postgres' full text search 可能值得一看。或trigram indexes
  • 是的,你需要三个索引,每个表达式一个,然后你可以期待BitmapOr。这是一个相当糟糕的查询。
  • upper 的要点是使搜索不区分大小写。连接的目的是启用名字和姓氏的搜索。例如,对于亚当·史密斯,我想匹配诸如“亚当·斯米”之类的查询。您能否建议我如何改进我的 SQL 查询,以便我可以使用索引并仍然拥有这些功能?

标签: sql postgresql performance spring-data-jpa


【解决方案1】:

我会这样重写查询:

SELECT * FROM users u
WHERE concat(' ', u.first_name,' ', u.last_name) ILIKE ' ' || :query || '%';

您必须创建一个三元组索引来支持这一点:

CREATE EXTENSION pg_trgm;

CREATE INDEX ON users USING gin
   (concat(' ', u.first_name,' ', u.last_name) gin_trgm_ops);

【讨论】:

    【解决方案2】:

    正如在 cmets 中指出的那样,它可能很慢,因为您在 WHERE 子句中使用了函数:

    https://www.mssqltips.com/sqlservertip/1236/avoid-sql-server-functions-in-the-where-clause-for-performance/

    或者,您可以向您的实体添加另一个字段 fullName 并在 setFirstName()setLastName() 中更新它,如下所示:

    void setFirstName(String first){
     this.fullName = first.toUpperCase() + " " + this.lastName.toUpperCase(); //TODO handle nulls
    }
    

    然后你可以通过 fullName 进行查询,它已经是大写的,然后你在 java 中搜索字符串并有一个简单的类似查询:

    public List&lt;User&gt; findByFullnameContaining(String searchText, Pageable pageable);

    【讨论】:

    • 谢谢,我最终使用了这个解决方案。我在表中添加了一列仅用于搜索,我将名字和姓氏连接起来并大写。这可能是表中的数据重复,但它使索引超级容易
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-29
    • 2017-01-14
    • 2010-09-27
    • 1970-01-01
    • 2021-11-15
    • 1970-01-01
    • 2016-05-28
    相关资源
    最近更新 更多