2013-08-15 2 views
0

잘못된 결과를주는 쿼리가 있습니다.SQL 쿼리에서 "잘못된"조인을 사용합니다.

테이블 :

A 
+----+ 
| id | 
+----+ 
| 1 | 
| 2 | 
+----+ 

B 
+----+----+ 
| id | x | B.id = A.id 
+----+----+ 
| 1 | 1 | 
| 1 | 1 | 
| 1 | 0 | 
+----+----+ 

C 
+----+----+ 
| id | y | C.id = A.id 
+----+----+ 
| 1 | 1 | 
| 1 | 2 | 
+----+----+ 

내가하고 싶은 것 : B의 ​​카운트의 각 행에 대해 A. 모든 행을 선택 B.id = A와 값 0 값 1 모든 x와 모든 X .신분증. A의 각 행에 대해 C.id = A.id로 C에서 최소 y를 얻습니다.

내가 기대하고 결과는 다음과 같습니다

+----+------+--------+---------+ 
| id | min | count1 | count 2 | 
+----+------+--------+---------+ 
| 1 | 1 |  2 |  1 | 
| 2 | NULL |  0 |  0 | 
+----+------+--------+---------+ 

의 첫 번째 시도는 : 이 작동하지 않습니다.

SELECT a.id, 
     MIN(c.y), 
     SUM(IF(b.x = 1, 1, 0)), 
     SUM(IF(b.x = 0, 1, 0)) 
FROM a 
     LEFT JOIN b 
       ON (a.id = b.id) 
     LEFT JOIN c 
       ON (a.id = c.id) 
GROUP BY a.id 

+----+------+--------+---------+ 
| id | min | count1 | count 2 | 
+----+------+--------+---------+ 
| 1 | 1 |  4 |  2 | 
| 2 | NULL |  0 |  0 | 
+----+------+--------+---------+ 

두 번째 시도 : 이 작동하지만 난 그게 나쁜 성능을 확신합니다.

SELECT a.id, 
     MIN(c.y), 
     b.x, 
     b.y 
FROM a 
     LEFT JOIN (SELECT b.id, SUM(IF(b.x = 1, 1, 0)) x, SUM(IF(b.x = 0, 1, 0)) y FROM b) b 
       ON (a.id = b.id) 
     LEFT JOIN c 
       ON (a.id = c.id) 
GROUP BY a.id 

+----+------+--------+---------+ 
| id | min | count1 | count 2 | 
+----+------+--------+---------+ 
| 1 | 1 |  2 |  1 | 
| 2 | NULL |  0 |  0 | 
+----+------+--------+---------+ 

마지막 시도 : 이 방법도 효과적입니다.

SELECT x.*, 
     SUM(IF(b.x = 1, 1, 0)), 
     SUM(IF(b.x = 0, 1, 0)) 
FROM (SELECT a.id, 
       MIN(c.y) 
     FROM a 
       LEFT JOIN c 
         ON (a.id = c.id) 
     GROUP BY a.id) x 
     LEFT JOIN b 
       ON (b.id = x.id) 
GROUP BY x.id 

이제 내 질문은 : 마지막 하나는 최선의 choise 아니면 하나의 select 문이 쿼리를 작성하는 방법 (첫 번째 시도에서처럼)이 있습니까?

+0

RBDMS는 무엇입니까? MySQL, PostgreSQL? –

+0

데이터베이스를 MySQL로 사용합니다. – KN4CK3R

답변

3

각 테이블에 여러 행이 있기 때문에 조인은 주어진 값에 대해 직교 변환을 수행합니다.

당신은 count(distinct)보다는 sum()를 사용하여이 문제를 해결할 수 있습니다

SELECT a.id, MIN(c.y), 
     count(distinct (case when b.x = 1 then b.id end)), 
     count(distinct (case when b.x = 0 then b.id end)) 
FROM a 
     LEFT JOIN b 
       ON (a.id = b.id) 
     LEFT JOIN c 
       ON (a.id = c.id) 
GROUP BY a.id; 

또한 b을 집계 사전 (및/또는 c)에 의해이 문제를 해결할 수 있습니다. 집계 함수가 b의 열 합계와 같은 경우이 접근 방식을 취해야합니다.

편집 :

정확합니다. 위의 쿼리는 B의 고유 값을 계산하지만 B에는 정확히 중복 된 행이 포함됩니다. (개인적으로, 나는 가난한 디자인의 기호 이름 중복을 가지고 id과 열을 갖는 생각하지만, 그것은 또 다른 문제입니다.)

당신은 b 테이블의 실제 id을함으로써 그것을 해결할 수 후 때문에 count(distinct)은 올바른 값을 계산합니다. 당신은 또한 그들에 합류하기 전에 두 테이블을 집계하여 해결할 수 있습니다 :

SELECT a.id, c.y, x1, x0 
FROM a 
     LEFT JOIN (select b.id, 
         sum(b.x = 1) as x1, 
         sum(b.x = 0) as x0 
        from b 
        group by b.id 
       ) b 
       ON (a.id = b.id) 
     LEFT JOIN (select c.id, min(c.y) as y 
        from c 
        group by c.id 
       ) c 
       ON (a.id = c.id); 

Here

이 문제에 대한 SQL 바이올린입니다.

편집 II는 :

당신은 하나 개의 문장에서 그것을 얻을 수 있지만, 나는 그것이 유사한 데이터에 일하는 것이 너무 확실하지 않다.그것은 조금 까다 롭습니다

SELECT a.id, MIN(c.y), 
     coalesce(sum(b.x = 1), 0)/count(distinct coalesce(c.y, -1)), 
     coalesce(sum(b.x = 0), 0)/count(distinct coalesce(c.y, -1)) 
FROM a 
     LEFT JOIN b 
       ON (a.id = b.id) 
     LEFT JOIN c 
       ON (a.id = c.id) 
GROUP BY a.id; 

당신이 NULL을 처리해야하기 때문에, : 아이디어는 x = 1하고 실제 고유 한 카운트를 얻기 위해 C 테이블에있는 행의 수에 의해 분할 위치를 모든 경우를 계산 할 수 있다는 것입니다 올바른 값을 얻으려고. 이 값은 테이블에서 고유 카운트를 얻기 위해 y 값을 계산합니다. 귀하의 질문은 모든 테이블에 고유 한 정수 기본 키를 갖는 것이 좋은 아이디어 인 이유를 다시 강요합니다.

+0

맞습니다. DISTINCT 절을 사용해야합니다. – GianlucaBobbio

+0

감사하지만이 쿼리는 올바른 결과를 제공하지 않습니다. 1, 1, 2, 1 대신 1, 1, 1, 1을 반환합니다. – KN4CK3R

+0

다시 한번 감사드립니다. 두 번째 편집은 두 번째 시도와 같으며 불필요한 합계가 많아 B에 항목이 많으면 좋은 성능을 내지 못합니다. 마지막 편집은 작동하지만 해킹 일 뿐이므로 한 번의 쿼리 만 사용하면됩니다. 나는 나의 마지막 시험 버전과 함께있을 것이다. – KN4CK3R

관련 문제