데이터 생성

CREATE DATABASE test_01;
USE test_01;

CREATE TABLE tCity
(
	name CHAR(10) PRIMARY KEY,
	area INT NULL ,
	popu INT NULL ,
	metro CHAR(1) NOT NULL,
	region CHAR(6) NOT NULL
);

INSERT INTO tCity VALUES ('서울', 605,974, 'y', '경기');
INSERT INTO tCity VALUES ('부산', 765,342, 'y', '경상');
INSERT INTO tCity VALUES ('오산', 42,21, 'n', '경기');
INSERT INTO tCity VALUES ('청주', 940,83, 'n', '충청');
INSERT INTO tCity VALUES ('전주', 205,65, 'n', '전라');
INSERT INTO tCity VALUES ('순천', 910,27, 'n', '전라');
INSERT INTO tCity VALUES ('춘천', 1116,27, 'n', '강원');
INSERT INTO tCity VALUES ('홍천', 1819,7, 'n', '강원');

SELECT * FROM tCity;

CREATE TABLE tStaff
(
	name CHAR (15) PRIMARY KEY,
	depart CHAR (10) NOT NULL,
	gender CHAR(3) NOT NULL,
	joindate DATE NOT NULL,
	grade CHAR(10) NOT NULL,
	salary INT NOT NULL,
	score DECIMAL(5,2) NULL
);

INSERT INTO tStaff VALUES ('김유신', '총무부', '남', '2000-2-3', '이사', 420, 88.8);
INSERT INTO tStaff VALUES ('유관순', '영업부', '여', '2009-3-1', '과장', 380, NULL);
INSERT INTO tStaff VALUES ('안중근', '인사과', '남', '2012-5-5', '대리', 256, 76.5);
INSERT INTO tStaff VALUES ('윤봉길', '영업부', '남', '2015-8-15', '과장', 350, 71.25);
INSERT INTO tStaff VALUES ('강감찬', '영업부', '남', '2018-10-9', '사원', 320, 56.0);
INSERT INTO tStaff VALUES ('정몽주', '총무부', '남', '2010-9-16', '대리', 370, 89.5);
INSERT INTO tStaff VALUES ('허난설헌', '인사과', '여', '2020-1-5', '사원', 285, 44.5);
INSERT INTO tStaff VALUES ('신사임당', '영업부', '여', '2013-6-19', '부장', 400, 92.0);
INSERT INTO tStaff VALUES ('성삼문', '영업부', '남', '2014-6-8', '대리', 285, 87.75);
INSERT INTO tStaff VALUES ('논개', '인사과', '여', '2010-9-16', '대리', 340, 46.2);
INSERT INTO tStaff VALUES ('황진이', '인사과', '여', '2012-5-5', '사원', 275, 52.5);
INSERT INTO tStaff VALUES ('이율곡', '총무부', '남', '2016-3-8', '과장', 385, 65.4);
INSERT INTO tStaff VALUES ('이사부', '총무부', '남', '2000-2-3', '대리', 375, 50);
INSERT INTO tStaff VALUES ('안창호', '영업부', '남', '2015-8-15', '사원', 370, 74.2);
INSERT INTO tStaff VALUES ('을지문덕', '영업부', '남', '2019-6-29', '사원', 330, NULL);
INSERT INTO tStaff VALUES ('정약용', '총무부', '남', '2020-3-14', '과장', 380, 69.8);
INSERT INTO tStaff VALUES ('홍길동', '인사과', '남', '2019-8-8', '차장', 380, 77.7);
INSERT INTO tStaff VALUES ('대조영', '총무부', '남', '2020-7-7', '차장', 290, 49.9);
INSERT INTO tStaff VALUES ('장보고', '인사과', '남', '2005-4-1', '부장', 440, 58.3);
INSERT INTO tStaff VALUES ('선덕여왕', '인사과', '여', '2017-8-3', '사원', 315, 45.1);

연습문제

-- 1. tCity의 모든 값들을 확인하라.
select * from tCity;

-- 2.  tStaff의 모든 값들을 확인하라.
select * from tStaff;

-- 3. tCity의 도시 이름과 인구에 대한 정보를 확인하라.
select name, popu from tCity;

-- 4. tCity의 도시이름, 지역, 면적에 대한 정보를 확인하라.
select name, region, area from tCity;

-- 5.  tStaff의 이름과 월급에 대한 정보를 확인하라.
select name, salary from tStaff;

-- 6. 직원테이블에서 이름, 부서, 직급만 출력하라.
select name, depart, grade from tStaff;

-- 7. 도시테이블에서 도시명, 면접(제곱km) 인구(만명)으로 이름이 보이도록 출력하라.
select name `도시명`, area `면적(제곱km)`, popu `인구(만명)` from tCity;

-- 8. 도시테이블에서 name, popu 값에 10000을 곱해서 인구(명)으로 이름이 보이도록 출력하라.
select name, popu * 10000 `인구(명)` from tCity;

-- 9. 도시테이블에서 이름, 면적, 인구와 인구밀도라는 이름으로 (기존의 popu * 10000 / area 로 계산이 되는)것을 보고 나타내라.
select name `이름`, area `면적`, popu `인구`, popu * 10000 / area `인구밀도` from tCity;

-- 10. 도시테이블에서 면적이 1000제곱키로미터 이상인 도시만 출력하라.
select * from tCity where area >= 1000; 

-- 11. 도시테이블에서 면적이 1000제곱키로미터 이상인 도시의 이름과 면적을 출력하라.
select name, area from tCity where area >= 1000; 

-- 12. 인구가 10만명 미만의 도시의 이름을 출력하라.
select name from tCity where popu < 10;

-- 13. 전라도에 있는 도시의 정보를 출력하라.
select * from tCity where region = '전라';

-- 14. 월급이 400만원 이상인 직원의 이름을 출력하라.
select name from tStaff where salary >= 400;

-- 15. 스탭의 테이블에서 SCORE의 값이 NULL인 정보를 출력하라.
select * from tStaff where score is null;

