我有一组与一组标签有多对多关系的城市。用户给了我一组标签(可能包含重复项!),我需要返回一个匹配条目列表,按相关性排序。
以下是一些示例数据来说明问题:
城市:
--------------------
| 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 |
------------------------
如果用户给我标签ID:[1,3,3,4],我想计算每个标签的匹配数,并返回相关性排序结果,如:
------------------------
| city | matches |
------------------------
| Cleveland | 4 |
| Baltimore | 2 |
| Eugene | 2 |
| Atlanta | 1 |
| Denver | 1 |
------------------------
由于克利夫兰匹配了所有四个标签,因此首先是巴尔的摩和尤金,其中每个都有两个标签匹配等。
另一个做出良好衡量的例子。对于搜索[2,2,2,3,4],我们得到:
------------------------
| city | matches |
------------------------
| Denver | 4 |
| Atlanta | 3 |
| Cleveland | 2 |
| Baltimore | 1 |
| Eugene | 1 |
------------------------
如果我忽略重复的标签,那么它很简单:
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允许我使用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;
非常感谢那些回答。
答案 0 :(得分:3)
如果用户列表以逗号分隔列表形式出现,您可以尝试将其转换为临时表并加入其中。我不知道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;
将逗号分隔列表转换为上述代码就像使用服务器端语言将所有,
替换为),(
一样简单,然后将其嵌入{{1填充临时表的语句。
演示(MySql):http://www.sqlize.com/1qNThhD9tC
答案 1 :(得分:1)
将所有标签粘贴到表格中然后加入,而不是将它们包含在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
;