'SQL' 카테고리의 다른 글
Bulk Insert 옵션에 따른 성능 비교 (0) | 2011.05.03 |
---|---|
Case When문 (0) | 2011.05.02 |
Case When문
Case When문이나 Decode함수를 사용하여 Select절의 결과물을 원하는 대로 변경하는 SQL은 여러 번 본 적이 있다.
하지만 주변 사람들과 이야기해보면 의외로 Select절 뿐만 아니라 Where절, Order By절, Group By절 등에 다 사용할 수 있다는 사실을 모르는 경우가 있었는데 오늘은 이런 사실에 대해 이야기해보고자 한다.
Case When문의 활용범위는 무궁무진하며, 이 것을 사용하지 않고 Static한 SQL을 사용한다는 것이 필자로서는 거의 불가능하게 느껴진다. 앞으로의 포스트에서도 줄기차게 사용할 것이므로 관심이 있으신 분들은 여러 활용방법을 제대로 알아두면 좋을 것 같다.
Case When문
간단하게는 Case When문은 Decode함수의 확장이다.
Decode함수는 Oracle에서 제공되는 SQL 내의 if/else의 조건문 역할을 하는 함수이나 = 연산만 가능하다. Case When문은 이를 확장한 것이며, 논리연산, 산술연산, 관계 연산을 다 지원하며 Oracle 뿐만 아니라 MS-SQL에서도 지원한다.
(예전에 듣기로는 My-SQL에서도 지원한다고 들은 적이 있다. Test해보지는 않았지만.)
복잡한 조건일 수록 Case When문은 Decode문에 비해 가독성이 높고, 수행속도도 빠르다고 할 수 있겠다.
사용하는 문법은 다음과 같다.
- case when [조건1] then [결과1] when [조건2] then?[결과2] ...else [결과3] end
- case [컬럼1] when [값1] then [결과1] when [값2] then [결과2] … else [결과 3] end
Select 절에서의 활용
실제로 가장 많이 사용되는 활용은 Select절에서일 것이다. 실제 우리가 가지고 있는 Data로부터 우리가 원한 Data를 뽑아내는데 자주 사용된다.
이 SQL은 당연히 다음과 같이도 사용가능하다.
지역이라는 Column이 없기 때문에 수도권인지 아닌지를 가져오기 위해 위와 같은 SQL을 사용하였다.
(왜 수도권에 '인천'은 없는지에 대해서 궁금해하는 사람은 없기 바란다. PT하다보면 가끔 그런 사람을 만날 수 있는데 정말 한숨만 나온다-_- 집중해야 할 곳에 집중하자.)
Group By 절에서의 활용
사례를 살펴보기 위해서 Cartesian Product(카테시안 곱) 항목에서 예로 들었던 SQL을 다시 가져와 보자.
From Table1 tbl1, (Select 1 no1 From dual Union All Select 2 From dual) tbl2
Group By (Case tbl2.no1 When 1 Then tbl1.품목 Else ‘합계’ End), tbl2.no1
복제된 Data에서 no1이 1인 것은 품목으로 Group by를 하였고, 2인 것은 "합계" 즉 상수로 Group by를 하였다. 따라서, 1인 경우는 품목별로 합계를 구하게 되고, 나머지 항목들은 전체 Group by가 되게 될 것이다.
Order By 절에서의 활용
회사 직원을 이름 순으로 뽑아오는 쿼리가 있다고 하자. 어느 날 직원 시스템을 한 번도 써보지 않았던 사장님이 시스템에 들어가보니 본인 이름이 중간에 있는 것을 발견하고 명색이 사장님인데 직원들 순서 중에서 내가 제일 앞에 나와야 하지 않는가 하는 의문을 제기하였다.
그런-_- 이유로 고쳐야할 시스템의 요구사항은 다음과 같다.
사장님의 이름만 맨 앞으로 뽑아내고 나머지는 가나다 순으로 정렬하면 되는 것이다. 물론 일단 Data를 Client로 다 가져온 다음 프로그램 상에서 처리할 수도 있겠으나, 나중에 이 사실을 알게 된 이사님도 이의를 제기한다면? 다른 관리자들은? 이런 요구사항들을 다 Client에서 처리한다면 프로그램은 갈수록 복잡해지게 될 것이다.
따라서, 그런 요구사항의 변경은 SQL에서 적용하는 것이 가장 간단할 것이며, 이 것이 우리가 추구하고자 하는 Static SQL의 본질이다. (Static SQL이란 원하는 집합을 하나의 SQL로 뽑아내는 것을 말하며 이 문제영역에서는 사장님이 맨 앞에 나오고 다른 직원들은 이름순으로 정렬된 집합이 우리가 원하는 집합이다. 따라서, 그것을 하나의 SQL로 얻어와야 한다.)
Order by절에서는 오름차순이건, 내림차순이건 사장님의 이름만 맨 앞으로 뽑아올 수 없으나, Case When문을 사용하면 간단하다.
From Emp
Order By (Case When 직함 = '사장' Then 1 Else 2 End), 이름
Where절에서의 활용
외부에서 들어온 조건(:v1)에 따라 서로 다른 동작을 정의하고자 할 때 사용할 수 있다.
From Emp
Where (Case When Length(:v1) = 0 Then 1 Else Instr(이름, :v1) End) > 0
검색할 이름에 대한 입력이 :v1에 들어오면 이름을 검색하여 결과를 보여주고 입력이 없으면 전체 직원의 이름을 Return하는 SQL이다. Where절을 동적으로 조합하지 않고 SQL 내부에서도 이러한 방법을 통해 서로 다른 결과값을 Return할 수 있다.
결론
SQL을 작성하는 여러가지 방법을 배우는 것은 문제에 따라 다양한 Idea를 내기 위한 필요조건이다. Client에서 혹은 PL/SQL에서의 if/else를 상당부분 SQL로 옮겨올 수 있는 Case When문을 익혀두는 것은 그 중에서도 첫 걸음이 될 수 있을 것이다
출처 : http://rtti.tistory.com/
'SQL' 카테고리의 다른 글
Bulk Insert 옵션에 따른 성능 비교 (0) | 2011.05.03 |
---|---|
Alias(별칭) "AS" (0) | 2011.05.02 |
우선순위 큐(Priority Queue)와 힙(Heap)
우선순위 큐와 힙을 비교해 봄으로써 추상적 데이타 타입과 자료구조의 차이를 명확하게 이해할 수 있다.
우선순위 큐는 추상적 데이타 타입(ADT)의 하나로서, ADT가 정의하는 것은 어떤 블랙 박스 같은 것으로,
그 안에 무엇이 어떻게 들어 있는지에는 관심이 없고, '어떤 일을 할 수 있다'에 초점을 맞춘다.
심지어 그 어떤 일을 할 때 얼마의 시간이 걸리는지에 대해서도 별로 관심이 없다.
우선순위 큐는 다음과 같은 일을 할 수 있다.
1. 큐에 '무엇인가'를 넣을 수 있다. 여기에 숫자로 된 우선순위를 꼬리표로 해서 함께 넣는다.
2. 큐에서 가장 높은 우선순위의 꼬리표가 붙은 '무엇인가'를 꺼낼 수 있다.
3. 2번의 변형으로, 가장 높은 우선순위에 무엇이 있는지 볼 수 있다. (꺼내지 않는다)
왜 가장 높은 우선순위의 것만 꺼낼 수 있고, 다른 것을 임의로 꺼낼 수도 없는,
그렇게 제약이 많은 것을 생각하느냐고 할 수도 있겠지만,
ADT에서 중요한 개념은 이것이다.
시간이나 공간 같은 제약을 고려하지 않고, 한정된 수의 요구사항을 명확히 하는 것.
요구사항이 한정되어야 그 다음에, 어떤 방법으로 구현하는 것이 효율적인지 생각할 수 있기 때문이다.
'배열로 구현하지'라고 먼저 생각해 버리면, 배열에서 효율적으로 동작하는 새로운 기능을 추가할 수도 있겠지만,
일의 순서가 그런 것이 아니기 때문이다.
이렇게 우선순위 큐의 기능을 정의하고 나서, 우선순위 큐를 효율적으로 구현하기 위한 자료구조를 생각해 보자.
메모리에 저장된 형태에 대해서 다음과 같은 것을 생각해 볼 수 있다.
1. 배열 - 여기에는 다시 임의 배열과, 정렬된 배열
2. 리스트 - 역시 임의 리스트와, 정렬된 리스트
3. 그리고 다음에 설명할 힙(Heap)
배열과 리스트가 별도의 자료구조인 것은 명확하지만, 임의로 넣은 것과, 정렬된 상태인 것이 다른 것인 이유는,
배열과 리스트에 대한 연산이 다르듯이, 정렬되었는지 여부에 따라 연산의 구현이 확연하게 달라지기 때문이다.
임의 배열을 사용할 경우 우선순위 큐의 구현은 다음과 같이 이루어진다.
1. 넣을 때 - 현재 들어 있는 것들 뒤에 넣는다. O(1)
2. 꺼낼 때 - 우선순위 값을 비교하여 가장 큰 것을 꺼낸다. O(n)
정렬되어 있는 배열을 사용할 경우 우선순위 큐의 구현은 다음과 같이 이루어진다.
1. 넣을 때 - 정렬된 순서에서의 위치를 찾아서 넣는다. O(n)
2. 꺼낼 때 - 맨 앞의 것을 꺼내면 된다. O(1)
n개를 넣고 꺼낼 때 걸리는 시간은, 둘 다 O(n * n)이 된다.
n개를 다 넣고 나서 - 정렬하고 - n개를 순서대로 꺼내는 것은, 우선순위 큐를 쓰고자 하는 목적에도 맞지 않으며,
우선순위 큐의 정의에 정렬 같은 것은 없다.
다만 이렇게 했을 때 걸리는 시간은 정렬에 걸리는 시간인 O (n log n)으로 낮출 수 있다.
이제 힙에 대해 얘기해 보자.
힙이라는 자료구조를 가지고 얻고자 하는 것은 임의로 넣고 꺼내는 것을 지원하면서, O(n * n)보다 빠른 방법이다.
이를 위해서 힙이라는 자료구조는 개념상으로 바이너리 트리에서, '부모 노드는 자식 노드보다 크다'라는 가정만
유지한다. 당연히 최상위 루트 노드의 값이 가장 크다.
이를 배열에 저장했을 때 유지되는 내부 형태는, n번째 항목의 자식 노드는 n * 2와, n * 2 + 1 번째에 위치한다.
그리고 배열에 들어있는 값들은 정렬된 형태도 아니고, 임의 형태도 아니지만,
맨 앞에는 가장 우선순위가 큰 값을 갖고 있게 된다.
1. 넣을 때 - 맨 뒤에 넣는다. 그리고 형제 노드, 부모 노드와 비교해서 셋 중에 큰 값을 부모 노드로 만든다.
그리고 위로 전파한다. O(log n)에 수행 가능하다.
2. 꺼낼 때 - 맨 앞의 것을 꺼낸다. 그리고 이 자리에는 자식 노드 둘 중에 큰 값을 갖고 올라온다.
그리고 아래로 전파한다. 역시 O(log n)에 수행 가능하다.
따라서 n개를 넣고 꺼낼 때 걸리는 시간은, O(n log n)이 된다.
전체적으로, 정렬에 걸리는 시간과 같은 복잡도를 갖게 되었다.
여기서 얘기하는 힙은 정확하게는 Binary Heap의 array implementation 이다.
이와 같이 추상적 데이타 타입이 문제에 초점을 둔다면, 자료구조는 해결에 초점을 두고 있다.
인터페이스와 임플리멘테이션도 이와 비슷하게 생각할 수 있을 것이다.
좀 더 자세한 것은 위키피디아에서 Priority Queue와 Heap을 검색해 보기 바란다.
출처 : http://core.egloos.com/
'Java' 카테고리의 다른 글
ArrayIndexOutOfBoundsException (0) | 2011.07.13 |
---|---|
Collection (0) | 2011.05.02 |