2014-10-15 2 views
0

먼저 제목이 이해가되지 않는다면 미안하지만 더 나아질 수는 없습니다. 나는 비슷한 질문을 찾으려고했지만, 나를 도울 수있는 어떤 것도 찾지 못했습니다.MySQL 다중 태그 선택

간략한 설명 :
나는 제품의 데이터베이스가 있습니다. 제품에 여러 개의 태그가 있습니다. 사용 가능한 태그가있는 제품을 필터링하고 싶습니다. 이미 작동하고있는 솔루션이 있지만 표에 40k 행만있는 약점이 있습니다. 기본 쿼리를 최적화하고 싶기 때문에 결국에는 나를 해치지 않습니다.

태그 항목의 관계 표 :

CREATE TABLE `tags_assoc` (
    `assoc_id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `tag_id` int(10) unsigned NOT NULL, 
    `item_id` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`assoc_id`), 
    KEY `tag_id` (`tag_id`), 
    KEY `item_id` (`item_id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

페이지가 작동하도록되어 방법 :

  1. 태그의 거대한 목록이 있습니다. 사용자가 tag_id을 선택하고 페이지가 필터링되어 item_id에 특정 태그 tag_id이 표시됩니다.

  2. 백엔드는 필터링 된 item_id을 검사하고 추가 필터링에 사용할 수있는 tag_id을 모두 나열합니다.

에 Query1 - 상품의 목록 :

SELECT 
    item_id, 
    count(tag_id) as count 
FROM 
    tags_assoc 
WHERE 
    tag_id IN (...) 
GROUP BY 
    item_id 
HAVING 
    count = (...) 

QUERY2이 - 나머지 태그 목록 :

SELECT 
    ta.tag_id, 
    t.tag_name, 
    COUNT(ta.tag_id) as count 
FROM 
    tags_assoc ta 
LEFT JOIN 
    tags t ON (ta.tag_id = t.tag_id) 
WHERE 
    AND item_id IN (...) 
    AND ta.tag_id NOT IN (...) 
GROUP BY 
    ta.tag_id 

작동 원리 :
가의가에 가정 해 봅시다 우리가 선택하는 첫 라운드 tag_id = 250. 즉, Query1에는 WHERE tag_id IN (250)HAVING count = 1이 포함됩니다. 반환 된 목록에는 다음과 같은 item_ids 1, 2, 3, 4, 5이 포함됩니다.

나머지 태그를 얻으려면 다음 매개 변수를 사용하여 Query2를 실행하십시오. AND item_id IN (1, 2, 3, 4, 5) AND ta.tag_id NOT IN (250).

사용자는 남은 태그에서 다른 태그를 선택합니다 (예 : tag_id = 300). 즉, Query1에는 WHERE tag_id IN (250, 300)HAVING count = 2이 포함됩니다. 반환 된 목록에는 다음 항목이 포함됩니다. 1, 2, 3.

나머지 태그를 얻으려면 다음 매개 변수를 사용하여 Query2를 실행하십시오. AND item_id IN (1, 2, 3) AND ta.tag_id NOT IN (250, 300).

문제는 :
사용자가 첫 번째 태그를 선택하고, 반환 된 ITEM_ID 목록은 상당히 길어질 수 있습니다. 즉, Query2는 다음을 포함 할 수 있으므로 길어집니다. AND item_id IN (1, 2, 3, ... 7500, 7501, 7502). 37k 문자의 긴 검색어가 있습니다. 성능에 관해서는, 그것은 끔찍하지는 않지만 딸꾹질을 일으킬 수 있습니다. 그리고 이것은 항목 테이블과 40k 행에서만 7k 행과 함께 있습니다.

은 이미 시도했다 : AND item_id IN (SELECT item_id FROM tags_assoc WHERE tag_id IN (...) GROUP BY item_id HAVING COUNT(tag_id) = (...)) :
내가 긴 ITEM_ID 목록을 포함, 대신 훨씬 Query1을에서처럼 하위 쿼리를 사용하지 QUERY2를 다시 썼다. 성과가 현명했기 때문에이 대안을 포기했습니다.

누군가 나를 더 나은 방향으로 안내 할 수 있습니까?
긴 게시물을 작성해 주셔서 진심으로 사과드립니다.

답변

0

임시 테이블에서 Q1의 출력을 유지 한 다음이 값을 사용하여 Q2의 효율성을 향상시킬 수 있습니다.

임시 테이블에 INNER JOIN을 사용하여 Q2를 필터링했지만 하위 쿼리도 옵션이 될 수 있습니다.

CREATE TEMPORARY TABLE tempQ1 
(
    item_id int(10) unsigned NOT NULL, 
    PRIMARY KEY (item_id) 
); 

INSERT INTO tempQ1 
SELECT 
    item_id, 
    count(tag_id) as count 
FROM 
    tags_assoc 
WHERE 
    tag_id IN (...) 
GROUP BY 
    item_id 
HAVING 
    count = (...); 

-- Output tempQ1 as the first result 
SELECT item_id FROM tempQ1; 

SELECT 
    ta.tag_id, 
    t.tag_name, 
FROM 
    tags_assoc ta 
LEFT JOIN tags t ON (ta.tag_id = t.tag_id) 
INNER JOIN tempQ1 ON ta.item_id = tempQ1.item_id 
WHERE 
    ta.tag_id NOT IN (...) 
GROUP BY 
    ta.tag_id; 

DROP TEMPORARY TABLE tempQ1; 
+0

고맙습니다! 이 메소드의 문제점은 여러 개의 tag_ids와 함께 표시됩니다. 예를 들어 쿼리 1은 다음과 같이 보일 것입니다 : SELECT DISTINCT item_id FROM tags_assoc ta WHERE ta.tag_id IN (250, 300)'. ** item_id에 ** ** 태그 중 적어도 하나를 나열합니다. ** ** 모든 ** 태그가있는 item_ids를 나열하고 싶습니다. 당신의 방법은 선택을 넓히고, 제 방법은 그것을 좁 힙니다. 적어도 이것이 내가 그것을 해석하는 방법이다. – user523736

+0

죄송합니다, 저의 실수 - HAVING 절을 설명합니다! – Alan

+0

저는 임시 테이블을 사용하여 제안 할 답변을 변경했습니다. 이렇게해도 문제가 해결되지 않으면 본인의 질문을 오해하여이 답변을 삭제할 투표를하겠습니다. – Alan