[Postgresql] exists 연산자 사용법 (count(*) > 0가 느릴땐 exists를 사용하자)

안녕하세요. 오늘은 exists() 연산자에 대해서 포스팅을 해보고자 합니다.

 

사실 함수인지 연산자인지 개인적으로 혼란이 오는 부분이 있었지만 알아본 결과.. operator라는 단어로 서술되어있는 documentation이 많이있더라구요. 따라서 이 포스팅 내에서도 연산자로 서술하였습니다.

 


 

문법

일단은 매개변수를 table 형태의 객체를 받고, bool 타입의 값을 리턴하는 형태의 구조를 띄고 있습니다.

EXISTS (subquery)

- 서브쿼리라함은 그 자체로 테이블 형태의 결과를 도출하기 때문에 Table 객체 타입으로 봐도 무방할 것 같습니다. exists 연산자를 사용할 때에는 이 subquery를 어떻게 간결하게 표현하느냐가 그 성능을 좌우하기도 합니다.

 


반환 타입

bool (true/false)

 

 

사용 예제

서브쿼리가 중요하다는 것은

 

예컨데, 무수히 많은 목록이 나오는 쿼리가 exists의 인자로 넘어가게 된다면.. 별 필요가 없습니다. exists는 단지 해당 서브쿼리에서 값이 있는지만 보기 때문입니다.

 

ex1) 아래의 경우에는 어차피 tblTable의 column_a에 값이 'test'인 모든 컬럼을 가져오게 됩니다.

select exists(
	select * from tblTable where column_a = 'test' 
);

이런식으로 가게 되면 결국 exists 내부 서브쿼리는 모든 테이블은 완성하고 exists 연산자는 그 테이블안에 값이 있는지 없는지만 판단하게 됩니다.

select exists(
	[column_a]
1    'test'
2    'test'
3    'test'
4    'test'
5    'test'
); -- true

결국 이런식의 진행이 되겠습니다.

 

ex2) 아래의 경우는 ex1번에서 효율적이지 못했던 부분을 바로 해결해줍니다. limit 1 처리를 통해서 2번째 이후의 row는 보지 않게됩니다. 단지 column_a = 'test' 의 조건을 만족하는 값이 1개라도 있으면 exists 연산은 true가 나오게 될테니까요.

select exists(
	select 1 from tblTable where column_a = 'test' limit 1
);

 

ex3) 하지만 결국 limit 1이라는 처리는 exists 연산에 의해 자동적으로 첫번째 컬럼이 등장하게 되면 종료가 되는 특성 때문에 처리되지 않고 바로 exists 연산에 의해 종료됩니다.

select exists(
	select 1 from tblTable where column_a = 'test'
);

따라서 위 처럼 limit 1 처리를 해주지 않아도 실행계획은 동일한 결과를 발생시킵니다.

 


 

즉, exists의 특성을 살펴보면 내부 서브쿼리(사실은 테이블형태)의 실행에 따라 첫번째 컬럼이 발견되자마자 해당 서브쿼리를 종료시키고 true를 반환하는 로직으로 동작하게 됩니다.

 

따라서 우리가 흔히 SQL에서 사용하는 존재여부 쿼리는 다음과 같을 것입니다.

 

ex) count(*) > 0을 활용한 검출쿼리

select coun(*) from tblTable where column_a = 'test'

좀더 복잡한 쿼리일 수록 실행계획은 무거워지고 Postgresql DBMS의 특성상 count 쿼리의 성능이 현저히 떨어지기 때문에 Paging이라던지 count를 활용하는 부분의 성능이 전체적으로 떨어지게 됨으로써 구현하는 어플리케이션의 병목이 이곳에서 막히게 될 가능성이 높아지는 부분입니다.

 

이러한 부분을 exists로 어느정도 해소할 수 있게 될 것 같습니다.

 

Postgresql을 활용하시는 분들은 반드시 count 쿼리가 상당히 느리다는 부분을 인지하시고 exists 연산을 활용해서 존재여부쿼리를 작성하시기를 추천드립니다.

 

감사합니다.

댓글

Designed by JB FACTORY