SQL 기초

haaaalin·2023년 9월 16일
0

SQL LevelUp

목록 보기
2/9
post-thumbnail

SELECT 구문

WHERE 구

IN으로 OR 조건 작성하기

OR 조건을 굉장히 많이 지정해야 할 때, 아래와 같이 쓸 수 있지만 IN 을 사용한다면 가독성 좋은 SQL문을 작성할 수 있다.

SELECT name, address
 FROM Address
 WHERE address = '서울시'
    OR address = '부산시'
    OR address = '인천시';

아래와 같이 작성하면, 실행 결과는 같지만 깔끔하게 바꿀 수 있다.

SELECT name, address
 FROM Address
 WHERE address IN ('서울시', '부산시', '인천시');

NULL인 레코드 선택하기

전화번호가 없는 사람만 선택하는 SQL문을 작성하고 싶다면 어떻게 해야 할까?

아래와 같이 NULL과 같은 사람만 선택하도록 SQL문을 작성한다면, 아무 결과도 얻지 못할 것이다.

SELECT name, address
 FROM Address
 WHERE phone_nbr = NULL;

NULL 레코드를 선택할 때는 IS NULL 이라는 키워드를 사용해야 한다.

반대로, NULL이 아닌 레코드를 선택할 때는 IS NOT NULL 을 사용하면 된다.

SELECT name, phone_nbr
 FROM Address
 WHERE phone_nbr IS NULL;

HAVING 구

SQL은 SELECT구문을 실행해 얻은 결과 집합에 또다시 조건을 걸어 선택하는 기능이 있다.

SELECT address, COUNT(*)
 FROM Address
 GROUP BY address
HAVING COUNT(*) = 1;
address  | count 
----------+-------
서귀포시 |     1
속초시   |     1
(2 rows)

WHERE 구 = ‘레코드’에 조건을 지정

HAVING 구 = ‘집합’에 조건을 지정

뷰와 서브쿼리

SELECT 구문을 데이터베이스 안에 저장하는 방법? View

CREATE VIEW CountAddress (v_address, cnt)
AS
SELECT address, COUNT(*)
 FROM Address
 GROUP BY address;

이렇게 만들어진 view는 일반 테이블처럼 SELECT 구문에서 사용할 수 있다.

view는 내부적으로 데이터를 보유하는 것이 아니라서, 실제로는 view 에 SELECT 구문을 실행 시 추가적인 SELECT 구문을 실행한다.

SELECT v_address, cnt
 FROM (SELECT address AS v_address, COUNT(*) AS cnt
          FROM Address
         GROUP BY address) AS CountAddress;

위와 같은 SQL문을 실행하는 것과 같아, 직접 SELECT 문을 작성해도 괜찮다.

FROM 구에 직접 지정하는 SELECT 구문을 서브쿼리라고 부른다.

서브쿼리 활용

위에서 본 서브쿼리는 IN 과 함께 유용하게 사용할 수 있다.

만약, address1과 address2에 공통으로 존재하는 데이터를 가져오고 싶다면?

SELECT name
 FROM Address
 WHERE name IN (SELECT name -- IN 내부에서 서브쿼리 사용
                  FROM Address2);

조건 분기, 집합 연산, 함수, 갱신

SQL 조건 분기

SQL은 절차적 언어가 아니기 때문에, 조건 분기를 ‘문장’ 단위로 하지 않고, ‘식’ 단위로 한다.

‘식’ 단위의 분기를 실현하는 기능이 바로 CASE 식이다.

검색 CASE 식의 구문

보통 아래와 작성하고, 작동은 절차 지향형 프로그래밍 언어에서 switch 조건문과 비슷하다.

CASE 
WHEN [평가식] THEN []
WHEN [평가식] THEN []
WHEN [평가식] THEN []
ELSE []
END

실제로 사용하는 예시는 다음과 같다.

SELECT name, address,
       CASE WHEN address = '서울시' THEN '경기'
            WHEN address = '인천시' THEN '경기'
            WHEN address = '부산시' THEN '영남'
            WHEN address = '속초시' THEN '관동'
            WHEN address = '서귀포시' THEN '호남'
            ELSE NULL END AS district
 FROM Address;

