2011-01-12 2 views
5

나는 다른 테이블의 행에있는 특정 값을 기반으로 한 테이블의 SELECT에서 행을 필터링하는 방법을 찾고 있습니다.다른 테이블의 열을 기반으로 SELECT에서 행 제거

아래 예제 구조로 실험하고 있습니다. 블로그 게시물의 내용 (블로그 게시물 당 한 행)과 게시물에 대한 메타 데이터의 다른 테이블 (키 - 값 쌍당 하나의 행, 블로그 게시물과 연관된 열이있는 각 행, 각 행에 대한 많은 행)이 있습니다. 블로그 게시물). metadata에 행이없는 경우에만 posts 행을 가져 오려면 metadata.pid=posts.pid AND metadata.k='optout'입니다. 즉, 아래 예제 구조에서는 posts.id=1 행만 반환하려고합니다.

pid에 대한 메타 데이터의 다른 행이 결과로 만드는 의미하기 때문에 JOIN의 일부 메타 데이터 곳 metadata.k='optout'이 게시물을 제거 끝나지 않는다 (I 무엇을 시도했다 기준). 메타 데이터 행이 매 pid의 원인,

mysql> select posts.* from posts where pid = any (select pid from metadata where k = 'optout'); 
+-----+-------+--------------+ 
| pid | title | content  | 
+-----+-------+--------------+ 
| 2 | Bar | More content | 
| 3 | Baz | Something | 
+-----+-------+--------------+ 
2 rows in set (0.00 sec) 

...하지만 pid != any (...)를 사용하는 것은 나에게 게시물의 행의 3을 제공합니다

mysql> select * from posts; 
+-----+-------+--------------+ 
| pid | title | content  | 
+-----+-------+--------------+ 
| 1 | Foo | Some content | 
| 2 | Bar | More content | 
| 3 | Baz | Something | 
+-----+-------+--------------+ 
3 rows in set (0.00 sec) 

mysql> select * from metadata; 
+------+-----+--------+-----------+ 
| mdid | pid | k  | v   | 
+------+-----+--------+-----------+ 
| 1 | 1 | date | yesterday | 
| 2 | 1 | thumb | img.jpg | 
| 3 | 2 | date | today  | 
| 4 | 2 | optout | true  | 
| 5 | 3 | date | tomorrow | 
| 6 | 3 | optout | true  | 
+------+-----+--------+-----------+ 
6 rows in set (0.00 sec) 

는 서브 쿼리 나에게 내가 원하는 걸의 역을 제공 할 수 있습니다 where k!='optout'.

답변

8

LEFT JOIN을 수행하고 조인 된 테이블의 값이 NULL 인 결과를 확인한 결과 같은 조인 된 레코드가 존재하지 않음을 나타냅니다. 예를 들어

는 :

SELECT * FROM posts 
LEFT JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = 'optout') 
WHERE metadata.mdid IS NULL; 

이 해당하는 metadatak = 'optout'의 값도 존재하지 않는 테이블 posts에서 임의의 행을 선택한다.

편집 : 이것은 왼쪽 조인의 주요 속성이며 일반 조인에서는 작동하지 않습니다. 조인 된 테이블에 일치하는 값이 없더라도 왼쪽 조인은 항상 첫 번째 테이블의 값을 반환하므로 이러한 행이 없으면 선택을 수행 할 수 있습니다.

편집 2 : LEFT JOINJOIN (여기서 명확하게하기 위해 INNER JOIN이라고 부르지 만 MySQL에서는 상호 변경 가능)과 관련하여 여기에서 어떤 일이 일어나는지 명확히 설명합니다.

는이 두 쿼리 중 하나를 실행한다고 가정

SELECT posts.*, metadata.mdid, metadata.k, metadata.v 
FROM posts 
INNER JOIN metadata ON posts.pid = metadata.pid; 

또는

SELECT posts.*, metadata.mdid, metadata.k, metadata.v 
FROM posts 
LEFT JOIN metadata ON posts.pid = metadata.pid; 

두 쿼리

을 생산하는 다음과 같은 결과 집합 : 이제