-- 16. 스탭의 테이블에서 SCORE의 값이 있는 사람들의 정보를 출력하라.
select * from tStaff where score is not null;

-- 17. 도시테이블에서 인구가 100만이상이면서, 면적이 700제곱키로 이상인 도시를 찾아라.
select * from tCity where popu >= 100 and area >= 700;

-- 18. 도시테이블에서 경기권 도시 중에서 인구가 50만명 이상이거나 또는 경기원이 아니고 인구가 50만보다 적더라도 면적이 500이상인 도시를 찾아라.
select * from tCity where (region = '경기' and popu >= 50) or (region != '경기' and popu < 50 and area >= 500);

-- 19.  직원 목록에서 월급이 300미만이면서 성취도는 60 이상인 직원이 누구인지 찾아라.
select * from tStaff where salary < 300 and score >= 60;

-- 20. 영업무의 여직원 분들의 이름을 찾아라.
select * from tStaff where depart = '영업부' and gender = '여';

-- 21.  도시 이름에 ‘천’이 들어가는 도시들을 찾아라.
select * from tCity where name like '%천%';

-- 22. 직원 목록에서 성이 “정”씨인 사람들을 찾아라.
select * from tStaff where name like '정%';

-- 23.  이름에 “신”자가 포함된 직원을 찾아라.
select * from tStaff where name like '%신%';

-- 24.  인구가 50~100만 사이인 도시를 찾아라.
select * from tCity where popu between 50 and 100;

-- 25. 직원들 중에서 입사일이 2015년부터 2018년 사이의 분들을 찾아라.
select * from tStaff where joindate between '2015-01-01' and '2018-12-31';

-- 26. 면적인 50~1000사이의 도시의 목록을 조사하라.
select * from tCity where area between 50 and 1000;

-- 27. 월급이 200만원대의 직원들을 조사하라.
select * from tStaff where salary between 200 and 299.9999;

-- 28. 지역이 경상/전라인 모든 도시를 찾아라.
select * from tCity where region in ('경상', '전라');

-- 29. 인구가 적은 도시부터 출력하라.
select * from tCity order by popu;

-- 30. 지역, 도시이름, 면적, 인구에 대한 것을 지역과 도시 이름에 대해서 정렬하라.
select region, name, area, popu from tCity order by region, name;

-- 31. 면적에 의해서 도시들의 정보들을 정렬하라.
select * from tCity order by area desc;

-- 32.  도시이름을 인구수에 따라서 도시의 이름만 출력하라.
select name from tCity order by popu desc;

-- 33. 경기도에 있는 도시만 골라서 면적별로 그 도시의 정보들을 출력하라.
select * from tCity where region = '경기' order by area desc;

-- 34. 직원 목록을 월급이 적은 사람부터 순서대로 출력하되, 월급이 같다면 성취도가 높은 사람을 먼저 출력하라.
select * from tStaff order by salary asc, score desc;

-- 35. 영업부 직원을 먼저 입사한 순서대로 정렬하라.
select * from tStaff where depart = '영업부' order by joindate;

 

'SQL' 카테고리의 다른 글

10. [SQL] 연습문제 - EDA(1)  (0) 2024.04.09
09. [SQL] 연습문제 - JOIN  (0) 2024.04.09
07. [SQL] CASE WHEN ... END  (0) 2024.04.08
06. [SQL] 조인  (0) 2024.04.08
05. [SQL] SELECT문  (1) 2024.04.07

문제 설명

주차장의 요금표와 차량이 들어오고(입차) 나간(출차) 기록이 주어졌을 때, 차량별로 주차 요금을 계산하려고 합니다. 아래는 하나의 예시를 나타냅니다.

  • 요금표
기본 시간(분) 기본 요금(원) 단위 시간(분) 단위 요금(원)
180 5000 10 600
  • 입/출차 기록
시각(시:분) 차량 번호 내역
05:34 5961 입차
06:00 0000 입차
06:34 0000 출차
07:59 5961 출차
07:59 0148 입차
18:59 0000 입차
19:09 0148 출차
22:59 5961 입차
23:00 5961 출차
  • 자동차별 주차 요금
차량 번호 누적 주차 시간(분) 주차 요금(원)
0000 34 + 300 = 334 5000 + ⌈(334 - 180) / 10⌉ x 600 = 14600
0148 670 5000 +⌈(670 - 180) / 10⌉x 600 = 34400
5961 145 + 1 = 146 5000

 

어떤 차량이 입차된 후에 출차된 내역이 없다면, 23:59에 출차된 것으로 간주합니다.

  • 0000번 차량은 18:59에 입차된 이후, 출차된 내역이 없습니다. 따라서, 23:59에 출차된 것으로 간주합니다.

00:00부터 23:59까지의 입/출차 내역을 바탕으로 차량별 누적 주차 시간을 계산하여 요금을 일괄로 정산합니다.

누적 주차 시간이 기본 시간이하라면, 기본 요금을 청구합니다.

누적 주차 시간이 기본 시간을 초과하면, 기본 요금에 더해서, 초과한 시간에 대해서 단위 시간 마다 단위 요금을 청구합니다.

초과한 시간이 단위 시간으로 나누어 떨어지지 않으면, 올림합니다.

  • ⌈a⌉ : a보다 작지 않은 최소의 정수를 의미합니다. 즉, 올림을 의미합니다.

주차 요금을 나타내는 정수 배열 fees, 자동차의 입/출차 내역을 나타내는 문자열 배열 records가 매개변수로 주어집니다. 차량 번호가 작은 자동차부터 청구할 주차 요금을 차례대로 정수 배열에 담아서 return 하도록 solution 함수를 완성해주세요.

제한사항

fees의 길이 = 4
- fees[0] = 기본 시간(분)
- 1 ≤ fees[0] ≤ 1,439
- fees[1] = 기본 요금(원)
- 0 ≤ fees[1] ≤ 100,000
- fees[2] = 단위 시간(분)
- 1 ≤ fees[2] ≤ 1,439
- fees[3] = 단위 요금(원)
- 1 ≤ fees[3] ≤ 10,000

 