‘서울시’ → ‘경기’, ‘인천시’ → ‘경기’ 이런 식으로 테이블에 존재하는 데이터를 교환할 때 유용하게 사용할 수 있다.

CASE식의 또다른 장점은, 식을 적을 수 있는 곳 어디든 적을 수 있다.

⇒ 예를 들어, SELECT, WHERE, GROUP BY, HAVING 등 구와 같은 곳 어디에나 적을 수 있다.

SQL의 집합 연산

UNION - 합집합

문자 그대로, 두 테이블을 하나의 테이블로 합친 결과가 나오는 연산

만약 Address에는 9개의 레코드, Address2에는 6개의 레코드가 있다고 치자.

하지만 아래 SQL문을 실행한 결과는 13개의 레코드가 나온다. 왜 그런걸까?

SELECT *
 FROM Address
UNION
SELECT *
 FROM Address2;

바로 UNION 연산에서는 양쪽 테이블에서 모든 데이터가 같은 중복 데이터는 제외한 결과를 반환하기 때문이다.

만약, 중복된 레코드를 제외하고 싶지 않다면 UNION ALL 처럼 ALL 옵션을 붙이면 된다.

INTERSECT - 교집합

SELECT *
 FROM Address
INTERSECT
SELECT *
 FROM Address2;

양쪽 테이블에 공통으로 존재하는 레코드를 출력한다.

이또한 공통으로 존재하는 레코드가 중복해서 있다면, 중복 데이터는 제외하고 출력되는 점 유의

EXCEPT - 차집합

SELECT *
 FROM Address
EXCEPT
SELECT *
 FROM Address2;

양쪽 테이블에 공통으로 존재하는 레코드를 제외한 레코드를 출력한다.

하지만 이때 주의할 점은 차집합은 수식으로 나타내면 Address - Address2 와 같은 뺼셈이다. 따라서, 교환 법칙이 성립하지 않는다.(즉, SQL 구문에서 어떤 테이블을 먼저 적는지에 대한 영향이 존재)

윈도우 함수

집약 기능이 없는 GROUP BY 구

아래는 윈도우 함수를 사용하지 않고, 주소별 사람 수를 출력하는 SQL문이다.

SELECT address, COUNT(*)
FROM Address
GROUP BY address;

일단 address 필드로 테이블을 자르고, 잘라진 테이블 별로 레코드 수를 출력한다.

윈도우 함수의 기본적인 구문

  • 집약 함수 뒤에 OVER 구 작성
  • 내부에 자를 키 지정하는 PARTITION BY 또는 ORDER BY 입력

아래는 윈도우 함수를 사용해, 주소별 사람 수를 출력하는 SQL문이다.

뭐가 다를까?

SELECT address,
COUNT(*) OVER(PARTITION BY address)
FROM Address;
address  | count 
----------+-------
 부산시   |     2
 부산시   |     2
 서울시   |     3
 서울시   |     3
 서울시   |     3
 속초시   |     1
 인천시   |     2
 인천시   |     2
 서귀포시 |     1
(9 rows)
  • 일단 테이블을 자르는 동작은 같다. ⇒ PARTITION BY
  • 자른 후에 집약하지 않으므로, 출력 결과의 레코드 수가 입력되는 테이블의 레코드 수와 같다

RANK

지정된 키로 레코드에 순위를 붙이는 함수

나이가 많은 순서로 순위를 붙인다면 아래와 같다.

SELECT name,
       age,
       RANK() OVER(ORDER BY age DESC) AS rnk
FROM Address;
name  | age | rnk 
--------+-----+-----
 하린   |  55 |   1|  45 |   2
 기주   |  32 |   3|  32 |   3
 인성   |  30 |   5
 아린   |  25 |   6
 하진   |  21 |   7
 인아   |  20 |   8
 빛나래 |  19 |   9
(9 rows)

위와 같이 나이에 따라 순위가 매겨진다.

위처럼, 기주와 민의 나이가 같아서 공동3위가 되어, 4위가 없는데 이러한 건너뛰는 작업을 없애고 싶다면 DENSE_RANK 를 사용하면 된다.

profile
한 걸음 한 걸음 쌓아가자😎

0개의 댓글