+-----+-------+--------------+------+-------+-----------+ 
| pid | title | content  | mdid | k  | v   | 
+-----+-------+--------------+------+-------+-----------+ 
| 1 | Foo | Some content | 1 | date | yesterday | 
| 1 | Foo | Some content | 2 | thumb | img.jpg | 
+-----+-------+--------------+------+-------+-----------+ 

,의 우리가 수정 가정하자 언급 된 "optout"에 대한 추가 기준을 추가하십시오.첫째,이 INNER JOIN :

SELECT posts.*, metadata.mdid, metadata.k, metadata.v 
FROM posts 
LEFT JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = "optout"); 

이 결과 세트를 생성 않습니다 : LEFT JOIN A를 것을 변경, 이제

Empty set (0.00 sec) 

: 예상대로

SELECT posts.*, metadata.mdid, metadata.k, metadata.v 
FROM posts 
INNER JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = "optout"); 

이 어떤 결과를 반환하지

+-----+-------+--------------+------+------+------+ 
| pid | title | content  | mdid | k | v | 
+-----+-------+--------------+------+------+------+ 
| 1 | Foo | Some content | NULL | NULL | NULL | 
+-----+-------+--------------+------+------+------+ 

INNER JOINLEFT JOIN의 차이는 INNER JOIN이 BOTH 조인 된 테이블의 행이 일치하는 경우에만 결과를 반환한다는 것입니다. LEFT JOIN에서는 조인 할 항목이 있는지 여부에 관계없이 첫 번째 테이블의 일치하는 행이 항상 반환됩니다. 많은 경우에 어떤 것을 사용하든 상관 없지만, 예기치 않은 결과가 나오지 않도록 올바른 것을 선택하는 것이 중요합니다. 그래서이 경우

은의 추천 검색어 :

SELECT posts.*, metadata.mdid, metadata.k, metadata.v 
LEFT JOIN metadata ON (posts.pid = metadata.pid AND metadata.k = 'optout') 
WHERE metadata.mdid IS NULL; 

위와 같이 설정 한 것과 같은 결과를 반환합니다 :

+-----+-------+--------------+------+------+------+ 
| pid | title | content  | mdid | k | v | 
+-----+-------+--------------+------+------+------+ 
| 1 | Foo | Some content | NULL | NULL | NULL | 
+-----+-------+--------------+------+------+------+ 

희망은 그것을 정리할 것을! 조인은 어느 것이 가장 좋은 것인지를 완전히 이해하고 배우는 것이 좋습니다.

+0

뭔가를 시도 할 수는 하위 쿼리는 메타 데이터 행을 일치하므로 metadata.mdid 그렇게하지 null의 경우 선택되지 않습니다. 그러나 optout이없는 게시물 인 경우 하위 쿼리는 행과 일치하지 않으므로 오른쪽에 null이 채워 지므로 where 절이 true입니다. – alxndr

+1

나는 조인이 어떻게 작동하는지에 대한 답변에 또 다른 섹션을 추가했습니다.이 섹션에는 회색 영역이 없어야합니다. 희망이 도움이됩니다! – futureal

3

당신은 선별 (Opt-Out)와 게시물에 대한 ... 그래서 난이 얻을 경우 나 보자

select p.* 
from posts p 
where NOT EXISTS (
         select pid 
         from metadata 
         where k = 'optout' 
         and  pid = p.pid 
        ) 
+0

와우, 고마워. NOT NOT ISISTS에서 읽을거야. – alxndr

+0

FYI는 36000 개의 행에서 다른 조인 원인에 체크 표시를하면 왼쪽 조인은 0.1 초 더 빠릅니다 ... – alxndr

+1

작은 결과 집합에서 두 쿼리는 거의 동일하게 수행되어야합니다. 그러나 하위 쿼리와 함께'EXISTS' 또는'NOT EXISTS'를 사용하면 하위 쿼리 테이블을 계산하여 임시 테이블에 복사해야합니다. 결과가 커지면 성능 병목 현상이 커질 수 있습니다. 나는 그것을 완전히 피하지 않을 것입니다. 때로는 복잡한 조인보다 읽기/이해하기가 훨씬 쉽습니다. 결국 결과 집합의 유형을 알아야합니다. – futureal

관련 문제