Database

프로그래머스 상위 n개 레코드 MySQL, Oracle, SQL Server (원하는 만큼의 행의 수를 가져오고 싶을 때 / LIMIT, ROWNUM, TOP )

hihiha2 2023. 11. 14. 18:30

🧷 링크

 

문제 설명

ANIMAL_INS 테이블은 동물 보호소에 들어온 동물의 정보를 담은 테이블입니다. ANIMAL_INS 테이블 구조는 다음과 같으며, ANIMAL_IDANIMAL_TYPEDATETIMEINTAKE_CONDITIONNAMESEX_UPON_INTAKE는 각각 동물의 아이디, 생물 종, 보호 시작일, 보호 시작 시 상태, 이름, 성별 및 중성화 여부를 나타냅니다.

 

동물 보호소에 가장 먼저 들어온 동물의 이름을 조회하는 SQL 문을 작성해주세요.

 

 

 

🙋‍♀️ 내 생각

SQL에는 상위 n개의 레코드만을 세는데 사용하는 키워드가 있다.

그래서 이 키워드를 써야겠다고 생각했다.

그런데 Oracle, SQL Server, MySQL에서 사용하는 키워드가 모두 다르다.

그래서 헷갈리지 않게 잘 정리해둬야한다.

 

내가 주로 사용할 MySQL은 이때 LIMIT을 사용한다.

LIMIT은 위에서 몇개의 레코드를 나타낼 지를 결정하는 것이며, SQL문의 가장 마지막에 쓴다.

 

공부를 위해서 Oracle에서 사용하는 ROWNUM, SQL Server에서 사용하는 TOP으로도 코드를 짜보았다.

 

 

✅  내 코드 1 (My SQL)

SELECT NAME
FROM ANIMAL_INS
ORDER BY DATETIME 
LIMIT 1;

 

우선 SELECT FROM을 통해, ANIMAL_INS에서 NAME을 가져온다.

SELECT NAME

FROM ANIMAL_INS

 

이렇게 하면 ANIMAL_INS안에 있는 모든 NAME을 가져온다.

 

그런데 문제에서는 가장 먼저 들어온 동물의 이름을 조회해아한다.

보호시작일이 가장 먼저인 레코드가 위로 오도록 ORDER BY절을 이용해서 순서를 바꾼다.

SELECT NAME
FROM ANIMAL_INS
ORDER BY DATETIME;

 

DATETIME을 기준으로 정렬하도록 한다.

그러고나서 다시 실행결과를 확인해보면, 순서가 보호시작일이 먼저인 것 순으로 바뀐 것을 확인할 수 있다.

 

 

그런데 여기에서 내가 필요한 레코드는 가장 먼저 들어온 1개만 있으면 되기 때문에 LIMIT1을 해준다.

 

 

🖥 LIMIT (My SQL)

 

- 상위 n개의 레코드만 가져오고 싶을 때

= 원하는 만큼의 행을 가져오고 싶을 때

- LIMIT은 SQL문의 가장 마지막에 적는다.

- LIMIT 숫자 형태로 사용

LIMIT 가져오고 싶은 행의 수;

 

 

LIMIT을 한 전체코드를 보면 아래와 같다.

SELECT NAME
FROM ANIMAL_INS
ORDER BY DATETIME
LIMIT 1;

 

 

이제 실행결과를 확인해보면, 아래와 같이 위에서 단 1개의 행만이 나온다.

(LIMIT을 1로 설정했기 때문)

 

 

 

✅  내 코드2(Oracle)

SELECT NAME
FROM (SELECT NAME
FROM ANIMAL_INS
ORDER BY DATETIME)
WHERE ROWNUM=1;

 

위의 코드는 결과적으로 완성한 코드인데, 이것을 위해서는 우선적으로 오라클의 ROWNUM에 대한 이해가 있어야한다.

그래서 위의 코드를 설명하기 보다는 우선 왜 바로 ROWNUM=1로 처리하면 안되는지 그리고 ROWNUM의 원리에 대해 설명하려고 한다.

 

 ❓ 왜 바로 ROWNUM=1로 처리하면 안되는지❓ 

그냥 편하게 LIMIT만 적어주면 되는 MySQL과는 다르게, Oracle은 조금 더 복잡한 과정을 거쳐야한다.

왜냐하면 ORDER BY의 실행순서때문이다.

 

오라클에서 원하는 행의 수를 지정하는 것은 ROWNUM인데, 행의 수를 지정하고 나서야 ORDER BY가 실행된다.

이런 순서의 문제때문에, 내가 행을 1개로 지정해도 무조건 맨 위의 행을 가져오고 나서 마지막에 ORDER BY가 실행된다.

그래서 단순히 LIMIT대신에 ORDER BY를 사용하면 결과가 아래와 같이 나온다.

 

이것을 코드로 나타내면 아래와 같다.

SELECT NAME
FROM ANIMAL_INS
WHERE ROWNUM=1
ORDER BY DATETIME;

 

 

 ❓  ROWNUM의 원리❓ 

🖥 ROWNUM (Oracle)

- 조회된 행이 몇번째인지 확인하고 싶을 때

- ORDER BY절과 사용시 실행순서: 1️⃣ ROWNUM ➡️ 2️⃣ ORDER BY

- 원하는 행의 수를 구하기 위해서 : 서브쿼리 사용으로 해결! 

 

기본적으로 ROWNUM의 역할은 조회된 행이 몇번째인지를 알려주는 것이다.

SELECT NAME, ROWNUM
FROM ANIMAL_INS
WHERE ROWNUM=10
ORDER BY DATETIME;

위와 같이 ROWNUM이 테이블에 보이도록 코드를 짜면 더 쉽게 이해할 수 있다.

 

