如何在SQL中对多个字段进行排序和筛选

时间:2015-01-16 20:47:58

标签: php sql database search search-engine

我有一个带有音乐歌曲的SQL数据库。每首歌当然都有艺术家,专辑和流派。他们也有一般的普及程度。计数器,是从外部来源获得的。但是,我想让用户有机会对这些歌曲进行投票。最后,搜索结果应按此受欢迎程度排序,以及原始查询结果的准确性。

我使用的当前查询如下:

SELECT *
FROM p2pm_tracks
WHERE
 `artist` LIKE '%$searchquestion%' OR
 `genres` LIKE '%$searchquestion%' OR
 `trackname` LIKE '%$searchquestion%' OR
 `album_name` LIKE '%$searchquestion%'
ORDER BY `popularity` DESC
LIMIT $startingpoint, $resultsperpage

我对以下内容感到困惑:

  1. 用户搜索某些内容。我查看所有领域:歌曲标题艺术家专辑流派。但是,通常某个搜索查询包含这些曲目的多个(部分)。
  2. 例如,用户可能会搜索Opening Philip Glass

    在这种情况下,第一个单词是歌曲的名称,第二个和第三个单词是艺术家名称

    另一个例子:

    如果我在空格上拆分查询,则会找到正确的轨道。但是,如果仅匹配其中一个单词的其他曲目具有更高的受欢迎程度,则会在实际精确匹配搜索查询的曲目之前返回。

    我仍然希望对结果进行排序,使得与查询的较大部分匹配的内容立即位于顶部。我怎么能用SQL做到这一点?

    1. 我有静态流行度,想要创建一个新的。因此,我想使用特定轨道上所有投票的平均值(这些投票存储在另一个表中),除了尚未投票的情况。 如何构建执行此操作的SQL查询?
    2. 我的应用程序是用PHP构建的,但我想在SQL中尽可能多地执行此操作,最好在尽可能少的查询中减少延迟。

      任何帮助都将不胜感激。

4 个答案:

答案 0 :(得分:4)

您可以为搜索结果中的每一列添加权重。

以下是代码:

SELECT *,
    CASE WHEN `artist` LIKE '%$searchquestion%' THEN 1 ELSE 0 END AS artist_match,
    CASE WHEN `genres` LIKE '%$searchquestion%' THEN 1 ELSE 0 END AS genres_match,
    CASE WHEN `trackname` LIKE '%$searchquestion%' THEN 1 ELSE 0 END AS trackname_match,
    CASE WHEN `album_name` LIKE '%$searchquestion%' THEN 1 ELSE 0 END AS album_name_match,
FROM p2pm_tracks
WHERE
 `artist` LIKE '%$searchquestion%' OR
 `genres` LIKE '%$searchquestion%' OR
 `trackname` LIKE '%$searchquestion%' OR
 `album_name` LIKE '%$searchquestion%'
ORDER BY 
 `artist_match` DESC,
 `genres_match` DESC,
 `trackname_match` DESC,
 `album_name_match` DESC,
 `popularity` DESC,
LIMIT $startingpoint, $resultsperpage

此查询将收集与以下内容相关的结果:

  • 艺术家FIRST,
  • 那个类型,
  • 然后是曲目的标题,
  • 然后是专辑的名字,
  • 那首歌的流行

要优化此查询,您应避免使用“LIKE”并改为使用“FULLTEXT SEARCH”。

优化的代码将是:

SELECT *,
    CASE WHEN MATCH (artist) AGAINST ('$searchquestion') THEN 1 ELSE 0 END AS artist_match,
    CASE WHEN MATCH (genres) AGAINST ('$searchquestion') THEN 1 ELSE 0 END AS genres_match,
    CASE WHEN MATCH (trackname) AGAINST ('$searchquestion') THEN 1 ELSE 0 END AS trackname_match,
    CASE WHEN MATCH (album_name) AGAINST ('$searchquestion') THEN 1 ELSE 0 END AS album_name_match,
FROM p2pm_tracks
WHERE
 MATCH (artist) AGAINST ('$searchquestion') OR
 MATCH (genres) AGAINST ('$searchquestion') OR
 MATCH (trackname) AGAINST ('$searchquestion') OR
 MATCH (album_name) AGAINST ('$searchquestion')
ORDER BY 
 `artist_match` DESC,
 `genres_match` DESC,
 `trackname_match` DESC,
 `album_name_match` DESC,
 `popularity` DESC,
LIMIT $startingpoint, $resultsperpage

确保您使用MyISAM引擎作为MySQL表,并为您要搜索的列创建了索引。 MySQL表的代码应如下所示:

CREATE TABLE p2pm_tracks (
    id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
    artist VARCHAR(255) NOT NULL,
    trackname VARCHAR(255) NOT NULL,
    ...
    ...
    FULLTEXT (artist,trackname)
) ENGINE=MyISAM;

有关详情,请查看以下内容:   - http://dev.mysql.com/doc/refman/5.0/en/fulltext-natural-language.html   - http://dev.mysql.com/doc/refman/5.5/en/fulltext-boolean.html

如果您正在寻找更高级的东西,那么请查看Solr(基于Lucene),Sphinx,ElasticSearch(基于Lucene)等。

答案 1 :(得分:1)

MySQL在搜索文本方面不是很好:(

  1. 您可以尝试做的是查看全文搜索功能(http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html

  2. 通过匹配功能,您可以获得可以订购的相关性。

    SELECT p2pm_tracks。*,    MATCH(艺术家,流派)反对('某些词')AS相关性,    MATCH(艺术家)AGAINST('some words')AS artist_relevance

答案 2 :(得分:1)

请不要使用LIKE。这很慢。您可以在mysql中使用全文搜索,但无法确定哪个列更重要。

更好的解决方案是使用sphinx的mysql。

答案 3 :(得分:0)

嗯,为了匹配你的1.例子在SQL中很难,我不确定是否有函数。 你需要的是像php中的这个funktion

http://php.net/manual/function.similar-text.php

或者您只在每次平均投票中选择sql查询,并通过php和类似文本函数计算结果匹配的“好”。