이 글은 MySQL 기반의 SELECT, JOIN, SubQuery 문제에 대한 정리를 바탕으로 작성했습니다.
해당 문제는 sakila DB를 기반으로 풀었습니다.
설치 과정에 대한 내용은 Installation이며,
sakila DB를 다운받는 위치는 Other MySQL Documentation 안에 Example Databases - sakila database(ZIP)
다운받으면 됩니다.
위의 sakila DB 다운받은 후에 MySQL Workbench
를 실행한 다음, 해당 파일(4.exercise)에서 문제를 풀었습니다.
문제를 풀기 전에 sakila DB
를 사용하는 명령어를 입력한다.
use sakila;
이 글은 MySQL 기반의 SELECT, JOIN 문제에 대한 정리를 바탕으로 작성했습니다.
해당 문제는 sakila DB를 기반으로 풀었습니다.
설치 과정에 대한 내용은 Installation이며,
sakila DB를 다운받는 위치는 Other MySQL Documentation 안에 Example Databases - sakila database(ZIP)
다운받으면 됩니다.
위의 sakila DB 다운받은 후에 MySQL Workbench
를 실행한 다음, 해당 파일(4.exercise)에서 문제를 풀었습니다.
문제를 풀기 전에 sakila DB
를 사용하는 명령어를 입력한다.
use sakila;
Q1. actor 테이블에서 전체 컬럼(열) 조회 -> 실행 결과 행의 수는 총 200개
SELECT * FROM actor LIMIT 200;
Q2. actor 테이블에서 first_name, last_name 컬럼 조회 -> 실행 결과 행의 수는 총 200개
SELECT first_name, last_name FROM actor LIMIT 200;
Q3. actor 테이블에서 first_name과 last_name을 하나로 연결(concat)하여 Actor Name이라는 컬럼명으로 조회하고, 전부 대문자로 조회 -> 실행 결과 행의 수는 총 200개
SELECT concat(first_name, '+' ,last_name) AS Actor_Name FROM actor;
Q4. actor 테이블에서 actor_id, first_name, last_name을 조회하되, first_name이 Joe인 데이터만 필터링하여 조회 -> 실행 결과 행의 수는 1개
SELECT actor_id, first_name, last_name
FROM actor
WHERE first_name = 'Joe';
Q5. actor 테이블에서 actor_id, first_name, last_name을 조회하되, last_name 에 Gen이 포함된 actor를 필터링하여 조회 (last_name의 맨 앞, 맨 뒤, 중간 등 어느 부분에 포함되어도 상관없이 전체 조회) -> 실행 결과 행의 수는 총 4개
SELECT actor_id, first_name, last_name
FROM actor
WHERE last_name LIKE '%Gen%';
Q6. actor 테이블에서 actor_id, first_name, last_name을 조회하되, last_name에 LI(엘, 아이)가 포함된 데이터를 필터링하고, last_name, first_name 순서로 오름차순 정렬하여 조회 -> 실행 결과 행의 수는 총 10개
SELECT actor_id, first_name, last_name
FROM actor
WHERE last_name LIKE '%LI%'
ORDER BY last_name, first_name ASC;
Q7. country 테이블에서 country_id, country 열을 조회하되,
IN 연산자
를 활용하여 country가 Afghanistan, Bangladesh, China 중에 속하는 데이터만 필터링하여 조회 -> 실행 결과 행의 수는 총 3개
SELECT country_id, country
FROM country
WHERE country IN('Afghanistan', 'Bangladesh', 'China');
Q8. actor 테이블에서 서로 동일한 last_name을 사용하는 배우(actor)들이 각각 몇 명인지 조회하고 싶을 때, actor 테이블에서 last_name 컬럼과 해당 last_name을 사용하는 사람의 인원 수를 집계해주는 컬럼을 조회 ex) 아래의 이미지를 참고하면, last_name으로 ALLEN을 사용하는 배우(actor)는 총 3명(2번째 행) https://guguttemy.speedgabia.com/DB/dml_practice8.png -> 8번 문제 실행 결과 행의 수는 총 121개
SELECT last_name, count(last_name)
FROM actor
GROUP BY last_name;
Q9. actor 테이블에서 last_name 컬럼과 해당 last_name을 사용하는 수를 집계해주는 컬럼을 조회하되, 집계되는 컬럼의 별칭은
Count of Last Name
이라고 짓고, last_name 카운트가 2 초과인 그룹만 필터링하여 조회 -> 실행 결과 행의 수는 총 20개
SELECT last_name, count(last_name) AS "Count of Last Name"
FROM actor
GROUP BY last_name
HAVING count(last_name) > 2;
Q10. address 테이블의 정보(description) 조회
SELECT * FROM address;
Q11. address 테이블의 총 행 수 조회 -> 실행 결과 행의 수는 총 603개
SELECT count(*) FROM address;
Q12. address 테이블의 가상 상위 데이터 5개만 제한(LIMIT)하여 조회 -> 실행 결과 행의 수는 총 5개
SELECT * FROM address ORDER BY address_id LIMIT 5;
Q13. staff 테이블의 별칭을 s, address 테이블의 별칭을 a로 짓고, 두 테이블을 연결(JOIN)하여 address_id가 일치하는 first_name, last_name, address를 조회 -> 실행 결과 행의 수는 총 2개
SELECT first_name, last_name, address
FROM staff c JOIN address a
WHERE c.address_id = a.address_id;
Q14. staff 테이블의 별칭을 s, payment 테이블의 별칭을 p로 짓고, 두 테이블을 연결(JOIN)하여 staff_id가 일치하는 조건의 staff_id, first_name, last_name 및 amount의 총 금액(sum) 컬럼을 조회하되 payment_date가 2005-08-01 00:00:00 이후이고, 2005-08-02 00:00:00 ‘미만’인 데이터만 필터링하여 staff_id를 기준으로 묶어서(grouping) 조회** -> 실행 결과 행의 수는 총 2개
SELECT s.staff_id, first_name, last_name, sum(amount)
FROM staff s JOIN payment p ON s.staff_id = p.staff_id
WHERE p.payment_date BETWEEN '2005-08-01 00:00:00' AND '2005-08-02 00:00:00'
GROUP BY staff_id;
Q15. film 테이블의 별칭을 f, film_actor 테이블의 별칭을 fa로 짓고, 두 테이블을 연결(JOIN)하여 각 film_id가 일치하는 조건의 title 및 해당 film에 참여한 총 actor의 수를 의미하는 컬럼 ‘총 배우 수’ 컬럼을 film 테이블의 title 별로(grouping) 조회 (단, 이대로 조회하면 결과 데이터가 총 997행이기 때문에 상위 20개의 행만 제한하여 조회) -> 실행 결과 행의 수는 총 20개(로 제한, LIMIT 사용)
SELECT title, actor
FROM film f JOIN film_actor fa
WHERE f.film_id = fa.film_id
LIMIT 20;
Q16. inventory 테이블의 정보(description) 조회
SELECT * FROM inventory;
Q17. inventory 테이블의 데이터 상위 10개 조회 ->실행 결과 행의 수는 총 10개
SELECT * FROM inventory LIMIT 10;
Q18. film 테이블에서 title, description 컬럼을 조회하되, 상위 20개만 조회 -> 실행 결과 행의 수는 총 20개
SELECT title, description
FROM film LIMIT 20;
Q19. ALABAMA DEVIL film이 모든 영화 대여점에 총 몇 개의 복제본(영화 필름)이 배포되어있는지 알고 싶을 때, film 테이블의 별칭을 f, inventory 테이블의 별칭을 i로 짓고, 두 테이블을 연결(JOIN)하여 film_id 컬럼이 일치하는 조건의 title 및 film_id의 총 개수(count)를 ‘복제본’으로 별칭을 작성하여 title 별로 조회하되, title이 ‘ALABAMA DEVIL’인 film만 조회 -> 실행 결과 행의 수는 1개
LECT title, count(f.film_id) AS '복제본'
FROM film f JOIN inventory i
WHERE f.film_id = i.film_id AND title = 'ALABAMA DEVIL';
Q20. 고객 별 총 사용 금액을 last_name을 오름차순 정렬하여 조회하고 싶을 때, customer 테이블의 별칭을 c, payment 테이블의 별칭을 p로 짓고, 두 테이블을 customer_id컬럼으로 연결(JOIN)하여 first_name, last_name, amount의 총 액수를 조회하되, first_name, last_name 순으로 묶어서(grouping) last_name을 기준으로 오름차순하여 조회 -> 실행 결과 행의 수는 599개
SELECT first_name, last_name, sum(amount)
FROM customer c JOIN payment p
GROUP BY first_name, last_name
ORDER BY last_name ASC;
해당 글은 MacOS M1 기반으로 MySQL, Workbench 설치하는 과정을 간략하게 작성했습니다.
brew install mysql
➜ ~ brew install mysql
==> Downloading https://formulae.brew.sh/api/formula.jws.json
#=#=#
==> Downloading https://formulae.brew.sh/api/cask.jws.json
#=#=#
==> Fetching dependencies for mysql: icu4c, ca-certificates, openssl@1.1, libevent, libcbor, libfido2, lz4, protobuf@21, zlib, xz and zstd
==> Fetching icu4c
==> Downloading https://ghcr.io/v2/homebrew/core/icu4c/manifests/72.1
######################################################################### 100.0%
// ... 중간 생략
==> Downloading https://ghcr.io/v2/homebrew/core/zstd/blobs/sha256:e3cb579108afe
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sh
######################################################################### 100.0%
==> Fetching mysql
==> Downloading https://ghcr.io/v2/homebrew/core/mysql/manifests/8.0.33_1
######################################################################### 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/mysql/blobs/sha256:e56237aaf649
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sh
######################################################################### 100.0%
==> Installing dependencies for mysql: icu4c, ca-certificates, openssl@1.1, libevent, libcbor, libfido2, lz4, protobuf@21, zlib, xz and zstd
==> Installing mysql dependency: icu4c
==> Pouring icu4c--72.1.arm64_monterey.bottle.tar.gz
🍺 /opt/homebrew/Cellar/icu4c/72.1: 263 files, 78.4MB
==> Installing mysql dependency: ca-certificates
==> Pouring ca-certificates--2023-05-30.arm64_monterey.bottle.tar.gz
==> Regenerating CA certificate bundle from keychain, this may take a while...
🍺 /opt/homebrew/Cellar/ca-certificates/2023-05-30: 3 files, 216.2KB
==> Installing mysql dependency: openssl@1.1
==> Pouring openssl@1.1--1.1.1u.arm64_monterey.bottle.tar.gz
// ... 중간 생략
🍺 /opt/homebrew/Cellar/zstd/1.5.5: 31 files, 2.3MB
==> Installing mysql
==> Pouring mysql--8.0.33_1.arm64_monterey.bottle.tar.gz
==> /opt/homebrew/Cellar/mysql/8.0.33_1/bin/mysqld --initialize-insecure --user=
==> Caveats
We've installed your MySQL database without a root password. To secure it run:
mysql_secure_installation
MySQL is configured to only allow connections from localhost by default
To connect run:
mysql -u root
To start mysql now and restart at login:
brew services start mysql
Or, if you don't want/need a background service you can just run:
/opt/homebrew/opt/mysql/bin/mysqld_safe --datadir=/opt/homebrew/var/mysql
==> Summary
🍺 /opt/homebrew/Cellar/mysql/8.0.33_1: 318 files, 300.0MB
==> Running `brew cleanup mysql`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
==> Upgrading 1 dependent of upgraded formulae:
Disable this behaviour by setting HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
openssl@3 3.1.0 -> 3.1.1
==> Fetching openssl@3
==> Downloading https://ghcr.io/v2/homebrew/core/openssl/3/manifests/3.1.1
######################################################################### 100.0%
==> Downloading https://ghcr.io/v2/homebrew/core/openssl/3/blobs/sha256:e2a9c60c
==> Downloading from https://pkg-containers.githubusercontent.com/ghcr1/blobs/sh
######################################################################### 100.0%
==> Upgrading openssl@3
3.1.0 -> 3.1.1
==> Pouring openssl@3--3.1.1.arm64_monterey.bottle.tar.gz
🍺 /opt/homebrew/Cellar/openssl@3/3.1.1: 6,495 files, 28.4MB
==> Running `brew cleanup openssl@3`...
Removing: /opt/homebrew/Cellar/openssl@3/3.1.0... (6,494 files, 28.4MB)
Removing: /Users/junyongmoon/Library/Caches/Homebrew/openssl@3--3.1.0... (7.6MB)
==> Checking for dependents of upgraded formulae...
==> No broken dependents found!
==> Caveats
==> mysql
We've installed your MySQL database without a root password. To secure it run:
mysql_secure_installation
MySQL is configured to only allow connections from localhost by default
To connect run:
mysql -u root
To start mysql now and restart at login:
brew services start mysql
Or, if you don't want/need a background service you can just run:
/opt/homebrew/opt/mysql/bin/mysqld_safe --datadir=/opt/homebrew/var/mysql
mysql --version
➜ ~ mysql --version
mysql Ver 8.0.33 for macos12.6 on arm64 (Homebrew)
mysql.server start
➜ ~ mysql.server start
Starting MySQL
. SUCCESS!
mysql_secure_installation
질문 리스트
``` ➜ ~ mysql_secure_installation
Securing the MySQL server deployment.
Connecting to MySQL using a blank password.
VALIDATE PASSWORD COMPONENT can be used to test passwords and improve security. It checks the strength of password and allows the users to set only those passwords which are secure enough. Would you like to setup VALIDATE PASSWORD component?
Press y|Y for Yes, any other key for No: n Please set the password for root here.
New password:
Re-enter new password: By default, a MySQL installation has an anonymous user, allowing anyone to log into MySQL without having to have a user account created for them. This is intended only for testing, and to make the installation go a bit smoother. You should remove them before moving into a production environment.
Remove anonymous users? (Press y|Y for Yes, any other key for No) : y Success.
/**
* bridge_length : 다리에 올라갈 수 있는 트럭 수
* weight : 다리가 견딜 수 있는 무게
* truck_weights : 트럭 별 무게
*/
import java.util.*;
class Solution {
public int solution(int bridge_length, int weight, int[] truck_weights) {
Queue<Integer> queue = new LinkedList<>();
int sum = 0; // 현재 다리를 건너는 트럭 무게의 합
int time = 0; // 모든 트럭이 다리를 건너는 최소의 시간
for(int i = 0; i < truck_weights.length; i++) { // 향상된 for문을 쓰는게 좋을 것
int truck = truck_weights[i];
while(true) {
// [1] 큐가 비어있는 경우 = 어떠한 트럭도 다리위에 없는 경우
if(queue.isEmpty()) {
queue.add(truck);
sum += truck;
time++; // 다리에 오를 때만 시간 추가
break;
} else if(queue.size() == bridge_length) { // [2] 큐가 가득찬 경우
sum -= queue.poll();
} else { // [3] 큐가 다리 길이만큼 가득 차지 않는 경우
// weight 값을 넘지 않는 선에서 새로운 트럭을 다리에 올려줌
if(sum + truck <= weight) {
queue.add(truck);
sum += truck;
time++;
break;
} else {
// weight 값을 넘는다면, 0을 넣어 이미 큐에 있는 트럭이 다리를 건너게 만듬
queue.add(0);
time++;
}
}
}
}
// 마지막 트럭에서 반복문이 끝나는데, 마지막 역시 다리 길이만큼 지나가야 하기 때문에, 다리 길이만큼 더해준다.
return time + bridge_length;
}
}
트럭 여러 대가 강을 가로지르는 일차선 다리를 정해진 순으로 건너려 한다. => 큐
를 사용한다.
큐를 다리라고 생각하고, 조건에 맞게 트럭을 큐에 넣고 빼면서 최소 몇 초가 걸리는 지 return 하면 되는 문제였다.
고려해야할 사항
다리에는 트럭이 최대 bridge_length
대 올라갈 수 있고, 다리는 weight
이하까지의 무게를 견딜 수 있다.
다리에 완전히 오르지 않는 트럭의 무게는 무시한다 == 다리를 건너는 트럭의 무게만 측정한다 => 같은 의미.
트럭이 다리에 올라가면 1초가 시작되고, 다리 위에서 1칸씩 움직일 때마다 역시 1초가 흘러간다.
다리에 트럭을 넣는 조건은 총 3가지다.
[1] 큐가 비어있는 경우 -> 트럭을 다리에 올려준다 (시간은 +1 증가)
[2] 큐가 가득찬 경우 -> 이때는 가장 앞에 넣은 트럭이 다리의 끝에 도달했다는 의미이므로 poll() 메서드를 이용해 트럭을 꺼내 줌으로써, 다리를 건너가도록 한다. 이때 다리에서 내릴 때는 시간이 들지 않는다.
[3] 큐가 다리 길이만큼 가득 차지 않는 경우 -> 큐에 이미 있는 트럭에 다리를 지나갈 수 있도록 0 값을 넣어준다. (시간은 +1 증가)
이 과정을 전체 트럭의 개수만큼 반복한다.
위의 반복문의 특성상 마지막 트럭의 경우 다리에는 올랐지만 다 건너지는 못한다. 그래서 정답을 출력할 때는 지금까지 걸린 time에서 마지막 트럭이 건너는데 걸리는 시간인 다리의 길이
를 더해서 출력하면 된다.
// 다리를 지나는 트럭(23.12.18) - 1차 복습
import java.util.*;
class Solution {
public int solution(int bridge_length, int weight, int[] truck_weights) {
int sum = 0; // 현재 다리를 지나는 트럭 무게의 합
int time = 0; // 모든 트럭이 다리를 건너는 최소 시간의 합(걸린 시간)
Queue<Integer> bridgeQueue = new LinkedList<>(); // 다리를 지나기 전 트럭
for(int truck : truck_weights) {
while(true) {
// 1. 큐가 비어있는 경우 트럭 추가
if(bridgeQueue.isEmpty()) {
bridgeQueue.offer(truck);
sum += truck;
time++; // 다리에 오를 때만 시간 추가
break;
}
// 2. 다리에 건너는 트럭 무게의 합 == 견딜 수 있는 최대무게(weight)
else if(bridgeQueue.size() == bridge_length) {
sum -= bridgeQueue.poll();
}
// 3. 큐가 비어있지 않은 경우
else {
// 2-2. (다음 트럭까지 포함하여) 다리에 건너는 트럭 무게의 합 > 견딜 수 있는 최대 무게
if(sum + truck > weight) {
bridgeQueue.offer(0);
time++;
}
// 2-3. (다음 트럭까지 포함하여) 다리에 건너는 트럭 무게의 합 < 견딜 수 있는 최대 무게
else {
bridgeQueue.offer(truck);
sum += truck;
time++;
break;
}
}
}
}
// 걸린 시간 + 마지막 트럭의 통과 시간
return time + bridge_length;
}
}
큐를 이용해서 문제를 풀어야 겠다는 생각은 가졌지만, 경우의 수를 생각하는 데 있어서 어려움이 있었다.
단순히 머리로만 생각하지말고, 뭔가 생각할 게 많다고 생각하면 그림을 그리면서 풀어가는 습관을 가지도록 하자.
import java.util.*;
class Solution {
public int[] solution(int[] prices) {
int[] answer = new int[prices.length];
for(int i = 0; i < answer.length; i++) {
for(int j = i + 1; j < answer.length; j++) {
answer[i]++;
if(prices[i] > prices[j]) break;
}
}
return answer;
}
}