Oracle SELECT 로 출력물 다단(왼쪽·오른쪽 2단) 만들기
명단·라벨 같은 출력물은 한 페이지를 두 단으로 나눠 찍는 경우가 많다. 리포트 도구에서 다단 기능을 못 쓰거나 쓰기 번거로울 때는 SQL 에서 아예 “한 행 = 출력물의 한 줄(왼쪽 칸, 오른쪽 칸)” 모양으로 만들어 넘기면 편하다.
목표: 한 페이지 20건, 왼쪽 단 10건 → 오른쪽 단 10건 → 다음 페이지 순서.
공식
데이터에 1부터 순번(rn)을 매기고, 한 단의 줄 수를 N(=10), 단 수를 K(=2)라고 하면:
| 값 | 식 | 뜻 |
|---|---|---|
| 페이지 | CEIL(rn / (N*K)) |
20건마다 한 페이지 |
| 줄 | MOD(rn - 1, N) + 1 |
페이지 안에서 몇 번째 줄인가 (1~10) |
| 단 | MOD(FLOOR((rn - 1) / N), K) |
0 = 왼쪽, 1 = 오른쪽 |
같은 (페이지, 줄) 끼리 묶어서 단별로 한 칸씩 펼치면 된다.
숫자 1~50 으로 확인하기
WITH src AS (
SELECT LEVEL AS rn
FROM dual
CONNECT BY LEVEL <= 50
)
, pos AS (
SELECT rn
, CEIL(rn / 20) AS page_no
, MOD(rn - 1, 10) + 1 AS line_no
, MOD(FLOOR((rn - 1) / 10), 2) AS col_no
FROM src
)
SELECT page_no
, line_no
, MAX(CASE WHEN col_no = 0 THEN rn END) AS l
, MAX(CASE WHEN col_no = 1 THEN rn END) AS r
FROM pos
GROUP BY page_no, line_no
ORDER BY page_no, line_no;
결과(일부):
| PAGE_NO | LINE_NO | L | R |
|---|---|---|---|
| 1 | 1 | 1 | 11 |
| 1 | 2 | 2 | 12 |
| … | … | … | … |
| 1 | 10 | 10 | 20 |
| 2 | 1 | 21 | 31 |
| … | … | … | … |
| 2 | 10 | 30 | 40 |
| 3 | 1 | 41 | (NULL) |
| … | … | … | … |
| 3 | 10 | 50 | (NULL) |
마지막 페이지는 오른쪽 단이 비어 NULL 로 나온다.
실제 테이블에 쓰기
순번은 ROW_NUMBER() 로 매기고, 찍을 컬럼마다 MAX(CASE ...) 를 하나씩 둔다.
WITH src AS (
SELECT ROW_NUMBER() OVER (ORDER BY e.dept_no, e.emp_no) AS rn
, e.emp_no
, e.emp_name
FROM emp e
WHERE e.retire_yn = 'N'
)
, pos AS (
SELECT s.*
, CEIL(rn / 20) AS page_no
, MOD(rn - 1, 10) + 1 AS line_no
, MOD(FLOOR((rn - 1) / 10), 2) AS col_no
FROM src s
)
SELECT page_no
, line_no
, MAX(CASE WHEN col_no = 0 THEN emp_no END) AS l_emp_no
, MAX(CASE WHEN col_no = 0 THEN emp_name END) AS l_emp_name
, MAX(CASE WHEN col_no = 1 THEN emp_no END) AS r_emp_no
, MAX(CASE WHEN col_no = 1 THEN emp_name END) AS r_emp_name
FROM pos
GROUP BY page_no, line_no
ORDER BY page_no, line_no;
- 한 칸에는 값이 하나뿐이라
MAX는 그 값을 그대로 꺼내는 역할만 한다. 문자열에도 쓸 수 있다. ROW_NUMBER()의 정렬 기준은 유일해야 한다. 같은 값이 있는 컬럼으로만 정렬하면 실행할 때마다 순서가 바뀌어 출력물이 달라질 수 있다. 끝에 PK(emp_no)를 붙여 두자.- 3단으로 바꾸려면
20(=N×K)을30으로, 단 계산의2를3으로 바꾸고col_no = 2칸을 추가한다.
PIVOT 으로 쓰기
같은 결과를 PIVOT 으로도 만들 수 있다. 컬럼이 많을 때는 이쪽이 짧다.
WITH src AS (
SELECT ROW_NUMBER() OVER (ORDER BY e.dept_no, e.emp_no) AS rn
, e.emp_no
, e.emp_name
FROM emp e
WHERE e.retire_yn = 'N'
)
SELECT *
FROM (
SELECT CEIL(rn / 20) AS page_no
, MOD(rn - 1, 10) + 1 AS line_no
, MOD(FLOOR((rn - 1) / 10), 2) AS col_no
, emp_no
, emp_name
FROM src
)
PIVOT (
MAX(emp_no) AS emp_no
, MAX(emp_name) AS emp_name
FOR col_no IN (0 AS l, 1 AS r)
)
ORDER BY page_no, line_no;
결과 컬럼 이름은 L_EMP_NO, L_EMP_NAME, R_EMP_NO, R_EMP_NAME 이 된다.
다시 쓰며 바로잡은 점
- 원래 글은
rn - FLOOR((rn + 10 - 1) / 20) * 10하나로 “페이지를 이어 붙인 줄 번호”를 계산했다. 결과는 맞지만 읽기 어려워서 페이지·줄·단을 따로 계산하도록 바꿨다. 출력 도구에서 페이지 나눔에page_no를 바로 쓸 수 있다. CONNECT BY LEVEL < 51은<= 50으로,DECODE는CASE로 바꿔 의도가 드러나게 했다.- 실제 테이블에 쓸 때 정렬 기준을 유일하게 잡아야 한다는 점을 덧붙였다.