![[Screenshot 2024-08-23 at 08.53.10.png]] > 속성의 분류 [[HDATALAB_SQLD_요약노트.pdf#page=10&selection=12,2,12,8|HDATALAB_SQLD_요약노트, page 10]] 속성의 특성에 따른 분류 - 기본속성 - 설계속성 - 파생속성 엔터티 구성방식에 따른 분류 - PK - FK - 일반속성 분해 여부에 따른 속성 - 단일속성 - 복합속성: 주소 - 다중값 속성: 이윤우, 배한나 --- ![[Screenshot 2024-08-23 at 09.02.34.png]] 일관성과 지속성이 헷갈린다. - 원자성: 모두 성공 또는 실패 - 고립성: 트랙잭션 단계에서 다른 트랜잭션 영향 없음 - 지속성은 트랜잭션 성공 후 영속됨 - 일관성은 트랜잭션 전 후의 데이터가 일관적이다는 뜻 --- ![[Screenshot 2024-08-23 at 09.04.48.png]] ③ NULL로만 구성된 컬럼을 COUNT한 결과는 공집합이다. -> 0으로 표시된다. ##### 설명 SQL에서 `COUNT` 함수는 NULL 값을 제외하고 계산하는데, `COUNT(column_name)`을 사용할 경우 그 컬럼에서 NULL이 아닌 값의 수를 세게 됩니다. 만약 컬럼이 NULL로만 구성되어 있다면 `COUNT(column_name)`의 결과는 0이 됩니다. 따라서 NULL로만 구성된 컬럼을 COUNT하면 공집합이 아니라 0이 반환됩니다. 공집합이라는 표현은 일반적으로 결과가 없는 상황을 의미하는데, SQL에서 `COUNT`의 결과는 공집합이 아니라 정수형으로 나타납니다. --- ![[Screenshot 2024-08-23 at 09.07.07.png]] WHERE 절은 SELECT 절보다 먼저 수행되므로 SELECT 절에서 정의한 컬럼 별칭을 사용할 수 없다. --- ![[Screenshot 2024-08-23 at 09.08.20.png]] 이 문제에서 제시된 각각의 SQL 함수에 대해 설명하겠습니다. ### 1. `SUBSTR('WWW.HDATALAB.CO.KR', -5)` - **설명:** `SUBSTR` 함수는 문자열의 일부를 추출하는 함수입니다. 첫 번째 인자는 문자열, 두 번째 인자는 시작 위치입니다. 여기서 음수 값은 문자열의 끝에서부터의 위치를 의미합니다. - **예시:** `SUBSTR('WWW.HDATALAB.CO.KR', -5)`는 문자열 'WWW.HDATALAB.CO.KR'에서 끝에서 5번째 위치부터 끝까지의 문자열을 추출하므로 결과는 `'CO.KR'`이 됩니다. ### 2. `LPAD('X', 5, 'X')` - **설명:** `LPAD` 함수는 문자열의 왼쪽에 지정된 문자로 채워 특정 길이를 만드는 함수입니다. 첫 번째 인자는 원본 문자열, 두 번째 인자는 결과 문자열의 길이, 세 번째 인자는 채워질 문자입니다. - **예시:** `LPAD('X', 5, 'X')`는 문자열 'X'의 왼쪽에 'X'를 추가하여 길이가 5가 되도록 합니다. 하지만 제시된 답안에서는 `XXXXXX`로 되어 있어, 길이가 6인 잘못된 결과가 나왔습니다. 올바른 결과는 `'XXXXX'`입니다. ### 3. `INSTR('WWW.HDATALAB.CO.KR', '.', 5, 2)` - **설명:** `INSTR` 함수는 특정 문자열이 원본 문자열 내에서 처음으로 나타나는 위치를 반환합니다. 세 번째 인자는 검색을 시작할 위치, 네 번째 인자는 몇 번째로 일치하는 문자열을 찾을지를 지정합니다. - **예시:** `INSTR('WWW.HDATALAB.CO.KR', '.', 5, 2)`는 5번째 위치부터 시작해서 두 번째로 나타나는 '.'의 위치를 반환합니다. 이 경우, 두 번째 '.'은 `HDATALAB` 다음에 나오는 위치로, 결과는 16입니다. ### 4. `LTRIM('AABABAA', 'A')` - **설명:** `LTRIM` 함수는 문자열의 왼쪽에서 지정된 문자를 제거하는 함수입니다. 첫 번째 인자는 원본 문자열, 두 번째 인자는 제거할 문자들입니다. - **예시:** `LTRIM('AABABAA', 'A')`는 문자열의 왼쪽에서 'A'를 모두 제거합니다. 그래서 결과는 `'BABAA'`가 됩니다. ### 정답 이 문제에서 틀린 결과는 **②**입니다. `LPAD('X', 5, 'X')`의 결과는 `'XXXXX'`이어야 하는데, 문제에 제시된 결과는 `'XXXXXX'`로 잘못된 답이 제시되었습니다. -> INSTR 함수 외우기 --- ![[Screenshot 2024-08-23 at 09.14.54.png]] 이 문제에서 주어진 각 함수는 모두 NULL 값을 처리하는 함수들입니다. 이 함수들의 핵심 개념과 예시 데이터를 함께 설명하겠습니다. ### 1. `NVL(COMM, 100)` - **설명:** `NVL` 함수는 첫 번째 인자가 NULL인 경우 두 번째 인자를 반환하고, 첫 번째 인자가 NULL이 아닌 경우 그 값을 반환합니다. 이 함수는 Oracle에서 사용됩니다. - **예시:** `COMM` 컬럼의 값이 NULL이면 `100`을 반환하고, 값이 있으면 그 값을 반환합니다. 예를 들어, `COMM`이 `NULL`이면 `100`, `COMM`이 `500`이면 `500`을 반환합니다. ### 2. `NULLIF(COMM, 100)` - **설명:** `NULLIF` 함수는 두 인자가 같으면 NULL을 반환하고, 그렇지 않으면 첫 번째 인자를 반환합니다. - **예시:** `COMM`이 `100`이면 NULL을 반환하고, 그렇지 않으면 `COMM`의 값을 그대로 반환합니다. 예를 들어, `COMM`이 `500`이면 `500`을 반환하고, `COMM`이 `100`이면 `NULL`을 반환합니다. ### 3. `COALESCE(COMM, 100)` - **설명:** `COALESCE` 함수는 인자들 중에서 첫 번째로 NULL이 아닌 값을 반환합니다. 여러 인자를 받을 수 있으며, 그 중 첫 번째로 NULL이 아닌 값을 반환합니다. - **예시:** `COMM`이 NULL이면 `100`을 반환하고, NULL이 아니면 그 값을 반환합니다. `COMM`이 `NULL`이면 `100`, `COMM`이 `500`이면 `500`을 반환합니다. 기본적으로 `NVL`과 비슷하게 작동합니다. ### 4. `ISNULL(COMM, 100)` - **설명:** `ISNULL` 함수는 `NVL`과 유사하게 동작하며, 주로 SQL Server에서 사용됩니다. 첫 번째 인자가 NULL이면 두 번째 인자를 반환하고, 첫 번째 인자가 NULL이 아니면 그 값을 반환합니다. - **예시:** `COMM`이 NULL이면 `100`을 반환하고, NULL이 아니면 그 값을 반환합니다. `COMM`이 `NULL`이면 `100`, `COMM`이 `500`이면 `500`을 반환합니다. ### 결론: 문제에서 `COMM` 컬럼의 값이 `NULL`인 경우, `NVL`, `COALESCE`, `ISNULL` 함수는 모두 `100`을 반환하지만, `NULLIF` 함수는 다릅니다. `NULLIF(COMM, 100)`는 두 인자가 같을 때 NULL을 반환하므로 다른 동작을 합니다. **정답은 ②번 NULLIF(COMM, 100)** 입니다. 이 함수는 나머지 함수들과 달리 NULL과 비교하는 방식으로 동작하여, 같은 값일 경우 NULL을 반환하게 됩니다. #### 암기 NVL = COALESCE = ISNULL --- ![[Screenshot 2024-08-23 at 09.33.10.png]] 이 문제는 Oracle DBMS에서 날짜와 시간을 다루는 SQL 쿼리와 관련된 문제입니다. 쿼리의 결과를 해석하기 위해 필요한 개념들을 설명하고, 예시를 통해 이해를 돕겠습니다. ### 1. SQL 쿼리 분석 주어진 SQL 쿼리는 다음과 같습니다: ```sql SELECT TO_CHAR(TO_DATE('2024/08/24 10:00', 'YYYY/MM/DD HH24:MI') - 30/24/60, 'YYYY.MM.DD HH24:MI:SS') FROM DUAL; ``` 이 쿼리는 `TO_DATE`와 `TO_CHAR` 함수, 그리고 날짜 연산을 포함하고 있습니다. #### 1.1. `TO_DATE` 함수 - `TO_DATE` 함수는 문자열을 날짜로 변환하는 함수입니다. - 여기서 `'2024/08/24 10:00'`이라는 문자열을 `'YYYY/MM/DD HH24:MI'` 형식에 맞춰 날짜와 시간으로 변환합니다. - 결과는 `2024년 8월 24일 10시 00분`이라는 날짜와 시간이 됩니다. #### 1.2. 날짜 연산 - 날짜에서 특정 값을 빼는 연산이 가능합니다. - `30/24/60`은 **30초**를 나타냅니다. 이는 30을 24(시간)로 나누고 다시 60(분)으로 나누어 **일(day)** 단위로 계산됩니다. - 30초 = 0.00034722일 #### 1.3. `TO_CHAR` 함수 - `TO_CHAR` 함수는 날짜 데이터를 문자열로 변환할 때 사용합니다. - 변환된 날짜 데이터를 `'YYYY.MM.DD HH24:MI:SS'` 형식으로 출력합니다. ### 2. 문제의 쿼리 실행 결과 쿼리에서는 `2024/08/24 10:00`에서 30초를 빼고 그 결과를 `'YYYY.MM.DD HH24:MI:SS'` 형식으로 출력합니다. - **기준 시간**: 2024년 8월 24일 10시 00분 00초 - **계산**: 30초를 빼면 9시 59분 30초가 됩니다. 따라서, 가장 적절한 답은 `④ 2024.08.24 09:59:30`입니다. ### 3. 예시 데이터로 이해하기 간단한 예시를 들어 이해를 돕겠습니다. ```sql SELECT TO_CHAR(TO_DATE('2024/12/31 23:59', 'YYYY/MM/DD HH24:MI') - 60/24/60, 'YYYY.MM.DD HH24:MI:SS') FROM DUAL; ``` 이 쿼리는 `2024년 12월 31일 23시 59분`에서 **60초(1분)**를 빼는 계산입니다. - **기준 시간**: 2024년 12월 31일 23시 59분 00초 - **계산**: 60초를 빼면 23시 58분 00초가 됩니다. - **결과**: `'2024.12.31 23:58:00'`이 됩니다. 이처럼 날짜와 시간의 계산은 정확한 연산을 통해 이루어지며, 결과는 항상 지정된 형식으로 출력됩니다. 이해를 돕기 위해, 한 줄씩 천천히 설명하고, 다른 예시도 함께 들어 보겠습니다. ### 1. `30/24/60`이 30초가 되는 이유 - **날짜와 시간 단위**: Oracle에서는 날짜와 시간을 계산할 때 기본 단위가 **일(day)**입니다. 그래서 시간이나 분, 초 단위의 값을 일 단위로 변환하여 연산하게 됩니다. #### 1.1. `30` (초) - 여기서 `30`은 **초(second)**를 의미합니다. #### 1.2. `24` (시간) - 하루는 24시간이므로 `24`로 나눔으로써 시간을 일 단위로 변환합니다. 하지만 아직 우리는 초(second) 단위로 남아 있기 때문에, 더 변환이 필요합니다. #### 1.3. `60` (분) - 1시간은 60분입니다. 따라서 1초를 일(day) 단위로 표현하기 위해서는 `24시간 × 60분 = 1440분`으로 나눠야 합니다. 이를 통해 초를 일 단위로 변환할 수 있습니다. ### 최종 계산 ```plaintext 30 / 24 / 60 = 30초를 일(day) 단위로 변환 ``` 이 계산은 30초를 일 단위로 표현하는 방식입니다. 따라서 `TO_DATE('2024/08/24 10:00', 'YYYY/MM/DD HH24:MI') - 30/24/60`는 `2024년 8월 24일 10시 00분`에서 30초를 뺀 값이 됩니다. ### 2. 다른 예시로 이해하기 #### 예시 1: 1시간 빼기 ```sql SELECT TO_CHAR(TO_DATE('2024/08/24 10:00', 'YYYY/MM/DD HH24:MI') - 1/24, 'YYYY.MM.DD HH24:MI:SS') FROM DUAL; ``` - **1/24**: 1시간을 일(day) 단위로 변환한 것입니다. - **결과**: `2024/08/24 09:00:00`이 됩니다. 10시에서 1시간을 빼면 9시가 됩니다. #### 예시 2: 45분 빼기 ```sql SELECT TO_CHAR(TO_DATE('2024/08/24 10:00', 'YYYY/MM/DD HH24:MI') - 45/24/60, 'YYYY.MM.DD HH24:MI:SS') FROM DUAL; ``` - **45/24/60**: 45분을 일(day) 단위로 변환한 것입니다. - **결과**: `2024/08/24 09:15:00`이 됩니다. 10시에서 45분을 빼면 9시 15분이 됩니다. ### 3. 한 줄씩 설명 다시 원래 문제로 돌아가서 한 줄씩 살펴보겠습니다. - `TO_DATE('2024/08/24 10:00', 'YYYY/MM/DD HH24:MI')` → 이 부분은 날짜와 시간을 Oracle의 날짜 형식으로 변환합니다. - `- 30/24/60` → 여기서 30초를 일(day) 단위로 변환한 후, 기준 시간에서 뺍니다. - `'YYYY.MM.DD HH24:MI:SS'` → 결과 날짜와 시간을 지정된 형식으로 출력합니다. 결과적으로, `2024년 8월 24일 10시 00분`에서 30초를 빼면 **2024.08.24 09:59:30**이 됩니다. 이렇게 날짜 연산은 시간, 분, 초를 일(day) 단위로 변환하여 계산하는 방식으로 동작합니다. 네, 맞습니다. `30/24/60`은 **30을 24로 나눈 후, 그 결과를 다시 60으로 나누는** 계산입니다. 이 과정을 좀 더 천천히 설명해 볼게요. ### 1. 수식 `30/24/60`의 의미 이 수식은 단계적으로 다음과 같이 해석됩니다: 1. **30**: 초(second) 단위의 숫자입니다. 이 숫자는 30초를 나타냅니다. 2. **24**: 하루는 24시간이므로, 시간 단위를 일(day) 단위로 변환하기 위해 사용됩니다. 3. **60**: 1시간은 60분이므로, 초(second) 단위를 일(day) 단위로 변환하기 위해 사용됩니다. ### 2. 수식을 단계별로 계산 #### 2.1. 첫 번째 나누기: `30/24` - **30초**를 **24**로 나누면, **1시간의 1/48**을 의미하는 값이 됩니다. - 30초를 24로 나눈 결과는 시간 단위로 1/48에 해당합니다. #### 2.2. 두 번째 나누기: `(30/24)/60` - 이제 `(30/24)`를 **60**으로 나누면, **1분의 1/2880**이라는 값이 됩니다. - 이 최종 결과는 **하루의 1/2880**을 의미하며, 이는 30초에 해당하는 일(day) 단위입니다. ### 3. 결과적으로 `30/24/60`이 30초를 일(day) 단위로 변환 Oracle에서 날짜 연산은 기본적으로 **일(day)** 단위로 이루어지므로, 초(second)를 일(day) 단위로 변환하여 날짜 계산에 활용할 수 있습니다. - `30/24/60 = 0.00034722`일 - 이 값은 30초를 일(day)로 환산한 값이며, 이를 기준 시간에서 빼는 연산이 이루어집니다. ### 4. 전체 과정 다시 이해하기 - `TO_DATE('2024/08/24 10:00', 'YYYY/MM/DD HH24:MI')`: 기준 시간은 2024년 8월 24일 10시 00분입니다. - `- 30/24/60`: 이 값은 30초를 일(day) 단위로 변환한 값이며, 기준 시간에서 30초를 빼는 연산을 수행합니다. - 결과는 `2024년 8월 24일 9시 59분 30초`가 됩니다. 이제 `30/24/60`이 30초를 나타내는 방식과 그 이유가 이해가 되셨길 바랍니다. 만약 여전히 궁금한 점이 있다면, 추가로 설명드리겠습니다. --- ![[Screenshot 2024-08-23 at 09.37.51.png]] 이 문제는 SQL 쿼리의 결과가 어떻게 다른지를 파악하는 문제입니다. 각 쿼리의 주요 차이점은 `COUNT`와 `SUM` 함수의 사용 및 `NVL` 함수의 사용입니다. 각 쿼리의 기능을 살펴보고, 어떤 쿼리가 다른 결과를 출력할지 분석해 보겠습니다. ### 1. 각 쿼리의 기능 설명 #### ① `SELECT` 문 ```sql SELECT JOB, COUNT(CASE WHEN DEPTNO = 10 THEN 1 ELSE 0 END) AS "10번부서원수", COUNT(CASE WHEN DEPTNO = 20 THEN 1 ELSE 0 END) AS "20번부서원수", COUNT(CASE WHEN DEPTNO = 30 THEN 1 ELSE 0 END) AS "30번부서원수" FROM EMP GROUP BY JOB; ``` - 이 쿼리는 각 `JOB`에 대해 `DEPTNO`가 10, 20, 30인 경우의 수를 계산합니다. - `COUNT` 함수는 조건에 맞는 값의 개수를 셉니다. 하지만 여기서 `ELSE 0`이 들어가므로, 결과적으로 부서 번호에 해당하지 않는 레코드도 계산됩니다. 이 경우, 각 `JOB` 그룹에 속한 모든 레코드에 대해 0이 아닌 값의 개수(=전체 레코드 수)를 셀 수 있습니다. #### ② `SELECT` 문 ```sql SELECT JOB, COUNT(CASE WHEN DEPTNO = 10 THEN 1 END) AS "10번부서원수", COUNT(CASE WHEN DEPTNO = 20 THEN 1 END) AS "20번부서원수", COUNT(CASE WHEN DEPTNO = 30 THEN 1 END) AS "30번부서원수" FROM EMP GROUP BY JOB; ``` - 이 쿼리는 각 `JOB`에 대해 `DEPTNO`가 10, 20, 30인 경우의 수를 계산합니다. - `COUNT` 함수는 `CASE`가 `THEN 1`인 경우에만 개수를 셉니다. `END` 이후의 값이 NULL이면 `COUNT`에 포함되지 않으므로, 특정 부서 번호에 속하지 않는 레코드는 무시됩니다. 따라서 실제로 조건에 맞는 레코드만 셉니다. #### ③ `SELECT` 문 ```sql SELECT JOB, SUM(CASE WHEN DEPTNO = 10 THEN 1 ELSE 0 END) AS "10번부서원수", SUM(CASE WHEN DEPTNO = 20 THEN 1 ELSE 0 END) AS "20번부서원수", SUM(CASE WHEN DEPTNO = 30 THEN 1 ELSE 0 END) AS "30번부서원수" FROM EMP GROUP BY JOB; ``` - 이 쿼리는 `SUM` 함수를 사용해 각 `JOB`에 대해 `DEPTNO`가 10, 20, 30인 경우의 합계를 계산합니다. - `SUM` 함수는 `CASE`가 참인 경우 `1`을, 거짓인 경우 `0`을 합산하므로, 실제로 각 부서에 속한 레코드 수를 정확하게 반환합니다. `COUNT`와 달리 모든 값을 더해 정확한 부서원 수를 계산할 수 있습니다. #### ④ `SELECT` 문 ```sql SELECT JOB, NVL(SUM(CASE WHEN DEPTNO = 10 THEN 1 END),0) AS "10번부서원수", NVL(SUM(CASE WHEN DEPTNO = 20 THEN 1 END),0) AS "20번부서원수", NVL(SUM(CASE WHEN DEPTNO = 30 THEN 1 END),0) AS "30번부서원수" FROM EMP GROUP BY JOB; ``` - 이 쿼리는 `③`의 쿼리와 유사하지만, `NVL` 함수를 사용하여 결과가 NULL일 경우 0으로 반환합니다. - `NVL`은 NULL 값을 0으로 바꿔주기 때문에, 해당 부서가 존재하지 않더라도 0을 반환합니다. - `SUM` 함수는 부서원 수를 계산하고, `NVL`을 통해 NULL이 아닌 값을 보장합니다. ### 2. 정답 분석 - `①`의 `COUNT` 함수는 `ELSE 0`을 포함하기 때문에, 각 부서에 대해 잘못된 결과(모든 레코드 수를 셈)가 나올 수 있습니다. - `②`는 `ELSE` 부분이 없어 NULL을 무시하고, 조건이 참인 레코드만 정확하게 셉니다. - `③`은 `SUM`을 사용하여 각 부서에 속하는 인원을 정확하게 합산합니다. - `④`는 `③`과 동일하나, NULL을 0으로 바꿔주기 위해 `NVL`을 사용합니다. ### **결과적으로 ①번 쿼리의 결과가 다릅니다**. - 이 쿼리는 잘못된 레코드 수를 계산할 수 있습니다. 따라서 `①`이 다른 결과를 반환하는 유일한 쿼리입니다. #### 핵심 -> Count는 null인 경우 세지 않는다. 1번 보기는 null이 아니라 0이 입력되므로, 무조건 다 0도 세버린다. 그런데 2번 보기는 else 값이 없어서 null이 반환되고, count는 null을 세지 않아서 조건에 맞는 값만 반환된다는 것. --- ![[Screenshot 2024-08-23 at 09.43.07.png]] 연산자는 NOT > AND > OR 순서대로의 우선순위를 가지고 있다. 따라서 1, 3, 4 번의 경우 DEPTNO = 20 AND JOB = 'CLERK' 조건이 먼저 실행되고, 이 결과에 DEPTNO = 10 조건에 만족하는 집합을 합하여 리턴되므로 2 번과의 결과가 달라진다. --- ![[Screenshot 2024-08-23 at 09.51.29.png]] SUM(COL2)는 NULL 만 제외하고 총합을 리턴하므로 500 이 출력된다. 하지만 2 번 지문의 경우 COL1 > 0 조건에 만족하는 행은 첫 번째 행 하나이므로 SUM(COL2) + SUM(COL3) = NULL + 100 이 되며, 이때는 NULL 이 리턴된다. --- ![[Screenshot 2024-08-23 at 09.53.20.png]] 이 문제는 `GROUP BY` 절에 대한 설명 중 틀린 것을 찾는 문제입니다. 각 선택지의 내용을 예시 데이터와 함께 설명해드리겠습니다. ### 1. 선택지 분석 #### ① `GROUP BY`절에는 컬럼 별칭을 사용할 수 없다. - **설명**: `GROUP BY` 절에서 컬럼의 별칭을 사용하는 것은 불가능합니다. 예를 들어, `SELECT` 절에서 `AS` 키워드를 사용해 컬럼에 별칭을 부여해도, `GROUP BY`에서는 그 별칭을 사용할 수 없습니다. - **예시**: ```sql SELECT ENAME AS EMPLOYEE_NAME FROM EMP GROUP BY EMPLOYEE_NAME; -- 잘못된 SQL 구문 ``` 이 예시에서는 `EMPLOYEE_NAME`이라는 별칭을 `GROUP BY` 절에서 사용할 수 없습니다. 올바르게 작성하려면 원래 컬럼 이름인 `ENAME`을 사용해야 합니다. - **올바른 구문**: ```sql SELECT ENAME AS EMPLOYEE_NAME FROM EMP GROUP BY ENAME; ``` #### ② `GROUP BY`절에는 `SUM`, `COUNT` 함수를 사용할 수 없다. - **설명**: `GROUP BY` 절에서는 집계 함수인 `SUM`, `COUNT` 등을 사용할 수 **없습니다**. 이러한 함수는 `SELECT` 절에서 사용됩니다. - **예시**: ```sql SELECT DEPTNO, SUM(SAL) FROM EMP GROUP BY DEPTNO; ``` 이 예시에서 `SUM` 함수는 `SELECT` 절에 포함되어 있으며, `GROUP BY` 절에는 오직 그룹핑할 컬럼만 명시합니다. - 따라서 이 선택지는 **맞는 설명**입니다. #### ③ `GROUP BY`절에 명시되지 않은 컬럼을 그룹함수 없이 `SELECT`절에 사용할 수 없다. - **설명**: `GROUP BY` 절에 명시되지 않은 컬럼은 `SELECT` 절에서 그룹 함수 없이 사용할 수 없습니다. `GROUP BY` 절은 데이터를 특정 컬럼으로 그룹핑하기 때문에, 그룹핑되지 않은 컬럼을 그룹 함수 없이 사용하는 것은 의미가 없습니다. - **예시**: ```sql SELECT DEPTNO, ENAME FROM EMP GROUP BY DEPTNO; ``` 이 예시는 오류를 발생시킵니다. `ENAME`은 `GROUP BY` 절에 없으므로, 그룹 함수 없이 `SELECT`에서 사용할 수 없습니다. - **올바른 구문**: ```sql SELECT DEPTNO, MAX(ENAME) FROM EMP GROUP BY DEPTNO; ``` 여기서 `MAX` 함수와 같은 그룹 함수를 사용하여 `ENAME`을 사용할 수 있습니다. #### ④ `GROUP BY`절에 나열되는 컬럼 순서에 따라 `SELECT`절의 그룹 함수의 연산 결과가 달라질 수 있다. - **설명**: `GROUP BY` 절의 컬럼 순서는 결과의 **그룹핑 방식**을 결정하지만, **그룹 함수의 연산 결과에는 영향을 미치지 않습니다**. 예를 들어, `GROUP BY`에서 컬럼의 순서를 바꾸더라도, `SUM`, `COUNT` 등의 결과는 동일한 값을 반환합니다. - **예시**: ```sql SELECT DEPTNO, JOB, SUM(SAL) FROM EMP GROUP BY DEPTNO, JOB; ``` 이 예시에서 `GROUP BY DEPTNO, JOB`과 `GROUP BY JOB, DEPTNO`는 다른 그룹핑을 제공할 수 있지만, 그룹 함수 `SUM(SAL)`의 결과는 동일하게 적용된 그룹 내에서 합산된 값을 반환합니다. - **결과**: 이 선택지는 **틀린 설명**입니다. `GROUP BY` 절의 컬럼 순서는 그룹핑 방식에만 영향을 미치며, 그룹 함수의 연산 결과에는 영향을 주지 않습니다. ### 2. 정답 위 설명을 통해, **④번이 틀린 설명**임을 알 수 있습니다. `GROUP BY` 절의 컬럼 순서는 그룹핑에만 영향을 미치며, 그룹 함수의 연산 결과에는 영향을 주지 않습니다. --- 버리는 문제 ![[Screenshot 2024-08-23 at 09.58.46.png]] 첫번재 쿼리. 그룹바이는 신경쓸 거 없고, Col1=200인 값이 없기 때문에, 이걸 Count 하라고 하면, null이 나와야 하지 않을까? 아, 공집합이라고 뜬다고 하는구나. 공집합은 칼럼명만 뜬다. 두번째 쿼리. 더하기인데, Col1=100인 칼럼에서 COL2를 더하라는데, null 값이다. 그럼, null은 칼럼명만 뜨는 게 아니라, 칼럼명에 아무값도 없는 상태가 된다. 세번째는 Col1을 카운트 하는데, Col3=400인 곳, 그건 null값이다. 그런데 Col1을 카운트 하라고 하니, 칼럼으로 카운트 할 때 null값은 카운트하게 되어 있던가. null을 카운트 하면, 0이 된다. 그래, Sum은 null + 숫자면 null인데, 카운트 null은 0이다. 아.. 이 문제 맞힐 자신이 없다. 틀리자. --- ![[Screenshot 2024-08-23 at 10.07.43.png]] 이 문제는 `ORDER BY` 절이 어떻게 작동하는지와 각 쿼리의 구문이 올바른지 여부를 묻고 있습니다. 각각의 쿼리를 하나씩 분석해 보겠습니다. ### 1. 각 쿼리 분석 #### ① `SELECT COL1, COL2, COL3 C1 FROM TAB1 T ORDER BY 1, COL2, COL3;` - **`ORDER BY 1`**: `1`은 `SELECT` 절의 첫 번째 열(`COL1`)을 의미합니다. 즉, `COL1`을 기준으로 정렬합니다. - **나머지**: `COL2`와 `COL3`을 차례대로 정렬합니다. - **구문**: 올바르게 작성된 쿼리입니다. #### ② `SELECT COL1, COL2, COL3 C1 FROM TAB1 T ORDER BY 1, 2, 3;` - **`ORDER BY 1, 2, 3`**: 각각 `SELECT` 절의 첫 번째(`COL1`), 두 번째(`COL2`), 세 번째(`COL3 C1`) 열을 기준으로 정렬합니다. - **구문**: 올바르게 작성된 쿼리입니다. #### ③ `SELECT COL1, COL2, COL3 C1 FROM TAB1 T ORDER BY 1, COL2, C1;` - **`ORDER BY 1`**: `1`은 첫 번째 열(`COL1`)을 의미합니다. - **`C1`**: `COL3`에 붙인 별칭 `C1`을 기준으로 정렬하려는 의도입니다. - **구문**: 이 쿼리도 올바르게 작성된 쿼리입니다. 별칭 `C1`을 `ORDER BY`에서 사용할 수 있습니다. #### ④ `SELECT COL1, COL2, COL3 C1 FROM TAB1 T ORDER BY COL1, COL1, T.C1;` - **`ORDER BY COL1, COL1`**: 두 번의 동일한 컬럼 정렬은 허용됩니다. - **`T.C1`**: 여기서 문제가 발생합니다. `C1`은 `COL3`의 별칭이므로 `T.C1`은 별칭을 테이블에 접두어로 붙여 사용할 수 없습니다. - 별칭 `C1`을 사용할 때는 테이블 접두어 없이 사용해야 합니다. - 이 구문은 올바르지 않습니다. ### 2. 결론 **`④번 쿼리`**가 **실행이 불가능**한 구문입니다. 별칭을 사용할 때 테이블 명을 앞에 붙이는 것은 허용되지 않기 때문에 이 구문은 올바르지 않습니다. --- ![[Screenshot 2024-08-23 at 10.08.57.png]]TO_CHAR에 의해 SAL 값이 문자로 변환되며, 문자 값의 대소 비교는 값의 왼쪽부터 비교하므로 왼쪽 값이 가장 작 은 값의 크기가 제일 작다. 따라서 1300 < 2500 < 300 < 800 순이 되며, 오름차순 정렬은 1300, 2500, 300, 800 순서 로 배치된다. --- ![[Screenshot 2024-08-23 at 10.10.49.png]] <> 이 함수에 대해서 이해해봐야 함. 다시 볼 문제 --- ![[Screenshot 2024-08-23 at 10.16.27.png]] 2개가 아니라 3개다. F% 값이 2번 출력. 행이 두개로. --- ### 반드시 복습하기 ![[Screenshot 2024-08-23 at 10.20.27.png]] 아래는 각 조인별로 `TAB1`과 `TAB2`의 결과를 정리한 표입니다. ### 1. INNER JOIN ```plaintext | COL1_x | COL2_x | KEY1 | COL1_y | COL2_y | KEY2 | |--------|--------|------|--------|--------|------| | A | 100 | BB | B | 200 | BB | | A | 100 | BB | C | 300 | BB | | None | 400 | EE | None | 400 | EE | ``` ### 2. LEFT OUTER JOIN ```plaintext | COL1_x | COL2_x | KEY1 | COL1_y | COL2_y | KEY2 | |--------|--------|------|--------|--------|------| | A | 100 | BB | B | 200 | BB | | A | 100 | BB | C | 300 | BB | | B | 200 | CC | NULL | NULL | NULL | | C | 300 | DD | NULL | NULL | NULL | | None | 400 | EE | None | 400 | EE | ``` ### 3. FULL OUTER JOIN ```plaintext | COL1_x | COL2_x | KEY1 | COL1_y | COL2_y | KEY2 | |--------|--------|------|--------|--------|------| | A | 100 | BB | B | 200 | BB | | A | 100 | BB | C | 300 | BB | | B | 200 | CC | NULL | NULL | NULL | | C | 300 | DD | NULL | NULL | NULL | | None | 400 | EE | None | 400 | EE | | NULL | NULL | NULL | A | 100 | AA | ``` ### 4. CROSS JOIN ```plaintext | COL1_x | COL2_x | KEY1 | COL1_y | COL2_y | KEY2 | |--------|--------|------|--------|--------|------| | A | 100 | BB | A | 100 | AA | | A | 100 | BB | B | 200 | BB | | A | 100 | BB | C | 300 | BB | | A | 100 | BB | None | 400 | EE | | B | 200 | CC | A | 100 | AA | | B | 200 | CC | B | 200 | BB | | B | 200 | CC | C | 300 | BB | | B | 200 | CC | None | 400 | EE | | C | 300 | DD | A | 100 | AA | | C | 300 | DD | B | 200 | BB | | C | 300 | DD | C | 300 | BB | | C | 300 | DD | None | 400 | EE | | None | 400 | EE | A | 100 | AA | | None | 400 | EE | B | 200 | BB | | None | 400 | EE | C | 300 | BB | | None | 400 | EE | None | 400 | EE | ``` ### 5. NATURAL JOIN ```plaintext | COL1 | COL2 | KEY1 | KEY2 | |------|------|------|------| | A | 100 | BB | AA | | B | 200 | CC | BB | | C | 300 | DD | BB | | None | 400 | EE | EE | ``` 이렇게 각 조인의 결과로 얻어지는 열들과 행들은 위와 같습니다. 이를 통해 조인 방식에 따른 결과가 어떻게 달라지는지 확인할 수 있습니다. `NATURAL JOIN`은 두 테이블 간에 동일한 이름을 가진 열들을 자동으로 매칭하여 조인하는 방식입니다. `NATURAL JOIN`은 해당 열들에 대한 명시적인 조건 없이 동일한 열 이름을 가진 모든 열을 기준으로 조인을 수행합니다. ### 1. 테이블 구조 복습 #### TAB1 | COL1 | COL2 | KEY1 | |------|------|------| | A | 100 | BB | | B | 200 | CC | | C | 300 | DD | | None | 400 | EE | #### TAB2 | COL1 | COL2 | KEY2 | |------|------|------| | A | 100 | AA | | B | 200 | BB | | C | 300 | BB | | None | 400 | EE | ### 2. `NATURAL JOIN` 작동 방식 `NATURAL JOIN`은 다음 열들로 자동으로 매칭을 수행합니다: - **COL1**: 두 테이블 모두에 존재합니다. - **COL2**: 두 테이블 모두에 존재합니다. 이제, `COL1`과 `COL2`의 값이 동일한 행들에 대해 조인이 이루어집니다. 동일한 이름을 가진 열들이 조인 키가 됩니다. ### 3. 조인된 결과 조인된 결과 테이블은 동일한 열 이름에 대해 중복을 제거하여 반환되며, 추가된 열로 나머지 열들이 포함됩니다. 즉, `KEY1`과 `KEY2`는 추가된 열로 반환됩니다. 결과는 다음과 같습니다: | COL1 | COL2 | KEY1 | KEY2 | |------|------|------|------| | A | 100 | BB | AA | | B | 200 | CC | BB | | C | 300 | DD | BB | | None | 400 | EE | EE | 이 테이블에서 `COL1`과 `COL2`의 값이 두 테이블에서 동일한 모든 행이 조인되었습니다. ### 4. 요약 - `NATURAL JOIN`은 두 테이블 간에 **동일한 이름을 가진 열들을 자동으로 매칭**하여 조인합니다. - 동일한 이름을 가진 열들을 기준으로 매칭된 행들만 포함되며, 매칭된 열은 중복 없이 출력됩니다. - 예시에서 `COL1`과 `COL2`가 동일한 이름을 가지고 있었고, 그 값이 일치하는 행들만 조인되었습니다. - 조인된 결과 테이블에는 `KEY1`과 `KEY2` 같은 추가 열이 포함되어 출력됩니다. --- ![[Screenshot 2024-08-23 at 10.40.11.png]] 2번이 답. 핵심은 NOT IN 을 할 때, or로 연산을 하는데, 테이블에 null 값이 있으면 거짓이 되므로, 연산이 불가하다. 아무것도 출력되지 않는다는 것. --- ![[Screenshot 2024-08-23 at 10.45.03.png]] 핵심. Count 함수는 null을 반환하지 않는다. 위에서 서브쿼리가 거짓이어서 반환할 게 없는데, count( * )이다. 이러면 null이 아니라 0을 반환한다. `COUNT` 함수는 **NULL 값을 세지 않습니다**, 하지만 **NULL 값**이 포함된 경우에 `COUNT` 함수 자체는 **0**을 반환합니다. ### 1. `COUNT(*)` vs `COUNT(column)` - **`COUNT(*)`**: - 테이블의 모든 행을 셉니다. 행에 NULL 값이 포함되어 있더라도, 행 전체가 NULL이 아닌 한 행의 수를 계산합니다. - 즉, 테이블에 한 개의 행이 있으면, 그 행의 모든 값이 NULL이더라도 `COUNT(*)`는 `1`을 반환합니다. - **`COUNT(column)`**: - 특정 열에서 **NULL이 아닌 값**을 셉니다. 열이 NULL 값을 포함하고 있으면 그 행은 무시됩니다. - 예를 들어, 특정 열의 모든 값이 NULL이면, `COUNT(column)`은 `0`을 반환합니다. ### 예시 가정된 테이블: | COL1 | |------| | 1 | | NULL | | 3 | | NULL | - **`COUNT(*)`**: 4를 반환합니다. (모든 행의 수를 계산) - **`COUNT(COL1)`**: 2를 반환합니다. (`COL1`에서 NULL이 아닌 값만 계산) ### 결론 - **`COUNT(*)`**는 전체 행의 수를 반환하며, NULL 여부와 상관없이 0보다 큰 값을 반환합니다. - **`COUNT(column)`**은 해당 열에 대해 **NULL이 아닌 값의 수**를 반환하며, 해당 열의 모든 값이 NULL이면 `0`을 반환합니다. - `COUNT` 함수는 **NULL을 반환하지 않습니다**. 만약 카운트할 값이 없으면, 0을 반환합니다. ---