2011-10-10 26 views
6

Tôi đã có một tập hợp các thành phố có mối quan hệ nhiều - nhiều với một bộ thẻ. Người dùng cung cấp cho tôi một bộ sưu tập các thẻ (có thể chứa các bản sao!) và tôi cần phải trả về một danh sách các mục phù hợp, được sắp xếp theo mức độ liên quan.Truy vấn SQL để tìm kiếm theo nhiều thẻ có phân loại phù hợp

Data

Dưới đây là một số dữ liệu mẫu để minh họa cho vấn đề:

Cities:

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

Tags:

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

Các thành phố được gắn thẻ như thế này:

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

... vì vậy các bảng CityTags trông giống như:

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

Ví dụ 1

Nếu người dùng mang lại cho tôi id tag: [1, 3, 3, 4], tôi muốn đếm có bao nhiêu trận đấu tôi đã cho mỗi người trong số các thẻ, và trả về một kết quả phù hợp-được sắp xếp như sau:

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

Kể từ Cleveland phù hợp tất cả bốn thẻ, nó đầu tiên, tiếp theo là Baltimore và Eugene, mà mỗi người có hai thẻ trận đấu, v.v.

Ví dụ 2

Một ví dụ khác để thực hiện biện pháp tốt. Đối với việc tìm kiếm [2, 2, 2, 3, 4], chúng tôi nhận được:

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

SQL

Nếu tôi bỏ qua các thẻ lặp đi lặp lại, thì đó là tầm thường:

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; 

Nhưng đó không phải là những gì tôi cần. Tôi cần phải tôn trọng các bản sao. Ai đó có thể đề nghị làm thế nào tôi có thể thực hiện điều này?

Giải pháp trong Postgresql

Aha! Một bảng tạm thời là tôi cần. Postgresql cho phép tôi làm điều này với cú pháp WITH của nó. Đây là giải pháp:

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; 

Cảm ơn rất nhiều đến những người đã trả lời.

+0

Người dùng nhập danh sách thẻ của họ như thế nào? Họ có nhập danh sách được phân cách bằng dấu phẩy mà sau đó bạn chỉ cần ghép nối vào truy vấn không? – mellamokb

Trả lời

3

Nếu danh sách người dùng đến dưới dạng danh sách được phân cách bằng dấu phẩy, bạn có thể thử biến nó thành bảng tạm thời và tham gia vào đó. Tôi không biết cú pháp relveant cho PosteGRE, vì vậy đây là ý tưởng trong 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; 

Chuyển đổi danh sách bằng dấu phẩy vào mã ở trên sẽ là đơn giản như thực hiện một thay thế tất cả các ,-),( sử dụng ngôn ngữ phía máy chủ của bạn và sau đó nhúng ngôn ngữ đó vào câu lệnh VALUES để điền bảng tạm thời.

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

+0

Whoa! sqlize.com thật tuyệt vời! Cảm ơn! Đó chỉ là những gì tôi cần. –

1

Stick tất cả các thẻ vào một bảng và sau đó tham gia thay vì bao gồm chúng trong một danh sách 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 
; 
Các vấn đề liên quan