5. [SQL기초] 쿼리 선택 조건 지정하기

테이블의 모든 데이터를 가져왔던 앞의 쿼리문을 개선해서 원하는 조건에 맞는 행 데이터만을 선택적으로 가져오는 방법을 알아보겠습니다.

기본 조건 쿼리문

Select 열1, 열2, 열3 from 테이블 이름 where 조건식;

회원정보 테이블에서 id가 3보다 큰 회원 정보의 id, name, nickname 컬럼 값만 가져오고 싶으면 다음과 같이 작성합니다. 쿼리문에서 사용한 선택 조건은 2가지입니다. 모든 컬럼을 가져오는 대신 필요한 컬럼만 컬럼 이름을 명시해서 가져오는 것이 첫 번째이고, where 절에서 id가 3보다 큰 회원만 선택하는 것이 두 번째 선택 조건이 됩니다.

Where 절은 공백으로 띄워서 1개 이상의 조건식을 표시하며, 조건식이 참이 되는 행의 select 절에 명시한 컬럼(들)을 가져와 표시합니다. 쿼리문의 적용 순서는 where 절이 먼저 실행되고, 그 결과에 대해 select 절이 실행됩니다.

컬럼 이름을 명시해서 원하는 컬럼을 여러 개 가져올 때는 쉼표(,)를 사용해 컬럼 사이를 구분합니다. 공백은 컬럼 구분을 쉽게 하기 위해 추가한 것이며, 공백이 없어도, 또는 여러 개 있어도 됩니다.

Select id, name, nickname from member where id > 3;

표시되는 결과 값은 명시한 컬럼 순서대로 표시됩니다. 표시되는 결과의 컬럼 순서를 변경하려면 쿼리문의 컬럼 이름 순서를 바꿔주면 됩니다.

Select 절에 표시하는 컬럼 이름은 중복이 가능합니다. 같은 컬럼 이름을 여러 개 표시할 수 있으며, 쿼리 결과도 그대로 표시됩니다.

Select name, nickname, id, id from member where name = ‘라이언’;

고정된 값을 가진 컬럼을 추가하고 싶으면 컬럼 이름 대신 값을 직접 명시할 수 있습니다. 명시한 값은 모든 컬럼에 같은 값으로 표시됩니다.

Select name, nickname, id, ‘친구들’ from member where id >= 3;

>> MySQL은 명시한 고정 값으로 컬럼 이름과 값이 모두 적용되지만 다른 데이터베이스에서는 컬럼 값에만 적용되고, 컬럼 이름에는 적용되지 않는 경우가 있습니다. 서브 쿼리 등을 사용할 때 이런 차이점이 영향을 미칠 수 있으므로 주의해야 합니다.

Select 절 뒤에는 꼭 컬럼 이름을 1개 이상 명시해야 합니다. 또는 별표(*)로 모든 컬럼을 가져오겠다고 명시적으로 선언을 해야 합니다. 컬럼 이름을 지정하지 않으면 에러가 발생합니다.

Select from member where nickname = ‘곤룡’;

에러가 발생한 쿼리문은 다음과 같이 컬럼 이름을 지정해서 가져와야 합니다. 그리고 문자열을 조건식 상수 값으로 비교할 경우 반드시 따옴표(‘’)로 감싸서 문자열을 표기해야 합니다.

선택 쿼리문의 절은 순서를 바꿀 수 없습니다. 정해진 순서대로만 작성해야 합니다.

Select절 -> from절 -> where절

순서를 바꾸면 구문 에러가 발생하고, 쿼리문은 실행되지 않습니다. 뒤에서 배우게 되는 다른 절들도 동일한 규칙을 따라 정해진 순서대로만 사용할 수 있습니다.

Select * where id > 2 from member;

where절의 조건식은 열의 조건을 비교해서 참이 되는 행을 선택하는 것입니다. 컬럼과 비교 연산자로 비교 대상 값이나 컬럼을 비교해서 조건에 해당하는지 확인을 합니다.

