## Basic Command ```sql -- Create Table CREATE TABLE users ( user_id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, username CHAR(10) NOT NULL UNIQUE, email VARCHAR(50) NOT NULL, gender ENUM('Male', 'Female'), intersets SET( 'Technology', 'Sports', 'Music', 'Art', 'Travel', 'Food', 'Fashion', 'Science' ) NOT NULL, bio TEXT NOT NULL, profile_picture TINYBLOB, age TINYINT UNSIGNED NOT NULL, is_admin BOOLEAN DEFAULT FALSE NOT NULL, balance FLOAT DEFAULT 0.0 NOT NULL, joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL, birth_date DATE NOT NULL, bed_time TIME NOT NULL, graduation_year YEAR NOT NULL CONSTRAINT chk_age CHECK(age<100), CONSTRAINT uq_email UNIQUE(email) ); -- Insert element INSERT INTO users ( username, email, gender, intersets, bio, age, is_admin, birth_date, bed_time, graduation_year ) VALUES ( 'mr.nobody', '[email protected]', 'Male', 'Travel,Food,Technology', 'I like traveling and eating', 88, TRUE, '1999.05.08', '22:30', '1976' ); -- drop column ALTER TABLE users DROP COLUMN profile_picture; -- change column ALTER TABLE users CHANGE COLUMN bio abuot_me TINYTEXT; -- modify column ALTER TABLE users MODIFY COLUMN; -- remane database ALTER TABLE users RENAME TO customers; ALTER TABLE customers RENAME TO users; -- drop constraints ALTER TABLE users DROP CONSTRAINT uq_email; ALTER TABLE users DROP CONSTRAINT username; -- adding constraints ALTER TABLE users ADD CONSTRAINT uq_eamil UNIQUE(email), ADD CONSTRAINT uq_username UNIQUE(username) -- add or remove a NULL constraint ALTER TABLE users MODIFY bed_time TIME NULL; ALTER TABLE users MODIFY bed_time TIME NOT NULL; -- Change column date type ALTER TABLE users ADD COLUMN grduation_date DATE NOT NULL DEFAULT MAKEDATE(graduation_year, 1); -- SHOW CURRENT TABLE design SHOW CREATE TABLE users; ``` ## GENERATED COLUMNS - GENERATED : 특정 값을 토대로 연산을 통해 만들어진 값을 만들어준다. - STORED : 실제로 값이 데이터로 저장된다. - VIRTUAL : 컬럼을 불러올 떄 자동으로 **연산**되어 보여진다.(저장X) ```sql CREATE TABLE users_v2 ( user_id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, first_name VARCHAR(50), last_name VARCHAR(50), email VARCHAR(100), full_name VARCHAR(101) GENERATED ALWAYS AS (CONCAT(first_name, ' ', last_name)) STORED ); ALTER TABLE users_v2 ADD COLUMN email_domain VARCHAR(50) GENERATED ALWAYS AS(SUBSTRING_INDEX(email, '@', -1)) virtual; SELECT * FROM users_v2; ``` ## FOREIGN KEY ```sql CREATE TABLE dogs ( dog_id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, date_of_birth DATE, weight DECIMAL(5, 2), owner_id BIGINT UNSIGNED, breed_id BIGINT UNSIGNED DEFAULT 2, -- ON DELETE CASCADE -- ON DELETE SET NULL -> NOT NULL(X) -- ON DELETE SET DEFAULT CONSTRAINT owner_fk FOREIGN KEY (owner_id) REFERENCES owners (owner_id) ON DELETE SET NULL, CONSTRAINT breed_fk FOREIGN KEY (breed_id) REFERENCES breeds (breed_id) ON DELETE SET DEFAULT ); ALTER TABLE dogs DROP FOREIGN KEY owner_fk; ``` ### Relationship #### 1:N OR N:1 ```sql CREATE TABLE owners ( owner_id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, email VARCHAR(100) UNIQUE, phone VARCHAR(20), address TINYTEXT ); CREATE TABLE breeds ( breed_id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, size_category ENUM('small', 'medium', 'big') DEFAULT 'small', typical_lifespan TINYINT ); ``` #### 1:1 ```sql CREATE TABLE pet_passports ( pet_id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, blood_type VARCHAR(10), allergies TEXT, last_checkup_date DATE, dog_id BIGINT UNSIGNED UNIQUE, FOREIGN KEY dog_id REFERENCES dogs (dog_id) ON DELETE CASCADE ); ``` #### N:N ```sql CREATE TABLE tricks ( trick_id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) UNIQUE NOT NULL, difficulty ENUM('easy', 'medium', 'hard') NOT NULL DEFAULT 'easy' ); -- Bridge TABLE CREATE TABLE dog_tricks ( dog_id BIGINT UNSIGNED, trick_id BIGINT UNSIGNED, proficiency ENUM('beginner', 'intermediate', 'expert') NOT NULL DEFAULT 'beginner', date_learned TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (dog_id, trick_id), FOREIGN KEY (dog_id) REFERENCES dogs (dog_id) ON DELETE CASCADE, FOREIGN KEY (trick_id) REFERENCES tricks (trick_id) ON DELETE CASCADE ); ``` ## EVENT ```sql CREATE TABLE archived_movies LIKE movies; TRUNCATE TABLE archived_movies; DROP EVENT archived_old_movies; CREATE EVENT archived_old_movies ON SCHEDULE EVERY 2 MINUTE STARTS CURRENT_TIMESTAMP + INTERVAL 2 MINUTE DO BEGIN INSERT INTO archived_movies SELECT * FROM movies WHERE release_date < YEAR(CURDATE()) - 20; DELETE FROM movies WHERE release_date < YEAR(CURDATE()) - 20; END; ``` ## TRIGGER ```sql -- BEFORE : INSERT, UPDATE, DELETE -- AFTER : INSERT, UPDATE, DELETE SHOW TRIGGERS; DROP TRIGGER after_movie_update; TRUNCATE records; CREATE TRIGGER after_movie_update AFTER UPDATE ON movies FOR EACH ROW BEGIN DECLARE changes TINYTEXT DEFAULT ''; IF NEW.title <> OLD.title THEN SET changes = CONCAT('title changed ', OLD.title, '->', new.title, '\n'); END IF; IF NEW.budget <> OLD.budget THEN SET changes = CONCAT(changes, 'Budget changed ', old.budget, '->', new.budget); END IF; INSERT INTO records (changes) VALUES (changes); END; ``` ## SELECT - `SELECT (column 명) FROM (table)` : 특정 Column 값을 보여준다. - `Table`의 모든 `Column`을 출력할려면 `*` 기호를 사용하면 된다. - **Table의 값이 출력하지 않더라도 SELECT를 사용하기 위해선 TABLE을 꼭 명시에 주어야 한다.** - 만약 테이블이 주어지지 않는다면 `SQL`에서 기본으로 제공하는 `information_schema.tables`을 사용할 수 있다. ```SQL SELECT * FROM information_schema.tables ``` ## WHERE - `WHERE 조건` : Column 안에서 특정 값만 추출 할 때 사용한다. - 조건(>, >=, =, AND, OR, NOT )등이 있다. ### BETWEEN - `BETWEEN`을 통해 `<= AND <=` 을 간결하게 표현 가능하다. ```SQL SELECT * FROM Customers WHERE CustomerID BETWEEN 10 AND 20 ``` ### LIKE - `LIKE`을 통해 문자열의 패턴을 검색할 수 있다. - EX) 'KO_' -> 하나의 문자만 해당되는 - EX) 'KO%' -> 모든 문자만 해당되는 ```SQL SELECT * FROM Customers WHERE Country LIKE 'New%' ``` ### RLIKE (Regular like) - 문자열 패턴을 정규표현식으로 표현하여 비교한다. #### 정규표현식 - `a-z` : 소문자 알파벳 - `A-Z`: 대문자 알파벳 - `^` : 정규 표현식 시작 표시 - `[]` : 범위 설정 - `+` : 조건 잇기 - `
: 정규 표현식 종료 표시 (**설정 하지 않으면 뒤에 문자열이 추가될 수 있음**) - `*` : 직전의 조건 범위를 충족하는 문자의 개수가 0개 이상이다. - `\\` : 이스케이프 문자 ```sql SELECT user_id, name, mail FROM Users WHERE mail RLIKE '^[a-zA-Z][a-zA-Z0-9_.-]*+@leetcode\\.com ``` ### IN - `IN`을 통해 여러 이산 값 조건을 할 수 있다. (특정 값들 중 하나인지 아닌지 판단한다) - [[MySQL#Sub Query|Sub Query]]의 조건을 걸기 위해 사용할 경우 순서가 **Sub Query -> Main Query**이다. ```SQL SELECT * FROM Customers WHERE Country = IN('New york', 'London', 'Vancouver') ``` ### EXISTS * - [[MySQL#Sub Query|Sub Query]]에 데이터가 존재하는지 체크하고 **존재할 경우 TRUE를 반환**한다. - 순서는 **Main Query -> Sub Query**이다. ## Sub Query - `Sub Query`를 추가해 조건을 넣을 수 있다. - **단 서브 쿼리의 결과가 둘 이상**이라면 **IN이나 EXISTS, ANY, ALL**등을 사용하여 한다. ```SQL SELECT * FROM Customers WHERE Country = (SELECT Country FROM Customers WHERE CustomerID = 30) ``` ### ANY - `Sub Query`의 결과 값 중 어떤 것이든 만족시키는 값을 찾을 때 사용한다. ```SQL SELECT * FROM Customers WHERE Country > ANY (SELECT Country FROM Customers WHERE CustomerID > 30) ``` ### ALL - `Sub Query`의 모든 결과값을 만족시키는 값을 찾을 때 사용한다.. ```SQL SELECT * FROM Customers WHERE CustomerID > ALL (SELECT CustomerID FROM Customers WHERE Country = 'Germany') ``` ## AS - `Column` 명이나 `Sub Query`명 등 여러 이름을 지정할 때 사용한다. - [[MySQL#SELECT|SELECT]] `Column` 명을 바꾸면 이를 출력할 때 `Column 제목`이 바뀐다. ```SQL SELECT CustomerID AS ID FROM Customers ``` ## ORDER BY - `특정 Column`을 기준으로 정렬하여 출력 할 수 있다. - ASC : 오름차순(생략가능), DESC : 내림차순 ```SQL SELECT * FROM Customers ORDER BY ID DESC, Country ``` ## CASE WHEN THEN ELSE END - `Column`의 값을 조건에 맞춰 특정 값로 바꿔 출력하려 할 때 사용한다. - `CASE`와 `END` 형식을 취하며 - `WHEN`으로 조건으로 걸고 해당 조건이 맞으면 `THEN`으로 원하는 값을 출력한다. - `WHEN`은 여러 번 사용할 수 있으며 모든 조건이 맞지 않을 시 `ELSE`에 있는 값이 출력된다. ```SQL SELECT BOARD_ID, WRITER_ID, TITLE, PRICE, CASE WHEN STATUS = 'SALE' THEN '판매중' WHEN STATUS = 'RESERVED' THEN '예약중' ELSE '거래완료' END AS STATUS FROM USED_GOODS_BOARD WHERE CREATED_DATE = '2022-10-05' ORDER BY BOARD_ID DESC; ``` ## DISTINCT - `전체 Column`의 중복된 내용을 제거한다. ```SQL SELECT DISTINCT CustomerID, Country FROM Customers ORDER BY Country ``` ## LIMIT - `상위 N`개만 선별한다. - [[MySQL#ORDER BY|ORDER BY]]와 결합하여 **최대 값**이나 **최소 값**을 가져올 수 있다. ```SQL SELECT DISTINCT Country FROM Customers ORDER BY Country DESC LIMIT 5 ``` ## GROUP BY - 같은 Column 값들 끼리 묶어서 보여준다. - **다른 Column 값**을 보기 위해선 **집계 함수**를 사용해야 한다. - AVG() : 평균 - MAX() : 최대값 - MIN() : 최소값 - COUNT() : 개수 - COUNT(DISTINCT) : 중복 제외 개수 - STDEV(): 표준편차 - VARIANCE(): 분산 ```SQL SELECT City, MAX(CustomerID) AS AVERage FROM Customers GROUP BY City ``` - 만약 `GROUP`으로 묶이지 않은 다른 값을 출력하려 한다면 오류가 발생한다. ### GROUP_CONCAT - `GROUP`별로 문자를 합칠 때 사용한다. ```sql SELECT sell_date, COUNT(DISTINCT product) AS num_sold, GROUP_CONCAT(DISTINCT PRODUCT ORDER BY PRODUCT) AS products FROM Activities GROUP BY sell_date ``` ### HAVING - [[MySQL#WHERE|WHERE]]과 비슷한 개념으로 조건을 제한한다. - **집계 함수**에 대해서 조건 제한하는 개념이다. - 여러 Column의 조합의 조건으로 Table을 조회하기 위해서는 **GROUP BY와 HAVING을 사용해야 한다.** - **반드시** `GROUP BY` 뒤에 나와야 한다. ```SQL SELECT City, MAX(CustomerID) AS MAXid FROM Customers GROUP BY City HAVING MAX(CustomerID) > 20 ``` ## PARTITION BY * PARTITION을 이용해 그룹 안 값 관리하기 ```SQL SELECT MAX(CASE WHEN OCCUPATION = 'Doctor' THEN NAME END), MAX(CASE WHEN OCCUPATION = 'Professor' THEN NAME END), MAX(CASE WHEN OCCUPATION = 'Singer' THEN NAME END), MAX(CASE WHEN OCCUPATION = 'Actor' THEN NAME END) FROM ( SELECT *, RANK() OVER (PARTITION BY OCCUPATION ORDER BY NAME) RN FROM OCCUPATIONS ) AS TEMP GROUP BY RN ``` ## IFNULL - `IFNULL(COLUMN, 대채 값)`을 통해 `COLUMN` 값이 `NULL`일 때 다른 값을 반환할 수 있다. ```SQL SELECT IFNULL(City, "EMPTY") FROM Customers ``` ## ROW_NUMBER - `Coulmn`의 값을 순차적으로 번호를 매길 때 사용한다. - `OVER(ORDER BY 정렬)`과 함께 써서 어떠한 순서대로 번호를 매길지 정해야 한다. ```SQL SELECT City, ROW_NUMBER() OVER(ORDER BY CustomerID) FROM Customers ``` ## RANK - `Coulmn`의 값을 순차적으로 랭킹을 매길 때 사용한다. - `OVER(ORDER BY 정렬)`과 함께 써서 어떠한 순서대로 랭킹을 매길지 정해야 한다. `RNAK()` : 순차적으로 번호 부여(동일 번호 다음엔 순위를 건너뛰고 부여) `DENSE_RANK()` : 순차적으로 번호 부여(동일 번호 다음엔 다음 순위 부여) `PERCENT_RANK()` : 순차적으로 상위 % 부여 ```SQL SELECT ROUND(AVG(A.LAT_N), 4) FROM ( SELECT LAT_N, PERCENT_RANK() OVER(ORDER BY LAT_N) AS P_RANK FROM STATION ) AS A WHERE A.P_RANK = 0.5 ``` ## SET - `SET`을 통해 변수 설정할 수 있다. - 변수 명은 `@`가 들어가야 한다. - `SET` 끝에는 `;`이 들어가야 한다. - 변수 안에 값을 넣을 때는 `:=`을 사용하여야 한다. ```SQL SET @hour = -1; SELECT (@hour := @hour+1) AS HOUR, ( SELECT COUNT(*) FROM ANIMAL_OUTS WHERE HOUR(DATETIME) = @hour ) AS COUNT FROM ANIMAL_OUTS WHERE @hour < 23 ORDER BY HOUR ``` ## JOIN * ### INNER JOIN ### LEFT JOIN ### RIGHT JOIN ## UNION * ## RECURSIVE - `RECURSIVE`을 활용하여 특정 `Table`을 반복시켜 변환할 수 있다. - [[MySQL#UNION|UNION]]을 통해 합쳐나가야 하며 **더 이상 합쳐지지 못할 때(NULL)** 재귀가 끝난다. ```SQL WITH RECURSIVE GENERATIONS AS ( SELECT ID, PARENT_ID, 1 AS GENERATION FROM ECOLI_DATA WHERE PARENT_ID IS NULL UNION ALL SELECT C.ID, C.PARENT_ID, G.GENERATION+1 FROM GENERATIONS AS G JOIN ECOLI_DATA AS C ON G.ID = C.PARENT_ID ) SELECT COUNT(*) AS COUNT, A.GENERATION FROM GENERATIONS AS A WHERE A.ID NOT IN( SELECT DISTINCT B.PARENT_ID FROM GENERATIONS AS B WHERE B.PARENT_ID IS NOT NULL ) GROUP BY A.GENERATION ORDER BY A.GENERATION ``` ## MYSQL 내장 함수 ### 문자열 함수 - `LENGTH()` : 문자열 길이 반환한다. - `CONCAT('a','b','c')` : 여러 문자열을 합친다. 만약 넣은 문자열 중 하나라도 `NULL`이면 `NULL`을 반환한다. - `LOCATE('target', 'string')` : 첫 번째 문자열이 두 번째 문자열의 어느 번째에 있는지 알려준다. (**인덱스가 1부터 시작임**) - `LEFT('string', 'int')`, `RIGHT('string', 'int')` : 왼쪽, 오른쪽에서 지정한 개수 만큼 추출함. - `LOWER()`, `UPPER()` : 대소문자로 변환한다. - `REPLACE('string1', 'string2', 'string3')` : 첫 번째 문장 중 두 번째 문자를 찾아 세 번째로 바꾼다. - `TRIM()` : 특정 문자열을 제거한다. (기본 설정 양쪽) - `TRIM(LEADING 'target' FROM 'string')` : 문자열의 앞쪽에 있는 특정 문자열을 제거한다. - `TRIM(TRALING 'target' FROM 'string')` : 문자열의 뒤쪽에 있는 특정 문자열을 제거한다. - `FORMAT(string, point)` : 데이터를 세 자리마다 쉼표를 사용하는 형식으로 변환 (**point는 소수점을 어느자리까지 표헌할지 정함**) - `SUBSTRING(string, position, length)` : 해당 문장의 position위치에서 지정한 길이(length)까지 부분 문자열을 반환한다. - `REPEAT(string, length)` : 해당 문장을 길이만큼 반복해 반환한다. ### 수학 함수 - `FLOOR()` : 내림 - `CEIL()` : 올림 - `ROUND(n1,n2)` : 반올림 (n2 : 소수점 몇자리 기준으로 반올림 할지 정한다.) - `TRUNCATE(숫자,버릴 자릿수)` : 숫자를 버릴 자릿수 아래로 버림(-일 경우 정수자리수 부분이다.) - `SQRT()` : 양의 제곱근 - `POW(n1,n2)` : 첫 번째 수로 밑 두 번째 수가 지수 - `EXP()` : e의 거듭제곱값 - `LOG()` : 자연로그값 - `PI()` : 파이값 - `SIN()` : 사인값 - `COS()` : 코사인값 - `TAN()` : 탄젠트값 - `ABS()` : 절대값 - `RAND()` : 0.0보다 크거나 같고 1.0보다 작은 하나의 실수 무작위 생성 ### 날짜 함수 - `NOW()` : 지금 날짜 반환(**YYYY-MM_DD HH:MM:SS로 변환**) - `CURDATE()` : 지금 날짜 변환(**YYYY-MM_DD로 변환**) - `CURTIME()` : 지금 시각 변환(**HH:MM:SS로 변환**) - `DATE(DATE)` : 전달받은 값에 해당하는 날짜 정보 반환 - `YEAR(DATE)` : 전달받은 값에 해당하는 월 정보 반환 - `MONTH(DATE)` : 전달받은 값에 해당하는 월 정보 반환 - `HOUR(DATE)` : 전달받은 값에 해당하는 시간 정보 반환 - `MINUTE(DATE)` : 전달받은 값에 해당하는 분 정보 반환 - `SECOND(DATE)` : 전달받은 값에 해당하는 초 정보 반환 - `MONTHNAME(DATE)`: 월에 해당하는 이름 반환 - `DAYNAME(DATE)`: 날짜에 해당하는 요일 반환 - `DAYOFWEEK(DATE)` : 일자가 해당 주에 몇번째 날인지를 반환 (일요일 = 1, 토요일 = 7) - `DAYOFMONTH(DATE)` : 일자가 해당 월에 몇 번째 날인지를 반환 (1~31) - `DAYOFYEAR(DATE)` : 일자가 해당 연도에서 몇번째 날인지를 반환(1~366) - `DATE_FORMAT(DATE, 'FORMAT')` : 특정 날짜를 포멧에 맞춰 출력 - `%Y` :4자리 년도 | `%y`: 2자리 년도 - `%m` : 숫자 월 ( 두자리 ) | `%M` : 긴 월(영문) | `%c` : 숫자 월(한자리는 한자리)  | `%b` : 짧은 월(영문)  - `%d` : 일자 (두자리)  | `%e` : 일자(한자리는 한자리)  - `%W` : 긴 요일 이름(영문)  | `%a` : 짧은 요일 이름(영문)  - `%I` : 시간 (12시간) | `%H`: 시간(24시간)  - `%i` : 분  - `%S` : 초 - `%r` : hh:mm:ss AM,PM  - `%T` : hh:mm:SS ```SQL SELECT DATE_FORMAT(NOW(),'%Y-%m-%d') AS DATE FROM DUAL ``` - `DATEDIFF(END_DATE, START_DATE)`로 두 DATE의 날짜 차이를 구할 수 있다. - `EXTRACT(month FROM COLUMN)`로 해당 DATE의 원하는 값을 가져올 수 있다. --- `FROM절/WHERE절 서브쿼리` `DISTINCT()` `like %` `COALESCE()` `IFNULL()` `DATE_FROMAT()` `CASE WHEN then ELSE` `LEFT()` `SUBSTR()` `JOIN` `UNION` `LIMIT` `TIMESTAMPDIFF()` `DISTINCT` `set` `REGEXP` `DATEDIFF` `RECURSIVE` `RIGHT()` `HAVING` `TRUNCATE()` `MID()` `ROUND()` `AVG()` `CONCAT()` `LPAD()` `RPAD()` `FLOOR()` `REPEAT` `REPLACE` `PARTITION BY` `RANK` `DENSE_RANK` `ROW_NUMBER` `CTE`, `GROUP_CONCAT` ```mysql WITH sub1 AS ( SELECT Start_Date, RANK() OVER(ORDER BY Start_Date) AS R FROM Projects WHERE Start_Date NOT IN (SELECT End_Date FROM Projects) ), sub2 AS ( SELECT End_Date, RANK() OVER(ORDER BY End_Date) AS R FROM Projects WHERE End_Date NOT IN (SELECT Start_Date FROM Projects) ) SELECT sub1.Start_Date, sub2.End_Date FROM sub1 JOIN sub2 ON sub1.R = sub2.R ORDER BY DATEDIFF(sub2.End_Date, sub1.Start_Date), sub1.Start_Date ``` ```mysql SET @NUM1 = 1; SET @NUM2 = 1; SELECT GROUP_CONCAT(NUM1 SEPARATOR '&') FROM ( SELECT @NUM1 := @NUM1+1 AS NUM1 FROM information_schema.tables AS t1,information_schema.tables AS t2 ) AS TEMP1 WHERE NUM1 <= 1000 AND NOT EXISTS ( SELECT * FROM ( SELECT @NUM2 := @NUM2+1 AS NUM2 FROM information_schema.tables AS t1,information_schema.tables AS t2 LIMIT 1000 ) AS TEMP2 WHERE MOD(NUM1, NUM2) = 0 AND NUM1 != NUM2 ) ```