2011-10-10 52 views
6

我有一组与标记集具有多对多关系的城市。用户为我提供了一组标签(,其中可能包含重复项!),我需要返回匹配条目的列表,按相关性排序。通过多个标记进行相关排序搜索的SQL查询

数据

下面是一些样本数据来说明问题:

城市:

-------------------- 
| 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 | 
------------------------ 

由于克利夫兰匹配所有四个标签,它是第一,其次是巴尔的摩和尤金,每个有两个标签匹配,等等

示例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; 

非常感谢那些回答。

+0

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

回答

3

如果用户列表以逗号分隔的列表形式出现,您可以尝试将其转换为临时表并加入。我不知道PosteGRE的relveant语法,所以这里是在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

+0

哇! sqlize.com太棒了! 谢谢!这正是我需要的。 –

1

棒的所有标签为一个表,然后再加入其中,包括在名单中,而不是。

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 
; 
相关问题