Id > 2
Name = ‘라이언’
Name = nickname

조건식을 상수와 상수를 비교하는 것도 가능하지만, 항상 참이 되기 때문에 where 절이 없는 것과 같게 됩니다. 따라서 상수와 상수를 비교하는 조건식은 사용하지 않습니다.

회원정보 테이블에서 이름과 닉네임이 같은 회원의 아이디와 이름을 가져오려면 다음처럼 쿼리문을 작성합니다.

Select id, name from member where name <> nickname;

회원정보 테이블의 모든 행에서 이름과 닉네임을 비교해서 같지 않은 행 만을 추출해서 반환합니다.

>> 조건식에서 값이 서로 같지 않은지 비교하려면 <> 연산자를 사용합니다. “<>” 이외에도 “!=” 를 사용할 수 있습니다. 두 연산자는 같습니다.


연산자 비교 상수의 표시 방법

Where 절의 비교식에 표시하는 상수 값은 정해진 규칙에 따라 표기를 해야 합니다. Where 절만 아니라 쿼리문 전체에서 상수는 표기하는 규칙에 다라 표시해야 합니다.

  • 숫자는 숫자 그대로 표시합니다.
  • 문자열 데이터 형은 반드시 따옴표(‘’)로 감싸서 표시합니다. ‘딸기’와 같이 감싸서 표기하면 됩니다. 따옴표 자체를 비교대상 문자열에 포함하려면 따옴표 두 개를 연속으로 표시해서 이스케이프를 해야 합니다. ‘’’딸기’’’ 로 조건식의 상수를 표시하면 따옴표를 포함해 ‘딸기’ 문자열을 비교하게 됩니다.
  • 날짜 데이터 형은 “-“로 구분해 “년-월-일”을 붙여서 상수를 표기해 비교합니다. 따옴표로 감쌉니다. 날짜시간 데이터 형과 비교를 하면 날짜 상수 끝에는 “00:00:00” 이 붙은 것으로 처리됩니다.
  • 시간 데이터 형은 “:”로 구분해 “시:분:초”를 붙여서 상수를 표기해 비교합니다. 따옴표로 감쌉니다.
  • 날짜시간은 “-“, “:”로 구분해 “년-월-일 시:분:초” 로 상수를 표기해 날짜시간 데이터 형과 비교합니다. 따옴표로 감쌉니다. 표기 자리 수는 4자리-2자리-2자리 2자리:2자리:2자리입니다. 남는 자리 수는 왼쪽에 0을 채워서 자리 수를 맞추면 됩니다. 자리 수를 맞추지 않아도 비교를 할 때 모자라는 자리 수만큼 0으로 채워서 비교를 합니다.
    ‘2021-9-3 5:0:4’ 는 ‘2021-09-03 05:00:04’로 변환이 됩니다.

널 비교하기

널은 컬럼에 데이터가 입력되지 않은 상태를 표시하는 값의 특별한 형태입니다. 그래서 비교식으로 비교를 할 때도 데이터 형 값과는 다른 연산자로 비교를 해야 합니다. 일반 연산자로 왜 비교를 할 수 없는지 확인해보겠습니다.

Select id, name, nickname from member where email = NULL;

널이 아닌지를 비교하는 <> NULL 비교식도 같은 결과를 반환합니다.

널인지를 비교하려면 다음과 같이 IS 비교 연산자를 사용해야 합니다.

Select id, name, nickname from member where email IS NULL;

널이 아닌지를 비교하려면 NOT 논리 연산자와 조합해 다음과 같이 널이 아닌지를 확인해야 합니다.

Select * from member where email IS NOT NULL;

컬럼 값이 널인지 여부를 확인하는 것은 항상 주의를 해야 합니다. Where 절로 컬럼 값을 비교할 때는 값의 비교하는 것과는 별개로 널 여부를 추가로 확인해야 할 수도 있습니다.