1 ≤ records의 길이 ≤ 1,000
- records의 각 원소는 "시각 차량번호 내역" 형식의 문자열입니다.
- 시각, 차량번호, 내역은 하나의 공백으로 구분되어 있습니다.
- 시각은 차량이 입차되거나 출차된 시각을 나타내며, HH:MM 형식의 길이 5인 문자열입니다.
- HH:MM은 00:00부터 23:59까지 주어집니다.
- 잘못된 시각("25:22", "09:65" 등)은 입력으로 주어지지 않습니다.
- 차량번호는 자동차를 구분하기 위한, `0'~'9'로 구성된 길이 4인 문자열입니다.
- 내역은 길이 2 또는 3인 문자열로, IN 또는 OUT입니다. IN은 입차를, OUT은 출차를 의미합니다.
- records의 원소들은 시각을 기준으로 오름차순으로 정렬되어 주어집니다.
- records는 하루 동안의 입/출차된 기록만 담고 있으며, 입차된 차량이 다음날 출차되는 경우는 입력으로 주어지지 않습니다.
- 같은 시각에, 같은 차량번호의 내역이 2번 이상 나타내지 않습니다.
- 마지막 시각(23:59)에 입차되는 경우는 입력으로 주어지지 않습니다.
- 아래의 예를 포함하여, 잘못된 입력은 주어지지 않습니다.
 - 주차장에 없는 차량이 출차되는 경우
 - 주차장에 이미 있는 차량(차량번호가 같은 차량)이 다시 입차되는 경우

 

 

입출력 예

fees records result
[180, 5000, 10, 600] ["05:34 5961 IN", "06:00 0000 IN", "06:34 0000 OUT", "07:59 5961 OUT", "07:59 0148 IN", "18:59 0000 IN", "19:09 0148 OUT", "22:59 5961 IN", "23:00 5961 OUT"] [14600, 34400, 5000]
[120, 0, 60, 591] ["16:00 3961 IN","16:00 0202 IN","18:00 3961 OUT","18:00 0202 OUT","23:58 3961 IN"] [0, 591]
[1, 461, 1, 10] ["00:00 1234 IN"] [14841]

입출력 예 설명

입출력 예 #1

문제 예시와 같습니다.

입출력 예 #2

  • 요금표
기본 시간(분) 기본 요금(원) 단위 시간(분) 단위 요금(원)
120 0 60 591
  • 입/출차 기록
시각(시:분)  차량 번호 내역
16:00 3961 입차
16:00 0202 입차
18:00 3961 출차
18:00 0202 출차
23:58 3961 입차
  • 자동차별 주차 요금
차량 번호 누적 주차 시간(분) 주차 요금(원)
0202 120 0
3961 120 + 1 = 121 0 +⌈(121 - 120) / 60⌉x 591 = 591
  • 3961번 차량은 2번째 입차된 후에는 출차된 내역이 없으므로, 23:59에 출차되었다고 간주합니다.

입출력 예 #3

  • 요금표
기본 시간(분) 기본 요금(원) 단위 시간(분)  단위 요금(원)
1 461 1 10
  • 입/출차 기록
시각(시:분) 차량 번호 내역
00:00 1234 입차
  • 자동차별 주차 요금
차량 번호  누적 주차 시간(분) 주차 요금(원)
1234 1439 461 +⌈(1439 - 1) / 1⌉x 10 = 14841
  • 1234번 차량은 출차 내역이 없으므로, 23:59에 출차되었다고 간주합니다.

아이디어

1. HH:MM의 형식으로 주어진 주차 시간을 분 단위로 환산
2. 출차 내역이 없는 차량의 경우 23:59 출차한 것으로 처리

3. 누적 주차 시간이 기본시간 이하인 케이스와 초과인 케이스를 나누어 처리
4. 누적 주차시간이 단위 시간으로 떨어지지 않을 때 올림 처리
→ math 패키지의 ceil 메서드 or 음수로 변환하는 테크닉 사용(구현 코드 참조)

5. 차량번호 오름차순으로 주차요금을 정렬하여 출력

 

구현

def convert_time(time_HHMM):
    hh, mm = map(int, time_HHMM.split(':'))
    return hh * 60 + mm
    
def solution(fees, records):
    answer = []
    park_dict = {}
    for record in records:
        park_time, car_no, status = record.split(' ')
        car_no = int(car_no)
        p_info = [convert_time(park_time), status]
        if car_no in park_dict:
            park_dict[car_no].append(p_info)
        else:
            park_dict[car_no] = [p_info]
    cost_dict = {}
    for car, v in park_dict.items():
        in_list = [m for m, s in v if s == 'IN']
        out_list = [m for m, s in v if s == 'OUT']
        if len(v) % 2 == 1:
            out_list.append(convert_time('23:59'))
        total_time = sum(out_list) - sum(in_list)
        if total_time <= fees[0]:
            cost_dict[car] = fees[1]
        else:
            cost = fees[1]
            add_time = -(-(total_time - fees[0]) // fees[2])
            cost += add_time * fees[3]
            cost_dict[car] = cost
    cost_list = sorted(cost_dict.items())
    return [cost_list[i][1] for i in range(len(cost_list))]

 


문제 출처 : 코딩테스트 연습 - 주차 요금 계산 | 프로그래머스 스쿨 (programmers.co.kr)

문제 설명

세 차례의 코딩 테스트와 두 차례의 면접이라는 기나긴 블라인드 공채를 무사히 통과해 카카오에 입사한 무지는 파일 저장소 서버 관리를 맡게 되었다.

저장소 서버에는 프로그램의 과거 버전을 모두 담고 있어, 이름 순으로 정렬된 파일 목록은 보기가 불편했다. 파일을 이름 순으로 정렬하면 나중에 만들어진 ver-10.zip이 ver-9.zip보다 먼저 표시되기 때문이다.

버전 번호 외에도 숫자가 포함된 파일 목록은 여러 면에서 관리하기 불편했다. 예컨대 파일 목록이 ["img12.png", "img10.png", "img2.png", "img1.png"]일 경우, 일반적인 정렬은 ["img1.png", "img10.png", "img12.png", "img2.png"] 순이 되지만, 숫자 순으로 정렬된 ["img1.png", "img2.png", "img10.png", img12.png"] 순이 훨씬 자연스럽다.

무지는 단순한 문자 코드 순이 아닌, 파일명에 포함된 숫자를 반영한 정렬 기능을 저장소 관리 프로그램에 구현하기로 했다.

소스 파일 저장소에 저장된 파일명은 100 글자 이내로, 영문 대소문자, 숫자, 공백(" "), 마침표("."), 빼기 부호("-")만으로 이루어져 있다. 파일명은 영문자로 시작하며, 숫자를 하나 이상 포함하고 있다.

파일명은 크게 HEAD, NUMBER, TAIL의 세 부분으로 구성된다.

  • HEAD는 숫자가 아닌 문자로 이루어져 있으며, 최소한 한 글자 이상이다.
  • NUMBER는 한 글자에서 최대 다섯 글자 사이의 연속된 숫자로 이루어져 있으며, 앞쪽에 0이 올 수 있다. 0부터 99999 사이의 숫자로, 00000이나 0101 등도 가능하다.
  • TAIL은 그 나머지 부분으로, 여기에는 숫자가 다시 나타날 수도 있으며, 아무 글자도 없을 수 있다.
파일명 HEAD NUMBER TAIL
foo9.txt foo 9 .txt
foo010bar020.zip foo 010 bar020.zip
F-15 F- 15 (빈 문자열)

 

파일명을 세 부분으로 나눈 후, 다음 기준에 따라 파일명을 정렬한다.

  • 파일명은 우선 HEAD 부분을 기준으로 사전 순으로 정렬한다. 이때, 문자열 비교 시 대소문자 구분을 하지 않는다. MUZI와 muzi, MuZi는 정렬 시에 같은 순서로 취급된다.
  • 파일명의 HEAD 부분이 대소문자 차이 외에는 같을 경우, NUMBER의 숫자 순으로 정렬한다. 9 < 10 < 0011 < 012 < 13 < 014 순으로 정렬된다. 숫자 앞의 0은 무시되며, 012와 12는 정렬 시에 같은 같은 값으로 처리된다.
  • 두 파일의 HEAD 부분과, NUMBER의 숫자도 같을 경우, 원래 입력에 주어진 순서를 유지한다. MUZI01.zip과 muzi1.png가 입력으로 들어오면, 정렬 후에도 입력 시 주어진 두 파일의 순서가 바뀌어서는 안 된다.

무지를 도와 파일명 정렬 프로그램을 구현하라.

 

입출력 형식

입력으로 배열 files가 주어진다.
- files는 1000 개 이하의 파일명을 포함하는 문자열 배열이다.
- 각 파일명은 100 글자 이하 길이로, 영문 대소문자, 숫자, 공백(" "), 마침표("."), 빼기 부호("-")만으로 이루어져 있다.
- 파일명은 영문자로 시작하며, 숫자를 하나 이상 포함하고 있다.
- 중복된 파일명은 없으나, 대소문자나 숫자 앞부분의 0 차이가 있는 경우는 함께 주어질 수 있다. (muzi1.txt, MUZI1.txt, muzi001.txt, muzi1.TXT는 함께 입력으로 주어질 수 있다.)
- 위 기준에 따라 정렬된 배열을 출력한다.

 

입출력 예제

입출력 예 #1
입력: ["img12.png", "img10.png", "img02.png", "img1.png", "IMG01.GIF", "img2.JPG"]

출력: ["img1.png", "IMG01.GIF", "img02.png", "img2.JPG", "img10.png", "img12.png"]
입출력 예 #2
입력: ["F-5 Freedom Fighter", "B-50 Superfortress", "A-10 Thunderbolt II", "F-14 Tomcat"]
출력: ["A-10 Thunderbolt II", "B-50 Superfortress", "F-5 Freedom Fighter", "F-14 Tomcat"]

 

아이디어

1. 입력된 문자열에서 HEAD와 NUMBER에 해당하는 값을 추출하기 → 정규식 사용
2. lambda 함수 등을 이용하여 여러가지의 정렬 조건 만족시키기 

구현

CASE 1

import re
def solution(files):
    answer = []
    my_files = []
    for i, file in enumerate(files):
        head = re.findall(r'\D+', file)[0]
        head = head.lower()
        number = int(re.findall(r'\d+',file)[0])
        my_files.append([head, number, i])
    my_files.sort(key = lambda x: (x[0], x[1], x[2]))
    answer = [files[j[2]] for j in my_files]
    return answer

 

CASE 2

import re
def solution(files):
    my_files = {}
    for file in files:
        head = re.findall(r'\D+', file)[0]
        head = head.lower()
        number = int(re.findall(r'\d+',file)[0])
        my_files[file] = [head, number]
    my_files = sorted(my_files.items(), key = lambda x: (x[1][0], x[1][1]))

 


문제 출처 : 코딩테스트 연습 - [3차] 파일명 정렬 | 프로그래머스 스쿨 (programmers.co.kr)

문제 상황

왕실의 정원은 체스판과 같이 생긴 8 by 8 평면으로 되어 있다.

왕실 정원의 특정한 한 칸에 나이트가 서 있다.

나이트의 이동 : L자 형태로만 이동을 할 수 있으며(아래 1,2 경우) 정원 밖으로는 나갈 수 없다.

  1) 수평으로 2칸 이동 뒤 수직으로 1칸 이동

  2) 수직으로 2칸 이동 뒤 수평으로 1칸 이동

아래 그림과 같이 8by8에서는 행 : 1~8, 열 : a ~ h로 표시한다.

출처: 이것이 코딩테스트다

예시상황

예시 상황 : a1에 위치해 있다면, 이동할 수 있는 경우의 수는 2가지이다.

  1. 오른쪽으로 2칸 이동 후 아래로 1칸 이동하여 : c2
  2. 아래로 2칸 이동 후 오른쪽으로 1칸 이동하기 : b3

예시 상황 : c2에 위치해 있다면, 이동할 수 있는 경우의 수는 6가지이다.

  1. 오른쪽으로 2칸 이동 후 아래로 1칸 이동 : e3
  2. 오른쪽으로 2칸 이동 후 위로 1칸 이동 : e1
  3. 왼쪽으로 2칸 이동 후 아래로 1칸 이동 : a3
  4. 왼쪽으로 2칸 이동 후 위로 1칸 이동 : a1
  5. 아래로 2칸 이동 후 오른쪽으로 1칸 이동 : d4
  6. 아래로 2칸 이동 후 왼쪽으로 1칸 이동 : b4

 

입출력 조건

- 입력 조건 : 현재 나이트가 위치한 곳의 좌표를 나타내는 두 문자를 받아서 처리할 것 : 순서는 문자열, 숫자행( 예 : a1) - - 출력 조건 : 이동할 수 있는 경우의 수 출력

 

입출력 예시

입출력 예 #1
입력 예시 : a1
출력 예시 : 2

입출력 예 #2
입력 예시 : c2
출력 예시 : 6

 

아이디어

1. 주어진 입력을 문자와 숫자로 분리하여 가로와 세로의 위치 정보로 분배
2. 가능한 8가지 이동에 대한 것을 구체화
3. 주어진 범위를 벗어나는 경우와 아닌 경우를 나누어 처리
4. 입력받은 위치에서  이동이 가능한 경우를 카운팅

구현

position = input('시작 위치를 입력하세요.')
x = int(position[1])
y = ord(position[0]) - ord('a') + 1
l_moves = [
    [-2,-1], [-2, 1], [2, -1], [2, 1], [-1, -2], [-1, 2], [1, -2], [1, 2]
]
cnt = 0
for move in l_moves:
    next_x = x + move[0]
    next_y = y + move[1]
    if next_x < 1 or next_x > 8 or next_y < 1 or next_y > 8:
        continue
    else:
        cnt += 1
print(cnt)

 

[참고]

  • ord() 함수 : 특정 문자를 아스키 코드로 변환
  • chr() 함수 : 아스키 코드값을 문자로 변환

문제 출처 : 이것이 코딩테스트다

CASE

  • 조건에 따라 분기
  • 다중 분기
  • 조건에 맞는 WHEN이 여러개더라도 먼저 조건이 만족하는 WHEN 처리됨
  • SELECT문에서 많이 사용됨
CASE
WHEN 조건 THEN 조건을 만족할 때 적용할 내용
ELSE 조건을 만족하지 않을 때 적용할 내용
END
Q1. buytbl에서 구매액이 1500원 이상인 사람은 최우수 고객, 1000원 이상인 사람은 우수고객, 1원 이상인 사람은 일반고객, 구매내역이 없는 고객은 유령회원으로 등급을 매기려고 한다. 회원 아이디, 이름, 총 구매액, 분류한 등급을 총 구매액이 높은 순서대로 출력하라.
select U.userID, U.name, sum(price*amount) Total_Price,
	case
    		when sum(price*amount) >= 1500 then 'VVIP'
		when sum(price*amount) >= 1000 then 'VIP'
		when sum(price*amount) >= 1 then 'Basic'
		else 'Ghost'
	end CustomerClass
from usertbl U left join buytbl B
    on B.userID = U.userID
    group by B.userID, U.name
    order by Total_Price desc;​

when 안에 별칭을 사용하면 에러가 발생, 그렇다면 별칭을 사용할 수 있는 다른 방법은?
>> 아래와 같이 서브쿼리문 사용!

select *,
	case
		when Total_Price >= 1500 then 'VVIP'
		when Total_Price >= 1000 then 'VIP'
		when Total_Price >= 1 then 'Basic'
		else 'Ghost'
	end CustomerClass
from (select U.userID, U.name, sum(price*amount) Total_Price
	from usertbl U left join buytbl B
    on B.userID = U.userID
    group by B.userID, U.name
    order by Total_Price desc)A;

>> 여기에서 A는 삽입된 서브쿼리문의 별칭, MySQL은 서브쿼리를 참조할 때 무의미한 별칭이라도 꼭 붙여줘야 함

 


Reference : 이것이 MySQL이다

'SQL' 카테고리의 다른 글

09. [SQL] 연습문제 - JOIN  (0) 2024.04.09
08. [SQL] 연습문제 - SELECT ... FROM  (0) 2024.04.08
06. [SQL] 조인  (0) 2024.04.08
05. [SQL] SELECT문  (1) 2024.04.07
04. [SQL] 테이블 외의 데이터베이스 개체의 활용  (1) 2024.04.07

조인(Join)

  • 두개 이상의 테이블을 서로 묶어서 하나의 결과 집합으로 만들어 내는 것
  • INNER JOIN, OUTER JOIN, CROSS JOIN, SELF JOIN 등이 있음.

 

INNER JOIN(내부 조인)

  • 조인 중에서 가장 많이 사용됨.
  • 일반적으로 join이라고 얘기하면 inner join을 말함.
  • inner를 생략하고 join이라고만 쓰면 Inner Join으로 인식함.
SELECT 열1, 열2, ...
FROM 테이블1
INNER JOIN 테이블2
ON 조인될 조건
WHERE 검색조건 ;
Q1. 전체 회원들이 구매한 목록 중 아이디, 이름, 구매물품, 주소, 연락처를 아이디 순서대로 출력하라.
select B.userID, U.name, B.prodName, U.addr, concat(U.mobile1 + U.mobile2) phonenumber
	from buytbl B
    inner join usertbl U
    on B.userID = U.userID
    order by U.userID;


Q2. 회원 테이블을 기준으로 아이디가 JYP인 회원이 구매한 물건의 목록을 출력하라.

select B.userID, U.name, B.prodName, U.addr, concat(U.mobile1 + U.mobile2) phonenumber
	from buytbl B
    inner join usertbl U
    on B.userID = U.userID
    where B.userID = 'JYP';


Q3. 쇼핑몰에서 한 번이라도 구매한 기록이 있는 회원의 아이디, 이름, 주소를 출력하라.

select distinct U.userID, U.name, U.addr
	from usertbl U
    inner join buytbl B
    on B.userID = U.userID
    order by U.userID;​

Q4. 학생 정보, 동아리 정보, 학생들의 동아리 정보가 각각 담긴 3개의 테이블을 이용해서 학생이름, 지역, 가입한 동아리, 동아리방을 출력하라. 단, 학생이름 사전 순으로 정렬하라.
select S.stdName, S.addr, SC.clubName, C.roomNo
	from stdtbl S
    inner join stdclubtbl SC
		on S.stdName = SC.stdName
	inner join clubtbl C
		on SC.clubName = C.clubName
	order by S.stdname;​

 

OUTER JOIN(외부 조인)

  • 조인의 조건에 만족되지 않는 행까지도 포함시키는 것
  • LEFT OUTER JOIN
    • 왼쪽 테이블을 기준으로 오른쪽 테이블에서 추가할 내용이 있으면 함께 출력
  • RIGHT OUTER JOIN
    • 오른쪽 테이블을 기준으로 왼쪽 테이블에서 추가할 내용이 있으면 함께 출력
  • OUTER는 생략 가능
  • JOIN 후 기준이 되는 테이블의 행이 늘어날 수는 있지만 기준 테이블의 데이터가 누락되는 일은 없음
SELECT 열1, 열2, ...
FROM 테이블1
LEFT/RIGHT (OUTER) JOIN 테이블2
ON 조인될 조건
WHERE 검색조건 ;
Q1. 회원 테이블과 구매 테이블이 주어졌을 때, 회원 아이디, 회원 이름, 구매 품목 이름, 회원 주소, 회원 연락처를 출력하라. 단, 회원 아이디 순서대로 출력하라.
select U.userID, U.name, B.prodName, U.addr, concat(U.mobile1, U.mobile2) phonenumber
	from usertbl U
	left join buytbl B
        on U.userID = B.userID
	order by U.userID;


Q2. 회원 테이블에는 있지만 구매 내역이 없는 회원들을 찾아라.

select U.userID, U.name, B.prodName, U.addr, concat(U.mobile1, U.mobile2) phonenumber
	from usertbl U
	left join buytbl B
        on U.userID = B.userID
	where B.prodName is null
	order by U.userID;


Q3. 앞의 학생, 동아리, 학생이 가입한 동아리 정보를 담은 3개의 테이블에서 동아리에 가입하지 않은 학생의 정보도 출력하라.

select S.stdName, S.addr, SC.ClubName, C.roomNo
	from stdtbl S 
    left join stdclubtbl SC
		on S.stdName = SC.stdName
    left join clubtbl C
		on SC.clubName = C.clubName
    order by S.stdName;

 

Q3. 동아리를 기준으로 가입된 학생들을 출력하라. 단, 가입 학생이 하나도 없는 동아리의 정보도 출력하라.하라.
select C.clubName, C.roomNo, S.stdName, S.addr
	from clubtbl C
    left join stdclubtbl SC
		on C.clubName = SC.clubName
	left join stdtbl S
		on SC.stdName = S.stdName
	order by C.clubName;​

 

CROSS JOIN(상호 조인)

  • 한 쪽 테이블의 모든 행들과 다른 쪽 테이블의 모든 행을 조인시키는 기능
  • M행의 테이블과 N행의 테이블을 CROSS JOIN 하면 M*N개의 행이 생성됨 
  • 테스트로 사용할 많은 용량의 데이터를 생성할 때 주로 사용
  • ON  구문을 사용할 수 없음
  • 대량의 데이터를 생성하면 시스템이 다운되거나 디스크 용량이 모두 찰 수 있으므로 COUNT(*) 함수로 개수만 카운트
- 방법1
SELECT 열1, 열2, ...
FROM 테이블1
CROSS JOIN `테이블2`
WHERE 출력조건 ;

- 방법2

SELECT 열1, 열2
FROM 테이블1, 테이블2
WHERE 출력조건 ;
Q. 회원 정보와 빌려간 영화의 정보가 담긴 테이블이 있을 때, 두 테이블에서 얻을 수 있는 모든 조합을 출력하라.
select * from movies cross join members;​
select * from movies, members;

 


Reference : 이것이 MySQL이다

USE 구문

  • SELECT문 학습 위해 사용할 데이터베이스 지정
  • 지정해 놓은 후 다른 DB를 사용하겠다고 명시하지 않는 이상 모든 SQL문은 지정된 DB에서 수행
USE `데이터베이스 이름`;
use employees;

 
SELECT ... FROM

  • 데이터베이스 내 테이블에서 원하는 정보를 추출
  • 테이블에 있는 모든 정보 조회
SELECT * FROM `테이블 이름`;
select * from employees;
  • 테이블에서 필요로 하는 열만 가져오기
SELECT `열 이름` FROM `테이블 이름`;
select first_name from employees;
  • 여러 개의 열을 가져오고 싶을 때는 콤마로 구분
  • 열 이름의 순서는 출력하고 싶은 순서대로 배열 가능
SELECT 열1, 열2, 열3 FROM `테이블 이름`;
select first_name, last_name, gender from employees;

 
주석(Remark)

  • 한 줄 주석처리 --
    • -- 뒤에 하나 이상의 공백이 있어야 함.
-- 한 줄 주석
  • 여러 줄 주석처리 /* */
/* 블록주석
   여러줄 주석
*/

 

DB, TABLE, 열의 이름이 확실하지 않을 때 조회하는 방법

  • 현재 서버에 어떤 DB가 있는지 조회
SHOW DATABASES;
  • 현재 DB에 있는 TABLE 정보 조회
SHOW TABLE STATUS;
  • 현재 DB에 있는테이블 이름만 간단히 보기
SHOW TABLES;
  • 테이블에 있는 열 정보 확인
DESCRIBE 테이블 이름 ; 
DESC 테이블 이름;

 
 
열 이름에 별칭 지정하기

열이름 as 별칭
  • as는 생략가능, 별칭 내에 공백이 있다면 ` `(백틱)으로로 묶어줘야 함. ' '(따옴표)와 구별에 주의
select first_name as 이름,
		gender as 성별,
		hire_date as `회사 입사일`
    from employees;

 
SELECT ... FROM ... WHERE

  • 특정 조건의 데이터만 조회
SELECT `열 이름` FROM `테이블 이름` WHERE 조건식;
select * from usertbl where name = '김경호';
Q1. 이름이 김범수인 사람을 찾아라.
select * from usertbl where name = '김범수';​

Q2. 이름이 조관우인 사람을 찾아라.
select * from usertbl where name = '조관우';​

Q3. 주소지가 서울인 사람을 찾아라.
select * from usertbl where Addr = '서울';​

Q4. 출생년도가 1973인 사람을 찾아라.
select * from usertbl where birthYear = 1973;​

Q5. Mobile1의 정보가 없는 사람을 찾아라.
select * from usertbl where mobile1 is null;​

 
관계 연산자를 사용한 특정 조건의 데이터 조회

  • 조건 연산자(=, <, >, <=, >=, <>, != 등)와 관계 연산자(NOT, AND, OR 등)를 조합하여 데이터를 효율적으로 추출 가능
Q1. 1970년 이후에 출생하고, 신장이 182 이상인 사람의 모든 정보를 조회하라.
select * from usertbl where birthYear >= 1970 and height >= 182;​

Q2.1970년 이후에 출생하고, 신장이 182 이상인 사람의 아이디와 이름을 조회하라.
select userID, name from usertbl where birthYear >= 1970 and height >= 182;​

Q3. 주소지가 서울인 사람들의 모든 정보를 조회하라.
select * from usertbl where addr = '서울';​

Q4. 주소지가 서울이면서 mobile1의 번호를 011로 사용하는 사람의 모든 정보를 조회하라.
select * from usertbl where addr = '서울' and mobile1 = 011;​

Q5. 주소지가 서울이면서 mobile1의 번호를 011로 사용하는 사람의 아이디, 이름, mobile1과 mobile2를 조회하라.
select userID, name, mobile1, mobile2 from usertbl where addr = '서울' and mobile1 = 011;​

Q6. 위의 결과 중 mobile1과 mobile2 사이에 구분자(-)를 넣어서 phonenumber라는 하나의 열로 나타내라.
select userID, name, concat(mobile1, '-' ,mobile2) as phonenumber
	from usertbl
    where addr = '서울' and mobile1 = 011;​

 

  • BETWEEN... AND : 데이터가 숫자로 구성되어 있으며 연속적인 값일 때
Q. 키가 180에서 183 사이인 사람들의 이름과 키를 출력하라.
select name, height from usertbl where height between 180 and 183;​
  • IN () : 이산적인 값의 조건을 나타낼 때
Q. 주소지가 경남, 전남, 경북 중 하나인 사람들의 이름과 주소를 출력하라.
select name, addr from usertbl where addr in ('경남', '전남', '경북');​
  • LIKE : 문자열의 내용 검색
    • % : 무엇이 오든 제한없이 허용
    •  _ : 한 글자와 매치
Q. 이름의 두번째 글자가 '용'인 사람의 이름을 출력하라.
select name from usertbl where name like '_용%';

 

 
서브쿼리(SubQuery, 하위쿼리)

  • 쿼리문 안에 또 쿼리문이 들어있는 것
  • 서브쿼리 결과가 2개 이상이면 에러가 발생하므로 ANY, SOME, ALL 등과 함께 사용
Q. 김경호보다 키가 큰 사람의 이름과 키를 출력하라.
select name, height from usertbl
	where height > (select height from usertbl where name = '김경호');​

 

  • ANY/SOME
    • 서브쿼리의 여러 개의 결과 중 한 가지만 만족해도 가능
    • = 'ANY(서브쿼리)'와 'IN(서브쿼리)'는 동일한 의미
Q. 지역이 경남인 사람의 키보다 큰 사람의 이름과 키를 출력하라. (any 사용)
select name, height from usertbl
	where height > any (select height from usertbl where Addr = '경남');​

>> 지역이 경남인 사람 중 어느 한사람보다만 크면 되므로 키가 제일 작은 170 보다 큰 사람들이 출력됨.

  • ALL
    • 서브쿼리의 결과 중 여러개의 결과를 모두 만족해야 함.
Q. 지역이 경남인 사람 누구의 키보다 큰 사람의 이름과 키를 출력하라. (all 사용)
select name, height from usertbl
	where height > all (select height from usertbl where Addr = '경남');​

>> 지역이 경남인 사람 누구보다 커야 하므로 키가 제일 큰 173 보다 큰 사람들이 출력됨.

 
원하는 순서대로 정렬하여 출력 : ORDER BY

  • 결과물에 대해 영향을 미치지는 않고 출력되는 순서를 조절
  • 오름차순으로 정렬하려면 열 이름 뒤에 ASC, 내림차순으로 정렬하려면 열 이름 뒤에 DESC를 붙임.
  • 기본값이 오름차순이므로 ASC는 생략 가능
  • 조건을 여러 개로 설정하여 정렬하는 것도 가능
Q. 키가 큰 순서로 정렬하되 만약 키가 같을 경우 이름 순으로 정렬하라.
select name, height from usertbl order by height desc, name asc;​

 
중복된 값에 대한 처리 : DISTINCT

  • 중복된 것을 유니크한 값으로 처리
  • 테이블의 크기가 클수록 효율적
  • DISTINCT 뒤에 여러개를 나열하면 모든 열에 적용됨.
Q. usertbl의 지역들을 중복없이 출력하라.
select distinct addr from usertbl;​

 
출력의 개수를 제한 : LIMIT

  • 일부를 보기 위해 여러 건의 데이터를 출력하는 부담을 줄임
  • 상위 N개의 데이터만 보고싶다면 limit N 사용
Q. employees 테이블에서 입사일이 오래된 5명의 사번과 입사일을 입사일이 오래된 순서대로 출력하라.
select emp_no, hire_date
	from employees
    order by hire_date asc
    limit 5;

 
테이블 복사하기 : CREATE TABLE ... SELECT

  • 테이블을 복사해서 사용할 경우 주로 사용
  • 지정한 일부 열만 복사하는 것도 가능
  • PK나 FK같은 제약 조건은 복사되지 않음.
CREATE TABLE `새로운 테이블` (SELECT `복사할 열` FROM `기존 테이블`);
Q1. sqldb의 buytbl 테이블을 buytbl2로 복사하라.
use sqldb;
create table buytbl2 (select * from buytbl);​

Q2. buytbl 테이블 중 userID와 prodName 열만 buytbl3로 복사하라.
create table buytbl3 (select userID, prodName from buytbl);​

 

GROUP BY ... HAVING

  • 그룹으로 묶어주는 역할
  • 집계 함수(Aggregate Function)와 함께 사용
  • GROUP BY 와 함께 자주 사용되는 집계 함수
함수명설명
AVG()평균
MIN()최소값
MAX()최대값
COUNT()행의 개수
COUNT(DISTINCT)중복을 제외한 행의 개수
STDEV()표준편차
VAR_SAMP()분산
Q. 각 사용자 별로 구매한 물품의 총 구매액을 ID의 사전 순서대로 출력하라.
select userID as `사용자 아이디`, sum(amount * price) as `총 구매 개수`
	from buytbl
    group by userID
    order by userID;​
  • HAVING절
    • WHERE와 비슷한 개념으로 GROUP BY절 뒤에서 집계 함수에 대해서 조건을 제한
Q. 총 구매액이 1000 이상인 회원의 ID와 총 구매액을 총 구매액이 큰 것부터 순서대로 출력하라.
select userID as `사용자 아이디`, sum(price * amount) as `총 구매액`
	from buytbl
    group by userID
    having sum(price * amount) > 1000
    order by sum(price * amount) desc;​

 

ROLLUP

  • 총합 또는 중간 합계가 필요한 경우 사용
  • GROUP BY와 함께 WITH ROLLUP문 사용
Q. buytbl의 groupName 분류별 구매액 및 총합을 구하라.
select groupName, sum(price * amount) as `총 구매액`
	from buytbl
	group by groupName
	with rollup;​

 


Reference : 이것이 MySQL이다
 

인덱스(Index)

  • 데이터베이스 '색인'의 개념
  • 쿼리에 응답하는 시간을 단축시켜 데이터베이스의 성능 향상
  • 데이터의 양이 많을수록 효과적으로 작용
  • 테이블의 열 단위에 생성

참고) 기본 키(PK) 열에는 명시적으로 인덱스를 생성하지 않아도 자동 생성됨.

Q. first_name이 Mary인 사람의 정보를 indextbl에서 찾아라.
select * from indextbl where first_name = 'Mary';​

>> Query cost : 50.75


Q. first_name 열에 인덱스를 생성한 후 위의 쿼리문을 재실행하라.

create index idx_indextbl_firstname on indextbl(first_name);
select * from indextbl where first_name = 'Mary';​

>> Query cost : 0.35 (같은 쿼리문이지만 속도면에서 확실한 개선이 나타남)

뷰(View)

  • 실제 행 데이터를 가지고 있지 않은 가상의 테이블
  • 실제 테이블에 링크(Link)된 개념
  • 뷰를 조회하면 진짜 테이블의 데이터를 조회하는 것과 동일한 결과가 나타남.
  • 테이블 중 일부 데이터만 조회할 수 있는 권한을 부여하려고 할 때 사용됨.
Q. membertbl에서 mamberName 과 memberAddress 데이터에만 접근할 수 있는 View를 생성하라.
create view uv_membertbl
	as select memberName, memberAddress
	from membertbl;

select * from uv_membertbl;​

 

저장 프로시저(Stored Procedure)

  • MySQL에서 제공해주는 프로그래밍 기능
  • 쿼리문을 하나로 묶어 편리하게 사용하는 기능
  • 자주 사용하는 쿼리문을 스토어드 프로시저로 만들어 놓으면 쿼리문을 매번 하나하나 수행하는 번거로움을 해결할 수 있음.
Q. membertbl에서 memberName 이 '당탕이'인 사람의 정보를 보여주고 producttbl에서 productName이 냉장고인 제품의 정보를 보여주는 쿼리문을 담은 스토어드 프로시저를 생성하고 호출하라.
DELIMITER //
create procedure myProc()
begin
	select * from membertbl where memberName = '당탕이';
    select * from producttbl where productName = '냉장고';
end //
DELIMITER ;

call myProc();
* 주의) 마지막 DELIMITER와 ; 사이에는 공백이 하나 존재해야 함.

 

트리거(Trigger)

  • 테이블에 부착되어 테이블에 INSERT/UPDATE/DELETE 작업이 발생하면 실행되는 코드
Q. membertbl에서 데이터가 삭제되었을 때 삭제된 정보를 저장하는 트리거를 생성하고 실행하라.
create table deletedmembertbl(
	memberID CHAR(8),
    memberName CHAR(5),
    memberAddress CHAR(20),
    deletedDate DATE);
    
DELIMITER //
	create trigger trg_deletedmembertbl
	after delete
	on membertbl
	for each row
begin
	insert into deletedmembertbl
    values (OLD.memberID, OLD.memberName, OLD.memberAddress, CURDATE());
end //
DELIMITER ;

delete from membertbl where memberName = '당탕이';
select * from deletedmembertbl;

 


Reference : 이것이 MySQL이다!

'SQL' 카테고리의 다른 글

06. [SQL] 조인  (0) 2024.04.08
05. [SQL] SELECT문  (1) 2024.04.07
03. [SQL] MySQL을 이용한 데이터베이스 구축  (0) 2024.04.07
02. [SQL] 요구사항 분석과 시스템 설계 그리고 모델링  (0) 2024.04.04
01. [SQL] DBMS 개요  (0) 2024.04.03

+ Recent posts