【问题标题】:MySQL Sorting Problem for an autocompleteMySQL 自动完成的排序问题
【发布时间】:2011-05-29 02:35:11
【问题描述】:

我的网站上有一个自动完成功能,用户可以在其中输入他们学校的课程。然后,它提供了一个下拉列表,其中包含他们在字段中某处输入的单词。 Classes 表具有 Class_ID、Term_ID、Department_Code、Course_Code、Class_Code、Course_Title、Instructor 字段,这些字段都由用户输入的术语进行搜索。假设用户输入单词“international econ”。然后它将在字段中搜索这两个术语,如下所示:

SELECT Class_ID, Department_Code, Course_Code, Class_Code, Course_Title, Instructor FROM Classes AND ((REPLACE(REPLACE(REPLACE(CONCAT(Department_Code, Course_Code, Class_Code), '-', ''), '(', ''), ')', '') LIKE '%international%' OR Instructor LIKE '%international%' OR Course_Title LIKE '%international%') AND (REPLACE(REPLACE(REPLACE(CONCAT(Department_Code, Course_Code, Class_Code), '-', ''), '(', ''), ')', '') LIKE '%econ%' OR Instructor LIKE '%econ%' OR Course_Title LIKE '%econ%')) LIMIT 10

关于这个查询的两个快速说明——我做了 REPLACE() 和 CONCAT() 以便用户可以输入部门/课程/部分作为一个单元,例如ECON-101(一个部门和一个课程一起)。此外,我还粘贴了一个大大简化的查询,省略了我目前的排序和分组方式。

但是我的问题涉及如何对结果进行排序。我希望在顶部返回与 Class_Code 匹配的条目,然后是 Course_Code,然后是 Dept,然后是 Course_Title,最后是 prof。换句话说,我希望结果按用户在自动完成中提供的具体程度排序。如果他们知道 Class_Code,我希望它位于顶部。

我想不出一种不需要大量查询的方法。到目前为止,我能想到的唯一方法是将每个术语的一堆 LIKE 比较别名到每个字段,然后按这些别名排序。这相当于用户在自动完成中输入的字数的 5 倍。换句话说,这是一个非常丑陋的查询。

有没有一种简单有效的方法来进行这种排序?如果有类似 INSTR() 函数需要 multiple 字符串进行搜索,我可以轻松地将自动完成中的所有单词与每个字段进行比较,并始终对由此产生的 5 个别名进行排序.

欢迎提出任何意见,即使这是超出问题范围的一般性建议。但是,我想提前说明一下,我不想在这里使用 FULLTEXT 之类的东西或 Sphinx 之类的搜索系统,因为我觉得它不适合像这样的非密集型快速查询。另外,我不接受重组我的数据库。所以我想用一个很好的 MySQL 查询来做这件事。

提前谢谢..

【问题讨论】:

    标签: mysql search autocomplete sql-order-by sql-like


    【解决方案1】:

    您是否考虑过使用 UNION 运算符?您可以将不同的查询分离到它们自己的 SELECT 语句中,然后使用 UNION 将它们连接在一起。这将自动将它们保留在您想要的组中 - 这是您运行 SELECT 语句的顺序。

    它可能会使您的查询更大,但应该是有效的。

    【讨论】:

    • 它可能会证明,很长的路是唯一的方法。如果是这样,我愿意做一个大的联合或别名一堆并订购它们。刚刚发布,看看是否有一些我遗漏的有效方法
    【解决方案2】:

    ORDER BY 接受多个值,因此只需按您想要的顺序列出列:

    SELECT column_name(s)
    FROM table_name
    ORDER BY column_name(s) ASC|DESC
    

    您可以尝试的另一种可能性是编写 SQL 生成器函数。有时,如果脚本非常丑陋并且必然会发生变化,让函数以模块化格式生成脚本可以更容易看到发生了什么。

    【讨论】:

    • 你读过这个问题吗?我不想对列进行排序,而是对列的匹配项进行排序。我在 qstn 中提出的所有问题都存在这样做的问题
    【解决方案3】:

    你可以这样做

    order by case when code  like '%blabla%' then 0 else 1 end
            ,case when dept  like '%blabla%' then 0 else 1 end
            ,case when title like '%blabla%' then 0 else 1 end
    

    这样,代码中的所有匹配项都将排在部门匹配项之前。

    已更新以供评论 如果我正确理解最后一条评论,您担心您会在查询中重复复杂的表达式吗?在这种情况下,您可以将替换/连接等包装在派生表中(见下文),或者您可以创建一个视图。

    select *
      from (select class_code
                  ,department_code
                  ,department_code
                  ,replace(concat(...)) as code
                  ,replace(concat(...)) as dept
                  ,replace(concat(...)) as title
              from classes
             where class_code      like '%blabla%'
                or department_code like '%blabla%'
                or class_title     like '%blabla%'
           ) as wrapped
     order by case when code  like '%blabla%' then 0 else 1 end
             ,case when dept  like '%blabla%' then 0 else 1 end
             ,case when title like '%blabla%' then 0 else 1 end
    

    只要确保对真实列而不是计算列进行实际搜索,以确保 mysql 可以使用索引。

    顺便说一句,只要过滤器和连接看起来“简单”,mysql就不会关心查询是否长而复杂。

    【讨论】:

    • 是的。我可以看到这样做(有关别名术语的想法,请参阅我的问题)。唯一的问题是,它很长,因为行数是用户输入的单词数的 5 倍
    • 这也可能有效:ORDER BY FIELD("blabla", code, dept, title) FIELD() docs dev.mysql.com/doc/refman/5.0/en/…
    • @German,感谢您的参考。我不知道 Field 功能。但它似乎只支持相等测试,因此我们不能在 OP 的上下文中使用它?
    • 是的@Ronnis,你是对的,这不适用于部分匹配。没想到
    猜你喜欢
    • 1970-01-01
    • 2011-09-12
    • 2012-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    • 1970-01-01
    相关资源
    最近更新 更多