여러 선택 조건 조합하기

2개 이상의 조건을 만족하는 쿼리 결과를 얻으려면 where 절에 2개 이상의 비교 조건식을 사용해야 합니다. 그리고 이 조건식들을 논리연산자로 결합해야 합니다.

비교식1 AND 비교식2 AND 비교식3

비교식1 OR 비교식2

NOT 비교식

NOT(비교식1 AND 비교식2)

실제 쿼리문을 작성하면 한 개의 비교식만으로는 원하는 결과를 얻을 수 없는 경우가 더 많습니다. 두 개 이상의 비교식을 조합하는 논리연산자는 AND, OR, NOT 3가지입니다. IN이나 EXISTS 연산자에 대해서는 뒤에서 배우므로 여기서는 비교식을 조합하는 논리 연산자에 대해서 알아봅니다.

AND, OR, NOT 연산자는 집합의 개념으로 이해를 하면 조금 더 쉽게 이해할 수 있습니다.

AND는 교집합, OR는 합집합, NOT은 차집합이 됩니다. 조건식1과 조건식2로 선택하는 결과를 기준으로 구분을 하면 다음과 같이 선택 결과를 구분할 수 있게 됩니다.

AND

연산자 좌우의 두 조건식을 모두 만족해야 참이 되는 논리 연산자입니다. 이렇게 2개의 조건식을 필요로 하는 연산자를 이항연산자라고 합니다.

예제 데이터베이스의 주문 내역에서 원하는 결과를 가져오는 쿼리문을 작성해보겠습니다.

2021년 3월 한달 동안 등록된 주문만 목록으로 뽑아서 보고 싶으면 주문일을 기준으로 3월 1일부터 3월 31일까지만 주문일의 범위를 제한해야 합니다.

Select * from p_order where ‘2021-03-01’ <= orderdate AND orderdate <= ‘2021-03-31’;

조금 더 세련되게 만들어보면, 매월 말일은 계속 날짜가 변경되므로 다음 달 1일보다 작은 날짜로 두 번째 조건식을 변경하면 조금 더 보기 쉬운 조건식을 만들 수 있습니다. 쿼리문을 작성하는 작은 팁입니다.

Select * from p_order where ‘2021-03-01’ <= orderdate AND orderdate < ‘2021-04-01’;

주문 상품 중에서 배송비가 있는 상품 중에서 상품 개수가 1개인 주문만 찾고 싶으면 다음처럼 비교식 2개를 작성하면 됩니다.

Select * from p_order where dtdmoney <> 0 AND p_num = 1;

첫 번째 조건식에 주의해야 합니다. 예를 든 쿼리문의 첫 번째 비교식은 배송비가 0보다 큰 것을 찾아야 합니다. 조건식인 <> 0은 0이 아닌 것을 찾게 되고, 0보다 작은 값을 가진 주문도 선택하게 됩니다. 그러나 배송비 컬럼이 0 이상이 값만 들어가도록 데이터 형이 양의 정수이거나, 제약 조건이 설정되어 있다면 “> 0”과 “<> 0”은 항상 같은 쿼리 결과를 반환하게 됩니다.

OR

연산자 좌우의 두 조건식 중 한 개 이상의 조건이 만족되면 참이 되는 논리 연산자입니다. 두 조건식이 모두 만족되지 않으면 거짓이 되고, 선택하는 대상에서 제외됩니다. 두 조건식의 결과를 합하는 합집합입니다.

마찬가지로 주문 테이블에서 원하는 결과를 얻기 위한 쿼리문을 작성해 보겠습니다.

취소되지 않은 주문 중에서 휴대폰 번호가 입력되지 않은 주문 목록을 가져오는 쿼리문을 작성해보겠습니다. 현실 세계에서는 회원 가입을 할 때, 또는 주문 양식을 작성할 때 휴대폰 번호나 배송주소가 입력되어 있지 않으면 주문 완료를 할 수 없겠지만, 예제 데이터베이스에서는 이런 주문이 있습니다. 휴대폰 번호가 없거나 배송 주소가 없는 경우 모두 문제가 있는 주문이기 때문에 두 조건 중 한 가지라도 해당되는 주문은 모두 찾아야 합니다.

