MariaDB 강좌 / ORDER BY·LIMIT로 정렬하고 조회 범위 제한하기
ORDER BY는 조회 행의 순서를 정하고 LIMIT는 그 순서에서 가져올 행 수와 시작 위치를 제한합니다. 같은 가격처럼 정렬값이 중복되면 기본키를 보조 정렬 기준으로 추가해야 상위 목록과 페이지 경계를 일관되게 설명할 수 있습니다.
정렬 기준과 가져올 개수를 따로 정하기
가격이 높은 상품 세 개를 고른다면 가격 내림차순과 세 행 제한이 모두 필요합니다. LIMIT 3만 적으면 서버가 어떤 세 행을 먼저 반환할지 업무상 순서가 보장되지 않습니다. 정렬은 원본 행의 저장 위치를 재배치하는 작업이 아니라 조회 결과의 순서를 정하는 작업입니다. 정렬 전에 남길 행은 WHERE로 조건 지정하기: 비교·AND·OR·IN·BETWEEN·LIKE로 정합니다. 조회 결과의 의미를 유지하며 깊은 페이지 비용을 검토하는 과정은 조회 조건·정렬·페이지 나누기를 개선하기에서 같은 기준으로 확장합니다.
| 표현 | 뜻 | 이 예제의 사용 |
|---|---|---|
| ASC | 오름차순·기본 방향 | 같은 가격에서 번호 작은 순 |
| DESC | 내림차순 | 가격 높은 순 |
| LIMIT 3 | 최대 3행 | 상위 상품 세 개 |
| LIMIT 2 OFFSET 2 | 앞의 2행을 건너뛰고 최대 2행 | 두 번째 페이지 |
각 정렬 열마다 방향을 지정합니다. ORDER BY price DESC, product_id ASC에서 DESC가 두 열 모두에 적용되는 것은 아닙니다. 숫자처럼 보이는 문자열을 정렬하면 숫자 열과 순서가 달라질 수 있으므로 타입도 확인합니다.
같은 가격이 있는 상품 데이터 만들기
명령줄 클라이언트에서 course_user로 학습용 서버에 접속하고 shopcourse 데이터베이스를 준비합니다. 접속과 UTF-8 SQL 파일 실행 방법은 명령줄 클라이언트와 SQL 파일 실행 안내를 참고하세요. 아래 코드는 운영 서버가 아닌 연습 환경에서 실행합니다.
이 글은 products_17_lab이라는 독립된 실습 테이블을 사용합니다. 이전 강좌의 products 등 기존 테이블에는 손대지 않습니다. SELECT VERSION(), DATABASE();와 SHOW TABLES LIKE 'products_17_lab';로 서버·선택한 데이터베이스·이름 중복을 먼저 확인합니다. 같은 이름이 이미 있으면 정의와 데이터를 확인하고, 이 글에서 만든 연습 데이터인지 판단한 뒤 마지막 절의 반복 방법을 따르세요.
USE shopcourse; CREATE TABLE products_17_lab ( product_id INT NOT NULL AUTO_INCREMENT, product_name VARCHAR(100) NOT NULL, category VARCHAR(30) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, note VARCHAR(100) NULL DEFAULT NULL, PRIMARY KEY (product_id), CONSTRAINT ck_p17_price CHECK (price >= 0), CONSTRAINT ck_p17_stock CHECK (stock >= 0) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; INSERT INTO products_17_lab (product_id, product_name, category, price, stock, note) VALUES (1, '노트', '문구', 3500.00, 20, NULL), (2, '볼펜', '문구', 1500.00, 0, ''), (3, '머그컵', '생활', 8000.00, 12, '선물 포장'), (4, '스탠드', '생활', 25000.00, 5, NULL), (5, '파일', '문구', 1500.00, 50, ''), (6, '케이블', '전자', 8000.00, 0, '호환 확인');
3번 머그컵과 6번 케이블은 8000원, 2번 볼펜과 5번 파일은 1500원입니다. 같은 가격일 때의 순서가 페이지 결과에 영향을 주는 상황을 일부러 만든 것입니다. 기본키 product_id는 각 행마다 다르므로 최종 동점 해소 기준으로 사용할 수 있습니다.
가격 내림차순에 기본키 보조 기준 붙이기
SELECT product_id, product_name, price FROM products_17_lab ORDER BY price DESC, product_id ASC LIMIT 3;
| 반환 순서 | 번호 | 상품 | 가격 |
|---|---|---|---|
| 1 | 4 | 스탠드 | 25000.00 |
| 2 | 3 | 머그컵 | 8000.00 |
| 3 | 6 | 케이블 | 8000.00 |
정렬은 첫 기준 price를 비교하고, 값이 같을 때만 다음 기준 product_id를 비교합니다. product_id 방향을 DESC로 바꾸면 같은 가격의 6번이 3번보다 먼저 옵니다. 다시 ASC로 되돌려 결과를 비교하세요. 가격이 모두 다르다고 가정해 보조 기준을 빼면 이후 데이터가 추가되었을 때 순서가 불안정해질 수 있습니다. ORDER BY 공식 문서에서 여러 정렬식의 적용 순서를 확인할 수 있습니다.
OFFSET 페이지와 그 한계 확인하기
SELECT product_id, price FROM products_17_lab ORDER BY price DESC, product_id ASC LIMIT 2 OFFSET 0; SELECT product_id, price FROM products_17_lab ORDER BY price DESC, product_id ASC LIMIT 2 OFFSET 2;
첫 페이지의 번호는 4·3, 다음 페이지는 6·1입니다. OFFSET은 페이지 번호가 아니라 건너뛸 행 수이며, 페이지 크기가 2라면 세 번째 페이지의 OFFSET은 4입니다. 같은 의미로 LIMIT 2, 2를 쓸 수 있지만 두 인수의 역할이 혼동되면 LIMIT 개수 OFFSET 시작위치 형태를 쓰는 편이 명확합니다.
일관된 보조 정렬을 해도 두 요청 사이에 다른 연결이 행을 추가·삭제하거나 가격을 바꾸면 페이지 경계가 달라질 수 있습니다. OFFSET은 같은 데이터 집합이라는 전제에서 위치를 설명합니다. 매우 큰 OFFSET은 앞부분의 많은 행을 처리하게 될 수 있어 깊은 페이지에서 비용도 검토해야 합니다. LIMIT가 항상 정렬·검색 작업 전체를 그 개수만큼으로 줄인다는 뜻은 아닙니다. LIMIT 공식 문서에서 시작 위치와 개수의 의미를 확인하세요.
마지막 정렬값을 기억하는 조회와 NULL 순서
앞 페이지의 마지막 행이 가격 8000·번호 3인 상황에서는 그 뒤로 올 조건을 정해 조회할 수도 있습니다. 내림차순 가격이 더 작거나, 같은 가격에서 번호가 더 큰 행이 뒤에 옵니다. 이 조건은 현재 데이터와 두 가지 정렬 방향에 맞춘 것이므로 정렬을 바꾸면 함께 바꿔야 합니다.
SELECT product_id, price FROM products_17_lab WHERE price < 8000 OR (price = 8000 AND product_id > 3) ORDER BY price DESC, product_id ASC LIMIT 2;
결과는 번호 6·1로 OFFSET 2의 예제와 같습니다. 이 방식을 키셋 페이지 나누기라고 부르며, 다음 조회를 위한 마지막 값을 보관해야 합니다. 임의의 페이지 번호로 바로 건너뛰는 기능과는 요구 사항이 다릅니다. 동시 수정 중 완전한 고정 목록이 필요하면 스냅샷이나 데이터 정책까지 검토해야 합니다.
SELECT product_id, note FROM products_17_lab ORDER BY note IS NULL ASC, note ASC, product_id ASC;
NULL은 기본 오름차순에서 낮은 값으로 정렬됩니다. 마지막 예제는 note IS NULL이 거짓인 행을 먼저 배치하고, 메모·기본키로 다시 정렬해 NULL을 뒤로 보냅니다. 문자열 사이의 세부 순서는 정렬 규칙에 영향을 받으므로 NULL 위치와 동점 처리에 초점을 맞춰 확인합니다.
예상한 순서가 아닐 때 점검하기
LIMIT를 제거한 상태로 기본키와 모든 정렬 열을 출력해 전체 순서를 먼저 확인합니다. 별칭·정렬 방향·문자열 타입을 차례로 살피고, 동일값에서 고유한 보조 기준이 있는지 확인하세요. 서버의 기본키 인덱스 순서나 이전 실행 결과를 암묵적인 ORDER BY로 취급하지 않습니다.
정렬 기준을 사용자 입력으로 선택하게 하는 프로그램에서는 허용한 열과 방향만 매핑합니다. SQL 값 매개변수로 임의의 열 이름까지 안전하게 치환할 수 있다고 가정하지 않습니다. 페이지 크기도 정수 범위를 정하고, 음수·과도한 요청을 입력 단계에서 처리하는 편이 좋습니다.
다시 실습하려면 SELECT DATABASE();, SHOW CREATE TABLE products_17_lab;와 저장된 행을 확인합니다. 보관할 데이터가 없는 이 글의 연습 테이블이 확실할 때만 아래 문장으로 지운 뒤 준비 코드를 다시 실행합니다. DROP TABLE은 구조와 데이터를 함께 없애며 일반적인 ROLLBACK으로 취소되지 않습니다.
DROP TABLE shopcourse.products_17_lab;










