PostgreSQL IN,EXISTS,ANY/ALL,JOIN 에 대한 sql 최적화 방안
postgres=# select version();
version
---------------------------------------------------------------------------------------------------------
PostgreSQL 11.9 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-39), 64-bit
(1 row)
postgres=#
데이터 준비:
$ pgbench -i -s 10
postgres=# \d
List of relations
Schema | Name | Type | Owner
--------+------------------+-------+----------
public | pgbench_accounts | table | postgres
public | pgbench_branches | table | postgres
public | pgbench_history | table | postgres
public | pgbench_tellers | table | postgres
(4 rows)
postgres=# select * from pgbench_accounts limit 1;
aid | bid | abalance | filler
-----+-----+----------+--------------------------------------------------------------------------------------
1 | 1 | 0 |
(1 row)
postgres=# select * from pgbench_branches limit 1;
bid | bbalance | filler
-----+----------+--------
1 | 0 |
(1 row)
postgres=# select * from pgbench_history limit 1;
tid | bid | aid | delta | mtime | filler
-----+-----+-----+-------+-------+--------
(0 rows)
postgres=# select * from pgbench_tellers limit 1;
tid | bid | tbalance | filler
-----+-----+----------+--------
1 | 1 | 0 |
(1 row)
postgres=# select * from pgbench_branches;
bid | bbalance | filler
-----+----------+--------
1 | 0 |
2 | 0 |
3 | 0 |
4 | 0 |
5 | 0 |
6 | 0 |
7 | 0 |
8 | 0 |
9 | 0 |
10 | 0 |
(10 rows)
postgres=# update pgbench_branches set bbalance=4500000 where bid in (4,7);
UPDATE 2
postgres=#
IN 구문조회 요구:잔액(balance)이 0 이상 인 모든 분기(branch)를 찾 아 pgbench 에 있 습 니 다.accounts 에 계 정 이 몇 개 있 습 니까?
1.IN 자구 사용
SELECT
count( aid ),bid
FROM
pgbench_accounts
WHERE
bid IN ( SELECT bid FROM pgbench_branches WHERE bbalance > 0 )
GROUP BY
bid;
2.Any 자구 사용
SELECT
count( aid ),bid
FROM
pgbench_accounts
WHERE
bid = ANY ( SELECT bid FROM pgbench_branches WHERE bbalance > 0 )
GROUP BY
bid;
3.EXISTS 자 구 를 사용한다
SELECT
count( aid ),bid
FROM
pgbench_accounts
WHERE
EXISTS ( SELECT bid FROM pgbench_branches WHERE bbalance > 0 AND pgbench_accounts.bid = pgbench_branches.bid )
GROUP BY
bid;
4.INNER JOIN 사용
SELECT
count( aid ),a.bid
FROM
pgbench_accounts a
JOIN pgbench_branches b ON a.bid = b.bid
WHERE
b.bbalance > 0
GROUP BY
a.bid;
이 조회 요 구 를 완성 할 때,어떤 사람 은 exists 와 inner join 의 성능 이 더 좋 을 것 이 라 고 가정 할 수 있 습 니 다.왜냐하면 그들 은 두 표 로 연 결 된 논리 와 최 적 화 를 사용 할 수 있 기 때 문 입 니 다.IN 과 ANY 자 구 는 하위 검색 을 사용 해 야 합 니 다.그러나 PostgreSQL(10 버 전 이후)은 위의 네 가지 쓰기 에 대해 똑 같은 실행 계획 을 세 울 수 있 을 만큼 스마트 합 니 다!
모든 위의 문법 은 같은 실행 계획 을 만들어 낸다.
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=23327.73..23330.26 rows=10 width=12) (actual time=97.199..99.014 rows=2 loops=1)
Group Key: a.bid
-> Gather Merge (cost=23327.73..23330.06 rows=20 width=12) (actual time=97.191..99.006 rows=6 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Sort (cost=22327.70..22327.73 rows=10 width=12) (actual time=93.762..93.766 rows=2 loops=3)
Sort Key: a.bid
Sort Method: quicksort Memory: 25kB
Worker 0: Sort Method: quicksort Memory: 25kB
Worker 1: Sort Method: quicksort Memory: 25kB
-> Partial HashAggregate (cost=22327.44..22327.54 rows=10 width=12) (actual time=93.723..93.727 rows=2 loops=3)
Group Key: a.bid
-> Hash Join (cost=1.14..22119.10 rows=41667 width=8) (actual time=24.024..83.263 rows=66667 loops=3)
Hash Cond: (a.bid = b.bid)
-> Parallel Seq Scan on pgbench_accounts a (cost=0.00..20560.67 rows=416667 width=8) (actual time=0.023..43.151 rows=333333 loops=3)
-> Hash (cost=1.12..1.12 rows=1 width=4) (actual time=0.027..0.028 rows=2 loops=3)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Seq Scan on pgbench_branches b (cost=0.00..1.12 rows=1 width=4) (actual time=0.018..0.020 rows=2 loops=3)
Filter: (bbalance > 0)
Rows Removed by Filter: 8
Planning Time: 0.342 ms
Execution Time: 99.164 ms
(22 rows)
그렇다면,우 리 는 이러한 결론 을 얻 을 수 있 습 니까?우 리 는 마음대로 조 회 를 작성 할 수 있 고,PostgreSQL 의 스마트 는 나머지 문 제 를 처리 할 수 있 습 니까?!잠깐 만!
만약 우리 가 상황 을 배제 하 는 것 을 고려한다 면 일 은 달라 질 것 이다.
검색 제외
조회 요구:잔액(balance)이 0 보다 크 지 않 은 모든 지점(branch)을 찾 아 pgbenchaccounts 에 계 정 이 몇 개 있 습 니까?
1.NOT IN 사용
SELECT
count( aid ),bid
FROM
pgbench_accounts
WHERE
bid NOT IN ( SELECT bid FROM pgbench_branches WHERE bbalance > 0 )
GROUP BY
bid;
실행 계획:
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=23645.42..23647.95 rows=10 width=12) (actual time=128.606..130.502 rows=8 loops=1)
Group Key: pgbench_accounts.bid
-> Gather Merge (cost=23645.42..23647.75 rows=20 width=12) (actual time=128.598..130.490 rows=24 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Sort (cost=22645.39..22645.42 rows=10 width=12) (actual time=124.960..124.963 rows=8 loops=3)
Sort Key: pgbench_accounts.bid
Sort Method: quicksort Memory: 25kB
Worker 0: Sort Method: quicksort Memory: 25kB
Worker 1: Sort Method: quicksort Memory: 25kB
-> Partial HashAggregate (cost=22645.13..22645.23 rows=10 width=12) (actual time=124.917..124.920 rows=8 loops=3)
Group Key: pgbench_accounts.bid
-> Parallel Seq Scan on pgbench_accounts (cost=1.13..21603.46 rows=208333 width=8) (actual time=0.078..83.134 rows=266667 loops=3)
Filter: (NOT (hashed SubPlan 1))
Rows Removed by Filter: 66667
SubPlan 1
-> Seq Scan on pgbench_branches (cost=0.00..1.12 rows=1 width=4) (actual time=0.020..0.021 rows=2 loops=3)
Filter: (bbalance > 0)
Rows Removed by Filter: 8
Planning Time: 0.310 ms
Execution Time: 130.620 ms
(21 rows)
postgres=#
2.<>ALL 사용
SELECT
count( aid ),bid
FROM
pgbench_accounts
WHERE
bid <> ALL ( SELECT bid FROM pgbench_branches WHERE bbalance > 0 )
GROUP BY
bid;
실행 계획:
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=259581.79..259584.32 rows=10 width=12) (actual time=418.220..419.913 rows=8 loops=1)
Group Key: pgbench_accounts.bid
-> Gather Merge (cost=259581.79..259584.12 rows=20 width=12) (actual time=418.212..419.902 rows=24 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Sort (cost=258581.76..258581.79 rows=10 width=12) (actual time=413.906..413.909 rows=8 loops=3)
Sort Key: pgbench_accounts.bid
Sort Method: quicksort Memory: 25kB
Worker 0: Sort Method: quicksort Memory: 25kB
Worker 1: Sort Method: quicksort Memory: 25kB
-> Partial HashAggregate (cost=258581.50..258581.60 rows=10 width=12) (actual time=413.872..413.875 rows=8 loops=3)
Group Key: pgbench_accounts.bid
-> Parallel Seq Scan on pgbench_accounts (cost=0.00..257539.83 rows=208333 width=8) (actual time=0.054..367.244 rows=266667 loops=3)
Filter: (SubPlan 1)
Rows Removed by Filter: 66667
SubPlan 1
-> Materialize (cost=0.00..1.13 rows=1 width=4) (actual time=0.000..0.001 rows=2 loops=1000000)
-> Seq Scan on pgbench_branches (cost=0.00..1.12 rows=1 width=4) (actual time=0.001..0.001 rows=2 loops=337880)
Filter: (bbalance > 0)
Rows Removed by Filter: 8
Planning Time: 0.218 ms
Execution Time: 420.035 ms
(22 rows)
postgres=#
3.NOT EXISTS 사용
SELECT
count( aid ),bid
FROM
pgbench_accounts
WHERE
NOT EXISTS ( SELECT bid FROM pgbench_branches WHERE bbalance > 0 AND pgbench_accounts.bid = pgbench_branches.bid )
GROUP BY
bid;
실행 계획:
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=28327.72..28330.25 rows=10 width=12) (actual time=152.024..153.931 rows=8 loops=1)
Group Key: pgbench_accounts.bid
-> Gather Merge (cost=28327.72..28330.05 rows=20 width=12) (actual time=152.014..153.917 rows=24 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Sort (cost=27327.70..27327.72 rows=10 width=12) (actual time=147.782..147.786 rows=8 loops=3)
Sort Key: pgbench_accounts.bid
Sort Method: quicksort Memory: 25kB
Worker 0: Sort Method: quicksort Memory: 25kB
Worker 1: Sort Method: quicksort Memory: 25kB
-> Partial HashAggregate (cost=27327.43..27327.53 rows=10 width=12) (actual time=147.732..147.737 rows=8 loops=3)
Group Key: pgbench_accounts.bid
-> Hash Anti Join (cost=1.14..25452.43 rows=375000 width=8) (actual time=0.134..101.884 rows=266667 loops=3)
Hash Cond: (pgbench_accounts.bid = pgbench_branches.bid)
-> Parallel Seq Scan on pgbench_accounts (cost=0.00..20560.67 rows=416667 width=8) (actual time=0.032..45.174 rows=333333 loops=3)
-> Hash (cost=1.12..1.12 rows=1 width=4) (actual time=0.036..0.037 rows=2 loops=3)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Seq Scan on pgbench_branches (cost=0.00..1.12 rows=1 width=4) (actual time=0.025..0.027 rows=2 loops=3)
Filter: (bbalance > 0)
Rows Removed by Filter: 8
Planning Time: 0.322 ms
Execution Time: 154.040 ms
(22 rows)
postgres=#
4.LEFT JOIN 과 IS NULL 사용
SELECT
count( aid ),a.bid
FROM
pgbench_accounts a
LEFT JOIN pgbench_branches b ON a.bid = b.bid AND b.bbalance > 0
WHERE
b.bid IS NULL
GROUP BY
a.bid;
실행 계획:
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------------------------
Finalize GroupAggregate (cost=28327.72..28330.25 rows=10 width=12) (actual time=145.298..147.096 rows=8 loops=1)
Group Key: a.bid
-> Gather Merge (cost=28327.72..28330.05 rows=20 width=12) (actual time=145.288..147.083 rows=24 loops=1)
Workers Planned: 2
Workers Launched: 2
-> Sort (cost=27327.70..27327.72 rows=10 width=12) (actual time=141.883..141.887 rows=8 loops=3)
Sort Key: a.bid
Sort Method: quicksort Memory: 25kB
Worker 0: Sort Method: quicksort Memory: 25kB
Worker 1: Sort Method: quicksort Memory: 25kB
-> Partial HashAggregate (cost=27327.43..27327.53 rows=10 width=12) (actual time=141.842..141.847 rows=8 loops=3)
Group Key: a.bid
-> Hash Anti Join (cost=1.14..25452.43 rows=375000 width=8) (actual time=0.087..99.535 rows=266667 loops=3)
Hash Cond: (a.bid = b.bid)
-> Parallel Seq Scan on pgbench_accounts a (cost=0.00..20560.67 rows=416667 width=8) (actual time=0.025..44.337 rows=333333 loops=3)
-> Hash (cost=1.12..1.12 rows=1 width=4) (actual time=0.026..0.027 rows=2 loops=3)
Buckets: 1024 Batches: 1 Memory Usage: 9kB
-> Seq Scan on pgbench_branches b (cost=0.00..1.12 rows=1 width=4) (actual time=0.019..0.020 rows=2 loops=3)
Filter: (bbalance > 0)
Rows Removed by Filter: 8
Planning Time: 0.231 ms
Execution Time: 147.180 ms
(22 rows)
postgres=#
NOT IN 과<>ALL 생 성 실행 계획 에는 하위 조회 가 포함 되 어 있 습 니 다.그들 은 각자 독립 했다.NOT EXISTS 와 LEFT JOIN 은 같은 실행 계획 을 세 웠 다.
이러한 hash 연결(또는 hash anti join)은 조회 요 구 를 완성 하 는 가장 유연 한 방식 입 니 다.이것 도 exists 나 join 을 추천 하 는 이유 다.따라서 exists 나 join 의 경험 법칙 을 추천 하 는 것 이 효과적이다.
하지만 우 리 는 계속 내 려 다 보 자!하위 조회 실행 계획 이 있어 도 NOT IN 자구 의 실행 시간 이 더 좋 을 까?
예.PostgreSQL 은 훌륭 한 최적화 를 했 고 PostgreSQL 은 하위 조회 계획 을 hash 처리 했다.그래서 PostgreSQL 은 IN 자 구 를 어떻게 처리 하 는 지 에 대해 더 잘 이해 하 게 되 었 다.이것 은 논리 적 사고방식 이다.왜냐하면 많은 사람들 이 IN 자 구 를 사용 하 는 경향 이 있 기 때문이다.하위 조회 가 되 돌아 오 는 줄 은 매우 적 지만,하위 조회 가 수백 줄 로 되 돌아 오 더 라 도 같은 상황 이 발생 할 수 있다.
그러나 하위 조회 가 대량 줄(몇 십 만 줄)로 돌아 가면 어떻게 합 니까?간단 한 테스트 를 시도 해 봅 시다.
CREATE TABLE t1 AS
SELECT * FROM generate_series(0, 500000) id;
CREATE TABLE t2 AS
SELECT (random() * 4000000)::integer id
FROM generate_series(0, 4000000);
ANALYZE t1;
ANALYZE t2;
EXPLAIN SELECT id
FROM t1
WHERE id NOT IN (SELECT id FROM t2);
실행 계획:
QUERY PLAN
--------------------------------------------------------------------------------
Gather (cost=1000.00..15195064853.01 rows=250000 width=4)
Workers Planned: 1
-> Parallel Seq Scan on t1 (cost=0.00..15195038853.01 rows=147059 width=4)
Filter: (NOT (SubPlan 1))
SubPlan 1
-> Materialize (cost=0.00..93326.01 rows=4000001 width=4)
-> Seq Scan on t2 (cost=0.00..57700.01 rows=4000001 width=4)
(7 rows)
postgres=#
여기 서 실행 계획 은 하위 조 회 를 물 화 했다.대가 평 가 는 1519503883.01 로 바 뀌 었 다.PostgreSQL 의 기본 설정 은 t2 표 의 줄 이 100 k 보다 낮 으 면 하위 조 회 를 hash 로 합 니 다).이렇게 하면 성능 에 심각 한 영향 을 줄 것 이다.따라서 그 피 드 조회 가 되 돌아 오 는 줄 수가 적은 장면 에 IN 자 구 는 좋 은 역할 을 할 수 있다.기타 주의 점
있다우리 가 서로 다른 방식 으로 조 회 를 쓸 때 데이터 형식의 변환 이 있 을 수 있 습 니 다.
예 를 들 면,문장:
EXPLAIN ANALYZE SELECT * FROM emp WHERE gen = ANY(ARRAY['M', 'F']);
암시 적 인 형식 변환 이 발생 합 니 다:
Seq Scan on emp (cost=0.00..1.04 rows=2 width=43) (actual time=0.023..0.026 rows=3 loops=1)
Filter: ((gen)::text = ANY ('{M,F}'::text[]))
여기(gen):text 에서 형식 변환 이 발생 했 습 니 다.큰 표 에 서 는 이런 유형 전환 의 대가 가 높 기 때문에 PostgreSQL 은 IN 자 구 를 더 잘 처리 했다.
EXPLAIN ANALYZE SELECT * FROM emp WHERE gen IN ('M','F');
Seq Scan on emp (cost=0.00..1.04 rows=3 width=43) (actual time=0.030..0.034 rows=3 loops=1)
Filter: (gen = ANY ('{M,F}'::bpchar[]))
IN 자 구 를 ANY 자구 로 변환 하고 gen 열 을 유형 변환 하지 않 았 습 니 다.M\F 를 bpchar 로 바 꿨 습 니 다.총결산
쉽게 말 하면 exists 와 직접 join 표 가 좋 습 니 다.
PostgreSQL 은 IN 자 구 를 hash 의 하위 계획 으로 바 꾸 는 경우 가 많다.일부 특수 한 장면 에서 IN 은 더 좋 은 실행 계획 을 얻 을 수 있다.이상 은 개인 적 인 경험 이 므 로 여러분 에 게 참고 가 되 기 를 바 랍 니 다.여러분 들 도 저 희 를 많이 응원 해 주시 기 바 랍 니 다.만약 잘못 이 있 거나 완전히 고려 하지 않 은 부분 이 있다 면 아낌없이 가르침 을 주시 기 바 랍 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Redmine 데이터베이스를 MySQL에서 PostgreSQL로 마이그레이션 (보충)Redmine 의 Database 를 MySQL 로 운용하고 있었습니다만, MySQL 5.6 이상이나 MariaDB 에는 , , 이러한 티켓이 수년 동안 방치된 상황을 감안하여, PostgreSQL로 마이그레이션하기...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.