Select * from p_order where mobile is null OR address = ‘’;

NOT

연산자 오른쪽의 조건식의 반대가 참인지를 논리 연산자입니다. 조건식은 오른쪽에 1개만 옵니다. 이런 연산자를 단항 연산자라고 합니다.

주문 테이블에서 배송비가 0원이 아닌 주문 전체를 가져오려면 다음과 같이 쿼리문을 작성합니다.

Select * from p_order where not(dtdmoney = 0);

이 쿼리문은 다음 쿼리문과 같습니다.

Select * from p_order where dtdmoney <> 0;

단일 조건식에서는 NOT은 NOT이 없는 비교식으로 쉽게 전환할 수 있습니다.

2개 이상의 조건식을 NOT 연산자와 조합할 때는 주의해야 합니다. 2개 이상의 조건식 결과에 대한 NOT 연산자 적용은 연산자 우선 순위에 따라 다르게 처리됩니다. 2개의 조건식을 괄호로 감싸서 먼저 처리가 되도록 해야 합니다.

배송비가 0원이거나 주문 상품 개수가 1개가 아닌 주문만을 가져오려면 다음과 같이 쿼리문을 작성할 수 있습니다.

Select * from p_order where NOT( dtdmoney <> 0 AND p_num = 1);

이 쿼리문은 다음 쿼리문과 같습니다.

Select * from p_order where dtdmoney = 0 OR p_num <> 1;

NOT 연산자를 사용할 때 다음과 같이 괄호를 사용해 묶지 않으면 다른 쿼리 결과가 반환됩니다. 다음 쿼리문은 배송비가 0원이고 주문 수량이 1개인 주문만을 가져옵니다.

Select * from p_order where NOT dtdmoney <> 0 AND p_num = 1;

NOT 연산자는 괄호가 있는 수식 계산의 곱하기 연산 배분 방식과 같습니다.

NOT( dtdmoney <> 0 AND p_num = 1) -> NOT (dtdmoney <> 0) NOT(AND) NOT(p_num = 1) -> dtdmoney = 0 OR p_num <> 1

NOT 연산자는 논리, 비교 연산자를 반대로 바꾸는 기능을 합니다.


논리 연산자 조합 우선순위

논리 연산자는 연산자 우선순위가 있고 괄호를 사용해 우선 연산 대상을 정하지 않으면 왼쪽에서부터 오른쪽으로, 그리고 연산자 우선 순위에 따라 먼저 연산이 됩니다.

OR < AND  < NOT 순으로 우선 순위가 높으며 괄호로 감싼 조건식은 최우선 순위가 있습니다.

조건식1 OR 조건식2 AND 조건식3 -> 조건식1 OR (조건식2 AND 조건식3)

조건식1 AND 조건식2 OR 조건식3 AND 조건식4 -> (조건식1 AND 조건식2) OR (조건식3 AND 조건식4)

OR 논리연산자 비교를 우선 적용하려면 괄호로 OR 연산자 좌우의 조건식까지 감싸서 우선 순위가 있음을 명시해야 의도한대로 조건식 비교가 적용됩니다. 괄호를 사용해 우선 순위를 조정하면 다음처럼 비교식 비교 순서가 달라집니다.

조건식1 AND NOT (조건식2 OR 조건식3) AND 조건식4


잘못된 조건식 사용

잘못된 조건식을 사용하면 원하는 결과를 얻을 수 없는데 논리 연산자로 여러 개의 조건식을 조합하다 테이블의 전체 행을 가져오거나 아무 행도 가져오지 않는 쿼리문을 만들게 되기도 합니다.

Select * from p_order where p_num = 1 or 2;
Select * from p_order where dtdmoney > 0 and 0;

