Notice
Recent Posts
Recent Comments
Link
«   2026/07   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

어리바리 신입 개발자의 얼렁뚱땅 개발 기록 ✨

23.05.15 / 내장 함수, MySQL 변수 본문

Database/MYSQL

23.05.15 / 내장 함수, MySQL 변수

낫쏘링 2023. 5. 15. 11:42
728x90

SQL의 함수는 크게 두 종류 : 내장 함수 / 사용자 정의 함수

[ 내장 함수 ]

  • DBMS가 제공하는 함수
  • 최초 선언 시, 유효한 값을 입력 받아야한다. 선언에 위배되는 값을 입력하면, 실행이 중지되고 에러 메시지가 출력된다.
  • SELECT, UPDATE, DELETE에서 모두 사용가능하다.
[[ 주요 내장 함수 ]]

[ 제어 흐름 함수 ]
프로그램의 흐름을 제어
- IF(수식, 참, 거짓)
SELECT IF(100 > 300, 'T', 'F'); 
-- F 반환​

- IFNULL(수식1, 수식2) : 수식1이 NULL이 아니면 수식1 반환 / NULL이면 수식2 반환
SELECT IFNULL(NULL,'NULL입니까'), IFNULL(300,'NULL입니까'); 
-- NULL입니까 / 300 반환​

- NULLIF(수식1, 수식2) : 수식1과 수식2가 같으면 NULL을 반환 / 다르면 수식1 반환
SELECT NULLIF(200,200),NULLIF(200,500); 
-- NULL / 200 반환​


[ 숫자 함수 ]
- ABS(숫자) : 숫자의 절댓값 계산
SELECT ABS(-27), ABS(27);
-- 27 / 27 반환​

- CEIL(숫자) OR CEILING(숫자) : 숫자보다 크거나 같은 최솟값의 정수
- FLOOR(숫자) : 숫자보다 작거나 같은 최소의 정수
SELECT CEIL(4.7), FLOOR(4.7);
-- 5 / 4 반환​​

- ROUND(숫자, n) : 숫자의 반올림. n  - 반올림의 자릿수 
SELECT ROUND(3.14159,2);
-- 3.14 반환​


[ 문자 함수(문자 반환) ]
- CONCAT(str1, str2) : 문자열 연결
SELECT CONCAT('안녕','하세요');
-- 안녕하세요 출력

- CONCAT_WS : 구분자와 함께 문자열 연결
SELECT CONCAT_WS('-','010','1234','5678');
-- 010-1234-5678 출력

- REPLACE(str1, str2, str3) : 원하는 문자열로 변경 / str1의 str2 부분을 str3으로 변경
SELECT REPLACE('마시는 비타민 비타500','비타민','비타민C');
-- 마시는 비타민C 비타500 출력

- SUBSTR(str1, num1, num2) :  str1의 num1번째 글자부터 num2번째 글자까지 반환 = SUBSTRING = MID 
SELECT SUBSTR('안녕하세요',1,2);
SELECT SUBSTRING('안녕하세요',1,2);
SELECT MID('안녕하세요',1,2);
-- 안녕 / 안녕 / 안녕 반환

- TRIM : 문자열의 앞 뒤 공백 제거
SELECT TRIM('        안녕하세요     ');
-- 안녕하세요 출력


[ 문자 함수(숫자 반환) ]
- ASCII(char)
SELECT ASCII('A'),CHAR(65);
-- 65 / A 반환

- LENGTH(str) : 할당된 byte 수를 반환
SELECT LENGTH('가나다'),LENGTH('abc');
-- 9 / 3 반환

- CHAR_LENGTH(str) : 문자의 개수를 반환

SELECT CHAR_LENGTH('가나다'),CHAR_LENGTH('abc');
-- 3 / 3 반환


[ 날짜 및 시간 함수 ]
- ADDDATE() : 날짜 더해서 반환
SELECT ADDDATE('2023-01-01', INTERVAL 31 DAY);
-- 2023-02-01 출력 ( INTERVAL 띄어쓰기 31 띄어쓰기 DAY 주의)​

- SUBDATE() : 날짜 빼서 반환
SELECT SUBDATE('2023-05-15', INTERVAL 10 DAY);
-- 2023-05-05 반환​

- ADDTIME() : 시간 더해서 반환
- SUBTIME() : 시간 빼서 반환
- CURDATE(), CURTIME(), SYSDATE() : 현재날짜, 시간 반환
SELECT CURDATE();
SELECT CURTIME();
SELECT SYSDATE();
-- 2023-05-15 / 12:28:42 / 2023-05-15 12:28:42 반환
-- 현재날짜   / 현재시간 / 현재날짜 시간​

- DATE(), TIME()
- DATEDIFF('날짜1', '날짜2') : 날짜1 - 날짜2
SELECT DATEDIFF('2023-12-31',NOW());
-- 230 반환 
-- NOW() : 오늘 날짜​

SELECT DATEDIFF('2023-12-31','2023-05-05');
-- 240 반환

 

- YEAR(), MONTH(), DATE() : 년,월,일
SELECT
	L.login_id
