【问题标题】:SQL query to search by multiple tags with relevance sortingSQL查询通过相关性排序的多个标签进行搜索
【发布时间】:2011-10-10 15:03:21
【问题描述】:

我有一组与一组标签具有多对多关系的城市。用户给了我一组标签(可能包含重复!),我需要返回一个匹配条目的列表,按相关性排序。

数据

这里有一些示例数据来说明问题:

城市:

--------------------
| id |    city     |
--------------------
|  1 |  Atlanta    |
|  2 |  Baltimore  |
|  3 |  Cleveland  |
|  4 |  Denver     |
|  5 |  Eugene     |
--------------------

标签:

------
| id |
------
|  1 |
|  2 |
|  3 |
|  4 |
------

城市的标签如下:

Atlanta:   1, 2
Baltimore: 3
Cleveland: 1, 3, 4
Denver:    2, 3
Eugene:    1, 4

...所以 CityTags 表如下所示:

------------------------
|  city_id  |  tag_id  |
------------------------
|     1     |     1    |
|     1     |     2    |
|     2     |     3    |
|     3     |     1    |
|     3     |     3    |
|     3     |     4    |
|     4     |     2    |
|     4     |     3    |
|     5     |     1    |
|     5     |     4    |
------------------------

示例 1

如果用户给我标签 ID:[1, 3, 3, 4],我想计算每个标签有多少匹配项,并返回一个相关性排序的结果,例如:

------------------------
|    city    | matches |
------------------------
|  Cleveland |    4    |
|  Baltimore |    2    |
|  Eugene    |    2    |
|  Atlanta   |    1    |
|  Denver    |    1    |
------------------------

由于 Cleveland 匹配了所有四个标签,所以它是第一个,其次是 Baltimore 和 Eugene,它们都有两个标签匹配,等等。

示例 2

再举一个例子。对于搜索 [2, 2, 2, 3, 4],我们会得到:

------------------------
|    city    | matches |
------------------------
|  Denver    |    4    |
|  Atlanta   |    3    |
|  Cleveland |    2    |
|  Baltimore |    1    |
|  Eugene    |    1    |
------------------------

SQL

如果我忽略重复的标签,那么它是微不足道的:

SELECT name,COUNT(name) AS relevance FROM
  (SELECT name FROM cities,citytags 
    WHERE id=city_id AND tag_id IN (1,3,3,4)) AS matches
  GROUP BY name ORDER BY relevance DESC;

但这不是我需要的。我需要尊重重复。有人可以建议我如何做到这一点吗?

Postgresql 中的解决方案

啊哈!我需要一个临时表。 Postgresql 让我用它的 WITH 语法来做到这一点。这是解决方案:

WITH search(tag) AS (VALUES (1), (3), (3), (4))
SELECT name, COUNT(name) AS relevance FROM cities
INNER JOIN citytags ON cities.id=citytags.city_id
INNER JOIN search ON citytags.tag_id=search.tag
GROUP BY name ORDER BY relevance DESC;

非常感谢那些回答的人。

【问题讨论】:

  • 用户如何输入他们的标签列表?他们是否键入了一个逗号分隔的列表,然后您将其连接到查询中?

标签: sql postgresql


【解决方案1】:

如果用户列表以逗号分隔的列表形式出现,您可以尝试将其转换为临时表并加入其中。我不知道 PosteGRE 的相关语法,所以这里是 MySql 中的想法:

create temporary table usertags (tag_id int);
insert usertags values (1),(3),(3),(4);

SELECT name, COUNT(name) AS relevance
FROM cities
JOIN citytags on cities.id = citytags.city_id
JOIN usertags on citytags.tag_id = usertags.tag_id
GROUP BY name ORDER BY relevance DESC;

将逗号分隔的列表转换为上述代码就像使用服务器端语言将所有 , 替换为 ),( 一样简单,然后将其嵌入到 VALUES 语句中以填充临时表。

演示(MySql):http://www.sqlize.com/1qNThhD9tC

【讨论】:

  • 哇! sqlize.com 太棒了!谢谢!这正是我所需要的。
【解决方案2】:

将所有标签粘贴到一个表中,然后加入而不是将它们包含在一个 IN 列表中。

CREATE TABLE #input (
  tag_id INT NOT NULL
)
;

INSERT INTO #input
          SELECT 1
UNION ALL SELECT 3
UNION ALL SELECT 3
UNION ALL SELECT 4
;

SELECT
  city.name,
  search.relevance
FROM
  city
INNER JOIN
(
  SELECT
    city_id,
    COUNT(*) AS relevance
  FROM
    citytags
  INNER JOIN
    #input
      ON #input.tag_id = citytags.tag_id
  GROUP BY
    city_id
)
  AS search
    ON search.city_id = city.id
ORDER BY
  search.relevance DESC
;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-10
    • 2021-09-25
    • 2021-08-19
    相关资源
    最近更新 更多