Where 절의 조건식의 결과는 언제나 TRUE, 또는 FALSE입니다. 그리고 where 절에서 상수 0은 거짓, 그 외에는 모두 참으로 간주됩니다.

p_num = 1 or 2 조건식 조합은 오른쪽 조건식이 숫자 2 상수이기 때문에 항상 참이 되고 모든 행을 반환합니다. 반대로 dtdmoney > 0 and 0 은 오른쪽 조건식이 상수 0이어서 항상 거짓이 되고 아무런 결과 행도 반환하지 않습니다.

Where 절에서는 상수 1개만 표시를 해도 정상인 조건식으로 간주됩니다. 다음의 쿼리문은 where 절의조건식이 항상 참이기 때문에 테이블의 모든 데이터를 반환합니다.

Select * from p_order where 1;

날짜 조건 지정하기

날짜, 시간, 날짜시간 데이터 형의 조건식을 작성할 때는 정해진 포맷에 맞는 비교 값을 작성해야 합니다. 그리고 날짜 범위를 정할 때는 연산자를 사용하는데도 조금 주의를 해야 합니다.

오늘 주문 내역만 가져오는 쿼리문의 조건식을 다음과 같이 작성합니다. 오늘 자정부터 밤 23시 59분 59초 보다는 작은 주문을 가져는 쿼리문입니다.

Select * from p_order where ‘2021-03-27 00:00:00’ <= orderdate and orderdate <= ‘2021-03-27 23:59:59’;

이 쿼리문에는 함정이 있습니다. 실제 데이터베이스의 시간, 날짜시간 데이터 형의 저장 데이터에는 밀리세컨드 단위까지 저장됩니다. “23:59:59”는 실제로는 “23:59:59.000”로 적용이 됩니다.

만약 23:59:59.235”에 주문이 등록되었으면 쿼리 결과에서 이 주문은 제외가 됩니다. 따라서 쿼리문은 연산자를 바꿔서 다음과 같이 바꿔야 합니다.

Select * from p_order where ‘2021-03-27 00:00:00’ <= orderdate and orderdate < ‘2021-03-28 00:00:00’;

처음 쿼리문의 비교 날짜인 ‘2021-03-27 23:59:59’를 ‘2021-03-27 23:59:59.999’로 바꾸면 새로 바꾼 쿼리문과 같은 결과를 얻을 수 있습니다.

하지만 어떻게 봐도 새로 작성한 쿼리문이 더 이해하기 쉽고, 작성하기도 쉽습니다.


조건 패턴 매칭 사용하기

조건 패턴 매칭을 위해 사용하는 키워드는 “like”입니다. 쉽게 like절이라고 하겠습니다. 오피스 프로그램의 문자열 찾기와 같은 방식으로 테이블의 컬럼에 찾고자 하는 내용이 포함되어 있는지를 찾는데 사용하는 절입니다.

“=”로 컬럼의 값을 비교하는 방법은 컬럼 값 전체가 비교 대상과 일치하는지를 확인합니다. 컬럼 값의 일부가 일치하거나, 일부가 포함되어 있는지를 비교할 수는 없습니다.

Like 절은 문자열 컬럼 값의 일부가 일치하는지를 비교합니다. 일부가 일치하는지를 찾기 위해서는 컬럼 값의 어느 위치에서부터 찾는지를 명시할 필요가 있습니다.

패턴 매칭을 하기 위해서는 임의의 문자, 또는 문자열을 표시하는 기호가 있어야 합니다. 패턴 매칭을 위해서 다음 2개의 특수문자를 사용합니다. 다른 특수문자는 일반 문자로 처리됩니다.

문자 설명
% 퍼센트 기호로 표시. 길이 제한이 없는 임의의 문자열을 표시하는데 사용.
검색할 문자열 앞에 붙이면 앞에 임의의 문자열이 오고 검색 대상 문자열이 오는 것이고, 뒤에 붙이면 검색할 문자열 뒤에 임의의 문자열이 더 오는 것입니다.
 