,	M.m_email
,	L.login_date
FROM
	tb_login AS L
	INNER JOIN
	tb_member AS M
	ON
	L.login_id = M.m_id
GROUP BY L.login_id
HAVING
	MONTH(L.login_date) = 2;
    
/* GORUP BY로 그룹으로 묶으면 딱 하나의 튜플만 조회 된다. 
	=> 2월 기록이 있어도 3월 기록이 있으면 3월 기록만 남음
       그 후에 2월 기록을 찾는 것이기 때문에 2월에 로그인한 회원을 찾을 수 없음 */    
    
=======================================    

SELECT
	L.login_id
,	M.m_email
,	L.login_date
FROM
	tb_login AS L
	INNER JOIN
	tb_member AS M
	ON
	L.login_id = M.m_id
WHERE 
	MONTH(L.login_date) = 2
GROUP BY L.login_id;

/* WHERE로 먼저 2월 로그인 기록을 찾고 그룹으로 묶으면 조회가 된다. 
	WHERE와 GROUP BY를 함께 쓸 수 없다는 것은 GROUP BY로 묶고 그 안에서 조건문을 쓸 때 불가능하다는 듯
    WHERE와 GROUP BY를 별개로 사용하면 가능하다.*/​​

- LAST_DAY('날짜') : 해당 날짜가 속한 달의 가장 마지막 날짜
SELECT
	L.login_id
,	M.m_email
,	L.login_date
FROM
	tb_login AS L
	INNER JOIN
	tb_member AS M
	ON
	L.login_id = M.m_id
WHERE 
	L.login_date BETWEEN '2023-02-01' AND LAST_DAY('2023-02-01')
GROUP BY L.login_id;​


[ 데이터 형 변환 함수 ]
명시적 형변환 : 내가 직접 함수를 사용해서 변환
- CAST(갑 AS 데이터_형식[(길이)]) / CONVERT(값, 데이터_형식[(길이)]) 

[ NULL 관련 함수 ]

[ 기타 : 시스템 함수 ]
시스템 정보 함수
시스템의 정보를 출력하는 함수를 제공한다
- USER() : 사용자 아이디 / root@localhost 출력
- DATABASE() : 데이터베이스 이름 / ksmart47db_basic 출력
- VERSION() : MySQL 버전 출력
- SLEEP(초) : 의도적으로 쿼리 실행을 멈춘다.
[[ WINDOW FUNCTION ]]

MySQL 8버전 이상에서 지원 -> 낮은 버전의 개발 환경에서는 사용할 수 없음

GROUP BY와 비슷한 역할 / 집계 함수 대부분 사용 가능 + 윈도우 함수에서만 지원하는 기능 사용 가능
행과 행을 비교 / 행과 행에 대한 연산 

- OVER(PARTITION BY ORDER BY)
- OVER(PARTITION BY)
- OVER(ORDER BY)

* PARTITION BY가 ORDER BY 보다 앞에 나와야함.
* PARTITION BY와 ORDER BY가 둘 다 있어야하는 것은 아니다.

함수(컬럼) OVER (PARTITION BY 컬럼명 ORDER BY 컬럼명)
- 함수 : 집계 함수 , 순위 함수 ....
- (컬럼) : 집계 대상 열 이름 작성
- PARTITION BY 컬럼명 : 어떤 컬럼을 그룹으로 만들지 기준이 되는 컬럼을 작성한다. (GROUP BY와 비슷)
- ORDER BY 컬럼명 : 데이터 정렬

[ 집계 함수 ]

SELECT 
     함수() OVER(PARTITION BY 컬럼명 ORDER BY 컬럼명 내림차순 또는 오름차순)
FROM 테이블_이름;
SELECT
	c.Name
,	c.CountryCode
,	c.Population
,	MAX(c.Population) AS 최대인구
FROM
	city AS c
GROUP BY c.CountryCode;

-- GROUP BY : 내용을 축약해서 보여준다.



SELECT
	c.Name
,	c.CountryCode
,	c.Population
,	MAX(c.Population) OVER(PARTITION BY c.CountryCode) AS 최대인구
FROM
	city AS c;
    
-- PARTITION BY : 축약하지 않고 전부 보여준다.

/*
GROUP BY를 사용하면 CountryCode가 같은 것을 묶어서 
그 중 최댓값을 갖고 있는 하나의 튜플만 보여준다.

OVER(PARTITION BY)를 사용하면 CountryCode가 같은 것을 묶어서
그 중 최댓값을 같은 그룹 내 모든 튜플의 '최대인구' 컬럼에 넣어서 보여준다. 
*/

 

GROUP BY 사용
OVER(PARTITION BY) 사용




SUM에 ORDER BY를 사용하면, 누적합을 구할 수 있다.

SELECT
	C.Name
,	C.CountryCode
,	C.Population
,	SUM(C.Population) OVER(PARTITION BY C.CountryCode) AS 인구합계
FROM
	city AS C;
    
/*
ORDER BY가 없을때
같은 CountryCode를 그룹으로 묶어서
같은 그룹 내 모든 튜플의 인구합계 컬럼에 합계를 넣어준다.
*/