결과를 확인해보면, 아래와 같이 임의의 숫자가 테이블에 적용된 것이 보인다.

오라클에서 ROWNUM은 ⭐️ORCER BY가 실행되기 전에 먼저 적용⭐️되며, 이후에 ORDER BY를 한다고 하더라도 이미 ROWNUM=1은 Sugar로 고정되어 있는 상태이다.

 

따라서 위의 코드에서 OREDER BY를 DATETIME으로 하고 ROWNUM=1을 한다고 하더라도 Sugar가 추출된다.

(엄밀히 말하면 ROWNUM은 행에 임의의 번호를 먹여주는 것이고, 원하는 행의 수를 제한하는 MySQL의 LIMIT과는 다른것같다.)

 

 

그래서 이러한 문제를 해결하기 위해서 서브쿼리를 사용한다.

 

🖥 서브쿼리

- 다른 SQL문 내에 포함된 독립적인 SELECT문

- WHERE절, FROM절, HAVING절, SELECT절 어디에서든 사용 가능

 

 

ROWNUM을 적용하기 이전에, 먼저 ORDER BY가 적용되어야한다.

그래서 데이터를 가져오는 FROM절에 서브쿼리로 ORDER BY우선적으로 하도록한다.

 

일단은 내가 원하는 것은 DATETIME에 따른 정렬이다.

우선 이것만 하면 아래의 코드와 같다.

SELECT NAME
FROM ANIMAL_INS
ORDER BY DATETIME;

이렇게하고 결과를 확인해보면, 데이터가 보호시작일을 기준으로 order by 된것을 확인할 수 있다.

 

 

그러면 이렇게 보호시작일을 기준으로 정렬된 것을 쿼리안에 넣어서 서브쿼리로 이용한다.

FROM절에 넣어서 이 데이터를 기준으로 ROWNUM을 적용하도록 한다.

 

SELECT ROWNUM, NAME
FROM (SELECT NAME
FROM ANIMAL_INS
ORDER BY DATETIME);

 

(SELECT문에 ROWNUM은 적을 필요가 없지만, 우선 값을 확인하기 쉽도록 하기 위해서 넣었다. 최종적으로는 뺄것이다.)

 

이렇게하고 결과를 확인해보면 아래와 같이 보호시작일을 기준으로 정렬되었고, 위에서 부터 ROWNUM이 적용된 것을 볼 수 있다.

 

이제 마지막으로 ROWNUM=1인 값 하나만을 남기도록 한다.

SELECT NAME
FROM (SELECT NAME
FROM ANIMAL_INS
ORDER BY DATETIME)
WHERE ROWNUM=1;

(위에서 확인을 위해 넣었던 SELECT문의 ROWNUM은 다시 제거했다.)

 

이렇게 하고 결과를 확인해보면, 원하는 값이 출력되는 것을 볼 수 있다.

 

 

✅  내 코드3(SQL Server)

SELECT TOP(1) NAME
FROM ANIMAL_INS
ORDER BY DATETIME;

SQL Server에서도 원하는 만큼의 행을 가져오고 싶을 때, 간단하게 가져올 수 있다

( 오라클만 복잡한듯🥲 )

 

우선 ANIMAL_INS에서 NAME을 가져오기 위해서 아래와 같이 적는다.

SELECT NAME

FROM ANIMAL_INS

 

이렇게 하면 모든 동물들의 이름이 나올 것이다.

 

다음으로 보호시작일을 기준으로 정렬해야 하기 때문에 ORDER BY를 사용한다.

SELECT NAME
FROM ANIMAL_INS
ORDER BY DATETIME

이렇게하면, 시작일을 기준으로 정렬된다.

 

그러고나서 마지막으로 맨 위의 1개의 행만을 가져오기 위해서 TOP()을 사용한다.

 

🖥 TOP(SQL Server)

- 상위 n개의 레코드만 가져오고 싶을 때

= 원하는 만큼의 행을 가져오고 싶을 때

- SELCET문안에 적는다

- TOP(숫자) 형태로 사용

 

맨 위의 1개의 행만을 가져오고 싶기때문에 TOP(1)을 SELECT문안에 적는다

 

 

 

 

🙋‍♀️ 내 생각

원하는 만큼의 행의 수를 가져오기 위해서 사용하는 키워드에 대해 공부했다.

MySQL과 SQL Server는 사실 엄청 간단한데, Oracle에서의 사용이 조금 번거롭다.

그래서 이 차이에 대해 정확하게 정리하고 이해해야겠다고 생각해서 정리해보았다.

 

📝 원하는 만큼의 행을 가져올 때 사용 키워드

  MySQL Oracle SQL Server
키워드 LIMT ROWNUM TOP
위치 SQL문 맨 마지막  WHERE절 SELCET문 맨 앞
ORDER BY와 실행순서 ORDER BY절 뒤에 수행 ORDER BY절 앞에 수행 ORDER BY절 뒤에 수행
예시 LIMIT 5 WHERE ROWNUM=5 SELECT TOP(5)

 

표로 정리하면 위와 같다.

 

여기서 주의할 점은

오라클의 ROWNUM은 엄밀히 말하면 원하는 만큼의 행을 가져오는 키워드가 아니고, 행에 임의의 번호를 매기고 그것을 이용하는 것이라는 점이다. 그래서 특별히 오라클에서 사용하는 ROWNUM만 주의하면 될 것 같다.

 

사실 그렇게 어려운 부분은 아닌데, ROWNUM의 개념자체를 원하는 만큼 행의 수를 가져온다고 잘못 생각했기때문에 이해가 더 어려웠던 것은 아닐까 생각한다. 아예 개념 자체를 저렇게 이해하면 서브쿼리를 통해서 해결하는 부분도 더 쉽게 이해된다.