Like ‘%검색문자열’
Like ‘검색 문자열%’
Like ‘%검색문자열%’
Like ‘검색%문자열’
_ 언더바(또는 언더스코어) 기호로 표시. 임의의 문자 1개가 있을 때 표시
 
Like ‘_검색문자열’
Like ‘검색_문자열’
Like ‘검색_문자열_’

상품정보 테이블에서 상품 상세 정보의 내용을 검색해 “신상품” 문자열이 포함되어 있는 상품 목록을 가져와 보겠습니다.

%는 0개 이상의 문자열 길이를 가진 임의의 문자열을 나타냅니다. 컬럼 값 안 문자열의 아무 위치에나 “신상품” 문자열이 있으면 결과에 포함됩니다.

Select * from product where description like ‘%신상품%’;

찾으려는 “신상품” 문자열 사이에 공백이나 기타 다른 문자가 들어있으면 앞의 쿼리문으로는 검색이 되지 않습니다.

Select * from product where description like ‘%신_상품%’;

“신”과 “상품” 사이에 공백이나 키다 다른 문자가 1개가 있으면 검색이 되지만, 실수로 공백을 2개 입력했다면 이 쿼리문으로는 검색되지 않습니다. 또는 2개 이상의 키워드 단어가 모두 들어있는 문자열을 검색하고 싶으면 다음과 같이 키워드 사이에 “%”를 키워 넣어 쿼리문을 작성할 수 있습니다.

Select * from product where description like ‘%2021%신상%’;

이 때 주의할 점이 있습니다. “신상”이 먼저 나오고 “2021”이 나중에 나오면 검색 결과에서 제외됩니다. 문자열이 나오는 순서에 따라 키워드가 매칭된다는 점을 주의해서 쿼리문을 작성해야 합니다.

패턴 문자열로 시작하거나 끝나는 검색 결과를 찾으려면 “%” 문자를 앞이나 뒤에 하나만 표시하면 됩니다. 상품 정보 테이블의 description 컬럼에는 “생일” 키워드가 들어간 상품이 2개가 있지만 다음과 같이 문자열 패턴 검색을 하면 “생일” 문자열이 description 컬럼 맨 앞에 나오는 행 1개만 반환됩니다.

Select * from product where description like ‘생일%’;

따옴표와 특수문자를 검색 대상에 포함하기

‘, %, _ 3개는 문자열 표현 및 패턴 매칭을 위한 특수 기호입니다. 문자열 비교, 또는 패턴 매칭을 할 때 이 기호들은 대체표현(이스케이핑)으로 표시를 해야 사용할 수 있습니다.

따옴표(‘)는 연속으로 따옴표 2개를 붙여서 표기하면 따옴표가 검색 문자열에 포함됩니다. ‘I’’m back’ 과 같이 표기합니다. 컬럼에 따옴표로 감싼 문자열이 있으면 검색 문자열로 ‘’’검색문자열’’’ 과 같이 표기합니다.

문자열 패턴 매칭을 위해 사용하는 %와 _는 역 슬래시(\)와 조합해서 이스케이프 문자로 표현해야 합니다. 다음과 같이 검색 문자열 패턴을 정의하면 description 컬럼에 “%” 문자가 들어있는 모든 행이 반환됩니다.

Select * from product where description like ‘%2021%\%%’;

조건 패턴 매칭의 제한

조건 패턴 매칭은 문자열을 검색하기 위한 것입니다. 숫자형, 날짜형에는 사용할 수 없습니다. 다음 쿼리문은 오류를 발생하지 않고 쿼리 결과를 반환하지만 “%”는 아무런 영향을 미치지 않습니다. 날짜 데이터 형을 비교하기 위한 비교 값에서 인식하지 못하는 문자는 삭제됩니다.

Select * from p_order where wt > '2021-03-14%';