SELECT
	C.Name
,	C.CountryCode
,	C.Population
,	SUM(C.Population) OVER(PARTITION BY C.CountryCode ORDER BY C.Name) AS 인구합계
FROM
	city AS C;
    
/*
ORDER BY가 있을때
같은 CountryCode를 그룹으로 묶어서
같은 그룹 내 모든 튜플의 인구합계 컬럼에 차례대로 누적 합계를 넣어준다.
*/

 

ORDER BY 사용 X

 

ORDER BY 사용


조건 : g_seller_id를 그룹으로 묶어서 판매 금액 총 합이 3,000,000 초과 / g_seller_id와 g_price 출력

SELECT
	g.g_seller_id
,	g.g_price 
FROM
	tb_goods AS g
GROUP BY
	g.g_seller_id
HAVING
	SUM(g.g_price) > 3000000;	
    
    
/* 
GROUP BY / HAVING 단독으로 사용할 경우
g_seller_id의 판매 금액들 중 맨 위에 있는 튜플 딱 하나만 출력
*/

SELECT 
	g.g_seller_id
,	g.g_price
FROM 
	tb_goods AS g
WHERE 
	g.g_seller_id 
    IN (
    	SELECT g_seller_id
    	FROM tb_goods
    	GROUP BY g_seller_id
    	HAVING SUM(g_price) > 3000000
	)
ORDER BY g.g_seller_id;

/*
GROUP BY / HAVING 을 WHERE ~ IN ~ 절에 넣어 사용할 경우
g_seller_id의 판매 금액들 모두 출력
*/
GROUP BY / HAVING만 사용
WHERE IN 절에 GROUP BY 사용


조건을 넣고 싶을 때
OVER(PARTITION BY) 와 GROUP BY 둘 다 WHERE에 직접적으로 사용 불가능
GROUP BY - HAVING 사용하면 된다. (딱 하나의 결과만 출력)
딱 하나의 결과만 출력하지 않고 여러 결과 출력하고 싶을때는 OVER 사용? - 안 됨
OVER랑 WHERE / HAVING 둘 다 못 쓰기 때문에 WHERE ~ IN (GROUP BY ~ HAVING) 사용한다.



[ 순위 ] 
순위_함수() OVER(ORDER BY 컬럼_명)
* 인자에 아무 값도 작성하지 않는다.

- ROW_NUMBER : 작은 순서부터 출력. 값이 커지는 순서에 따라 순위도 높아짐. 어떻게든 순서를 정해서 중복 순서 없음
- RANK : 값이 동일하면 같은 순위를 부여한다.
- DENSE_RANK : 1이 3개 있을 때 RANK는 2와 3 없이 4로 넘어가지만, DENSE_RANK는 넘기는 것 없이 2와 3 부여

SELECT
	C.Name
,	ROW_NUMBER() OVER(ORDER BY C.Population) AS row_num
,	RANK() OVER(ORDER BY C.Population) AS 'rank' -- rank는 예약어이기 때문에 '' 필수
,	DENSE_RANK() OVER(ORDER BY C.Population) AS dense
FROM
	city AS C
WHERE
	C.CountryCode ='KOR';

 



 

[ 사용자 정의 함수 ]

사용자가 직접 만드는 함수

- 다른 프로그래밍 언어처럼 변수를 선언하고 사용할 수 있는데, 형태가 약간 다름

SET @변수_이름 := 개입값;
SET @num1 := 10;
SET @num2 := 20;

SELECT
	@num1 AS '변수1'
,	@num2 AS '변수2';

-- 변수1 = 10 / 변수2 = 20


LIMIT의 경우 변수를 사용할 수 없으나 PREPARE문과 EXCUTE문을 사용하여 변수를 사용할 수 있다.

/* 
city 테이블 = c 에서 CountryCode가 KOR인 경우만 
cityName 컬럼에 도시의 이름: 을 붙여서 
Name, CountryCode 컬럼 출력 
*/

SET @cityName := '도시의 이름:';
SET @num := 10;

SELECT 
	@cityName
,	c.Name
,	c.CountryCode
FROM
	city AS c
WHERE
	c.CountryCode = 'KOR'
LIMIT @num;

-- LIMIT @num 에서 오류 발생


PREPARE 쿼리_이름
FROM
'쿼리문';

PREPARE - > 쿼리문을 준비하고, 실행은 하지 않는다.

EXECUTE 쿼리_이름 USING 변수명;

EXECUTE 쿼리_이름 -> EXECUTE 쿼리를 만나는 순간 실행, PREPARE 쿼리문의 ? 자리에 USING 변수명을 대입한다.

/* 
city 테이블 = c 에서 CountryCode가 KOR인 경우만 
cityName 컬럼에 도시의 이름: 을 붙여서 
Name, CountryCode 컬럼 출력 
*/

SET @cityName := '도시의 이름:';
SET @num := 10;

PREPARE queryTest
FROM
	'SELECT 
		@cityName
	,	c.Name
	,	c.CountryCode
	FROM
		city AS c
	LIMIT ?';
EXECUTE queryTest USING @num;
728x90