3 테이블에서 m : n 관계 쿼리를 최적화하는 방법
-
06-09-2019 - |
문제
이것은 내 SQL 문제입니다 - 3 개의 테이블이 있습니다.
이름은 ListhasNames를 나열합니다 Id Name Id Desc ListsId NamesId =-------- ------------ ---------------- 1 Paul 1 Football 1 1 2 Joe 2 Basketball 1 2 3 Jenny 3 Ping Pong 2 1 4 Tina 4 Breakfast Club 2 3 5 Midnight Club 3 2 3 3 4 1 4 2 4 3 5 1 5 2 5 3 5 4
이는 Paul (ID = 1)과 Joe (ID = 2)가 축구 팀 (Lists.id = 1), 농구 팀의 Paul과 Jenny 등에 있다는 것을 의미합니다.
이제 목록을 반환하는 SQL 명령문이 필요합니다. 특정 이름 조합의 ID : 그 목록은 그 목록의 유일한 멤버 인 Paul, Joe 및 Jenny는 무엇입니까? Tina도 그 목록에 있기 때문에 5 (아침 클럽) - 5 (Midnight Club)가 아닙니다.
내부 조인 및 하위 쿼리로 시도했습니다.
SELECT Q1.Lists_id FROM ( SELECT Lists_Id FROM names as T1, listhasnames as T2 WHERE (T1.Name='Paul') and (T1.Id=T2.Names_ID) and ( ( SELECT count(*) FROM listhasnames as Z1 where (Z1.lists_id = T2.lists_Id) ) = 3) ) AS Q1 INNER JOIN ( SELECT Lists_Id FROM names as T1, listhasnames as T2 WHERE (T1.Name='Joe') and (T1.Id=T2.Names_ID) and ( (SELECT count(*) FROM listhasnames as Z1 WHERE (Z1.Lists_id = T2.lists_id) ) = 3) ) AS Q2 ON (Q1.Lists_id=Q2.Lists_id) INNER JOIN ( SELECT Lists_Id FROM names as T1, listhasnames as T2 WHERE (T1.Name='Jenny') and (T1.Id=T2.Names_ID) and ( (SELECT count(*) FROM listhasnames as Z1 WHERE (Z1.Lists_id = T2.lists_id) ) = 3) ) AS Q3 ON (Q1.Lists_id=Q3.Lists_id)
조금 복잡해 보입니다. 그것을 최적화하는 방법? 특정 이름이있는 목록 만 필요합니다 (그리고이 이름과 다른 사람 만). 아마도 select in?
안부, 데니스
해결책
SELECT ListsId
FROM ListHasNames a
WHERE NamesId in (1, 2, 3)
AND NOT EXISTS
(SELECT * from ListHasNames b
WHERE b.ListsId = a.ListsId
AND b.NamesId not in (1, 2, 3))
GROUP BY ListsId
HAVING COUNT(*) = 3;
편집하다: Chris Gow의 의견 덕분에 수정되었습니다. 다른 사람들이있는 목록을 제외하려면 하위 선택이 필요합니다.편집 2 Dennis의 의견 덕분에 테이블 이름을 수정했습니다
다른 팁
Carl Manaster의 솔루션을 출발점으로 사용하여 다음과 같이 생각했습니다.
SELECT listsid
FROM listhasnames
GROUP BY listsid HAVING COUNT(*) = 3
INTERSECT
SELECT x.listsid
FROM listhasnames x, names n
WHERE n.name IN('Paul', 'Joe', 'Jenny')
AND n.id = x.namesid
업데이트 :
select a.ListsId from
(
--lists with three names only
select lhn.ListsId, count(*) as count
from ListHasNames lhn
inner join Names n on lhn.NamesId = n.Id
group by lhn.ListsId
having count(*) = 3
) a
where a.ListsId in (select ListsId from ListHasNames lhn where NamesId = (select NamesId from names where Name = 'Paul'))
and a.ListsId in (select ListsId from ListHasNames lhn where NamesId = (select NamesId from names where Name = 'Joe'))
and a.ListsId in (select ListsId from ListHasNames lhn where NamesId = (select NamesId from names where Name = 'Jenny'))
나는 최근에 당신의 사건에도 잘 작동 할 수있는 문제를 해결하고있었습니다. 과잉 일 수 있습니다.
올바른 솔루션 일 수있는 후보 협회 목록을 작성한 다음 커서 또는 큐 테이블을 사용하여 올바른 솔루션을 통해 전체 검증을 수행하는 방법을 사용했습니다.
필자의 경우 이것은 같은 행동으로 구현되었습니다
select
ParentId
count(*) as ChildCount
checksum_agg(checksum(child.*) as ChildAggCrc
from parent join child on parent.parentId = child.parentId
그런 다음 조회 데이터와 카운트 및 집계 체크섬을 비교할 수 있습니다 (예 : 3 개의 이름을 확인할 수 있습니다). 행이 일치하지 않으면 일치하지 않습니다. 행이 일치하는 경우 행 세트 사이에 불일치가 있는지 확인하기 위해 해당 특정 부모의 조인을 수행 할 수 있습니다.
진흙처럼 깨끗합니까? :)