2017-11-20 4 views
3

저도 상대적으로 성능이 좋은 PostgresSQL 쿼리를 작성하고 저에게 제가 원하는 데이터 세트를 제공합니다. 그러나 이것이 가장 간단한/최선의 방법인지 궁금합니다. 쿼리를 작성하십시오. 필요한 조건을 충족시키는보다 간단한 조인 연산이 있어야하는 것 같습니다.Postgres는 "on"열에서 null을 유지하면서 복잡한 외부 조인을 수행합니다.

편집 : 큰 테이블에서 실행해야합니다. 아래 주어진 예에서 애완 동물은 1 억 5 천만 줄, 음식은 대략 100 만 줄입니다. 하단의 내 솔루션은 약 0.6ms로 클록됩니다. 두 테이블 모두 id 및 user_id에 대한 인덱스가 있습니다. 식탁은 또한 pet_id에 대한 색인을 포함합니다.

나는 내 시스템에서 하나의 보장 된 공유 속성 인 user_id와 관련된 두 개의 테이블을 가지고 있습니다. 다음은 본질적으로 내 문제 표시 한 예입니다 :

애완 동물

+------+-------+---------+ 
| id | type | user_id | 
+------+-------+---------+ 
| 1234 | dog |  1 | 
| 1235 | cat |  1 | 
| 1236 | gecko |  1 | 
+------+-------+---------+ 

식품

+------+-----------+---------+--------+ 
| id | name | user_id | pet_id | 
+------+-----------+---------+--------+ 
| 4321 | hamburger |  1 | NULL | 
| 4322 | dog food |  1 | 1234 | 
| 4323 | cat food |  1 | 1235 | 
+------+-----------+---------+--------+ 

원하는 결과를

+------+------+ 
| p.id | f.id | 
+------+------+ 
| NULL | 4321 | --no pet, hamburger 
| 1234 | 4322 | --dog, dog food 
| 1235 | 4323 | --cat, cat food 
| 1236 | NULL | --gecko, no food 
+------+------+ 

이제 참조 할 예제를 통해 결과가 무엇인지 확실하게 알 수 있습니다. 결과에는 내 user_id에 속하는 양면의 모든 행이 포함됩니다 (테이블에 user_id 1에 속하지 않은 다른 행이 수천 개있을 수 있다고 가정). 이 결과 행에 다른 행과 일치하는 각 행의 사본 하나만 포함 시키길 원합니다.

SELECT p.id, f.id 
FROM pets p FULL OUTER JOIN food f ON p.user_id = f.user_id 
WHERE p.user_id = 1; 

그것은 왼쪽에서 NULL의 제외

  1. 때문에이 쿼리에 약간의 문제가있다 :

    완전 외부의 예는 내가이 일을하려고한다는 가입 쿼리의 그게 필요해.

  2. user_id는 본질적으로 상수이기 때문에 user_id와 일치하므로 많은 중복으로 끝납니다. 왼쪽의 모든 행은 오른쪽의 모든 행과 일치합니다. 내가 원하는 건 아니야. 나는 일대일 매치가 필요하다.

나는 WHERE 필터에 OR을 포함하여 1 번을 고칠 수 : 이유

SELECT p.id, f.id 
FROM pets p FULL OUTER JOIN food f ON p.user_id = f.user_id 
WHERE p.user_id = 1 OR f.user_id = 1; 

내가 쿼리가 시간이 오래 걸릴 수 있습니다, 완전히 확실하지 않다. 우리 시스템에서는 두 테이블 모두 user_id에 대한 인덱스를 가지고 있으므로 인덱스가 부족하지는 않습니다.

SELECT p.id, f.id 
    FROM pets p LEFT JOIN food f 
     ON p.id = f.pet_id AND f.user_id = 1 
    WHERE p.user_id = 1 
UNION 
SELECT p.id, f.id FROM pets p RIGHT JOIN food f 
     ON p.id = f.pet_id 
    WHERE f.user_id = 1 AND p.id IS NULL; 

그래서 제 질문은 이것이다 : 단일 쿼리로 이것을 실행하는 간단한 방법이 있나요

내 문제를 해결하기 위해, 나는 (정말이 결합 된) 다음 쿼리에 착륙?

+0

쿼리를 분리하여 사용해 보셨습니까? 성능 질문에는'EXPLAIN ANALYZE'와 테이블 크기, 인덱스, 현재 시간 성능, 원하는 시간 등에 대한 정보가 포함되어야합니다.'느림 '이란 상대적인 용어이며 비교할 실제 가치가 필요합니다. –

+0

의견에 감사드립니다. 나는 두 쿼리를 따로 시도했다. 그것들은 조금 더 빠르게 동작합니다 (~ 0.3ms는 0.6ms에 비해). 데이터 처리 후 작업/저장 작업을 저장하는 응용 프로그램으로 데이터 집합 반환을 시도하고 있습니다. 테이블 크기와 현재 시간 성능에 대한 메모를 포함하기 위해 질문을 조금 더 편집했습니다. 원하는 성능은 전류와 유사합니다. 같은 것을 수행하는보다 직접적인 접근법을 놓치고 있는지 궁금합니다. –

답변

3

SQL DEMO

SELECT p.id, f.id 
FROM pets p 
FULL OUTER JOIN food f 
    ON p.user_id = f.user_id 
AND p.id = f.pet_id 
AND p.user_id = 1; 

OUTPUT

|  id |  id | 
|--------|--------| 
| 1234 | 4322 | 
| 1235 | 4323 | 
| 1236 | (null) | 
| (null) | 4321 | 

참고 :

,

두 표 모두에 대해 (user_id, pet_id)에 복합 색인을 추가해야합니다.

+1

두 테이블 모두에 대해 '(user_id, pet_id)'에 복합 인덱스가 필요합니다. –

+0

나는이 인덱스 코멘트가 포함 된 나의 질문에 가장 잘 대답한다고 생각한다. 당신의 대답으로 코멘트를 옮기고 나서 나는 upvote하고 받아 들인 것으로 표시 할까? –

+0

일을 끝내면 내가 의견을 남기면 알림을받습니다. 만약 내가 대답을 편집하지 않는다면. 이제 얼마나 빠릅니까? –

2

너는 조금 지나치게 overthinking이다.당신은 P.ID = F.PET_ID에 가입하려는 :

SELECT P.ID, F.ID 
FROM PETS P 
FULL OUTER JOIN FOOD F ON P.ID = F.PET_ID 
         AND P.USER_ID = F.USER_ID 
         AND P.USER_ID = 1 --optional       
ORDER BY P.ID 
+0

답변을 제공해 주셔서 감사합니다. 불행히도, 이것은 저울에서 조금 느립니다. 나는 이것에 대해보다 명확하게 질문을 편집했다. 이 쿼리를 시도하고 122057 ms (두 테이블에서 순차 스캔이 발생했습니다) 걸렸습니다. –

+0

@JustinR. 아래의 Juan의 설명을 참조하십시오. 복합 인덱스는 작업 속도를 향상시켜야합니다. –