📢 들어가며
업무 중 콤마를 포함한 스트링으로 DB 데이터를 조회해야하는 일이 생겼다.
간단히 설명하자면, "apple,banana,orange"라는 데이터를 콤마로 구분하여 각 단어를 뽑아내고
fruit 라는 컬럼에서 apple, banana, orange가 존재한다면 select 해오는 일이었다.
자바에서 작업하고 List<String>
로 넘겨 Mybatis에서 IN
작업을 해주려고 했었는데,
PostgreSQL ANY
만으로도 간단히 해결하는 방법을 찾아내어 포스팅해보려고 한다.
💬 ANY 란?
서브 쿼리의 반환 값과 비교할 때 사용하는 연산자.
서브 쿼리의 반환 값 중 하나라도 일치하면 조건이 성립된다.
JavaScript 유저라면 '하나라도 일치하면' 이라는 문구에서 some()
과 비슷한 느낌을 받았을 것이다.
실제로 SQL 에도 SOME
이라는 연산자가 존재하고, 이는 ANY
와 동의어이다.ANY
가 쓰일 수 있는 모든 곳에 SOME
을 쓸 수 있다.
이렇게만 봐선 아직 잘 모르겠다. 아래 예제를 살펴보자.
💬 ANY 예제
SELECT title
FROM film
WHERE length >= ANY(
SELECT MAX( length )
FROM film
INNER JOIN film_category USING(film_id)
GROUP BY category_id );
ANY
내의 서브 쿼리는 카테고리로 그룹 지어진 영화 중 가장 긴 length
(컬럼 명임)를 반환한다는 내용이다.
예를 들어 서브쿼리의 반환 값이 7 이라고 했을 때film
테이블의 length
(컬럼 값)가 7보다 같거나 크면 해당 title
을 반환한다.
즉, ANY는 서브 쿼리의 반환 값과 특정 컬럼을 비교 했을 떄 하나라도 일치한다면 조건이 성립하는 것이다.
위 예제에선 MAX
를 썼기 때문에 서브 쿼리의 반환 값이 하나여서 굳이 ANY를 쓰지 않아도 됐겠지만
반환 값이 여러개인 경우 ANY
가 유용하게 쓰일 수 있다.
위와 같은 서브 쿼리 외에도 function 의 반환 값과도 ANY
를 사용할 수 있다.
"📢 들어가며" 에서 언급한 상황을 시도해보자.
select
fruit
from
food
where
fruit = ANY(string_to_array('apple,banana,orange', ','))
string_to_array
연산자가 보이는가?string_to_array
가 무엇인지도 짚고 넘어가보자.
💡 string_to_array
string_to_array
는 문자열을 특정 문자(구분자)로 나누거나 특정 문자를 null
로 만들고 싶을 떄 사용하는 연산자이다.
string_to_array('문자열', '구분자', '구분자', '구분자'...)
💡 string_to_array 예제
SELECT string_to_array('xx~^~yy~^~zz', '~^~', 'yy');
결과
string_to_array
-----------------
{xx,NULL,zz}
(1 row)
문자열 xx~^~yy~^~zz
에서 ~^~
와 yy
가 null로 대체된 것을 알 수 있다!
다시 ANY
예제로 돌아와보자.
즉, string_to_array('apple,banana,orange', ',')
는{apple,NULL,banana,NULL,orange}
를 반환할 것이다.
이 반환 값이 ANY
를 돌게 되는데,fruit
컬럼의 값이 apple
또는 banana
또는 orange
와 같다(=
)면 참이라는 뜻이다.
💬 ANY 와 IN의 차이
결론적으로 ANY
는 비교 연산자(>=
, <=
, =
, >
, <
등)와 사용이 가능하고 IN
은 그렇지 않다는 것이다.
이 것 외엔 여러 값을 하나의 컬럼 값과 비교한다는 점에서 거의 유사하다고 할 수 있다.
ANY
예제
SELECT
title, category_id
FROM
film
INNER JOIN
film_category
USING(film_id)
WHERE
category_id = ANY(
SELECT
category_id
FROM
category
WHERE
NAME = 'Action'
OR
NAME = 'Drama'
);
IN
예제
SELECT
title, category_id
FROM
film
INNER JOIN
film_category
USING(film_id)
WHERE
category_id IN(
SELECT
category_id
FROM
category
WHERE
NAME = 'Action'
OR
NAME = 'Drama'
);