ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 2024-01-02
    스파르타/TIL(Today I Learned) 2024. 1. 2. 23:18
    더보기

    SQL 코드카타 문제

     

    3월에 태어난 여성 회원 목록 출력하기(SQL) (date_format)

    https://school.programmers.co.kr/learn/courses/30/lessons/131120

    MEMBER_PROFILE 테이블에서 생일이 3월인 여성 회원의 ID, 이름, 성별, 생년월일을 조회하는 SQL문을 작성해주세요. 이때 전화번호가 NULL인 경우는 출력대상에서 제외시켜 주시고, 결과는 회원ID를 기준으로 오름차순 정렬해주세요. 라는 문제이다.

    SELECT MEMBER_ID, MEMBER_NAME, GENDER, date_format(DATE_OF_BIRTH,'%Y-%m-%d')
    FROM MEMBER_PROFILE
    WHERE date_format(DATE_OF_BIRTH,'%m-%d')=3 and GENDER = 'W' and TLNO is not null
    

    date_format 조건으로 검색하는 방법이 아직 조금 확실히 모르겠는데, 날짜 형식 데이터가 있으면 str으로 원하는 형식으로 바꿀 수 있고, 앞자리를 기준으로 같으면 조건에 만족되는 듯 하다.

     

    대여 기록이 존재하는 자동차 리스트 구하기(SQL) (date_format)

    https://school.programmers.co.kr/learn/courses/30/lessons/157341

    CAR_RENTAL_COMPANY_CAR 테이블과 CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블에서 자동차 종류가 '세단'인 자동차들 중 10월에 대여를 시작한 기록이 있는 자동차 ID 리스트를 출력하는 SQL문을 작성해주세요. 자동차 ID 리스트는 중복이 없어야 하며, 자동차 ID를 기준으로 내림차순 정렬해주세요. 라는 문제이다.

    SELECT distinct C.CAR_ID
    FROM CAR_RENTAL_COMPANY_CAR C inner join CAR_RENTAL_COMPANY_RENTAL_HISTORY H on C.CAR_ID = H.CAR_ID
    WHERE CAR_TYPE='세단' and date_format(START_DATE,'%m-%d')=10
    ORDER BY 1 desc
    

    마찬가지로date_format을 월 단위로 필터링 하는 것인데 이전 문제와 유사하다 볼 수 있어 빠르게 제출했다

     

    모든 레코드 조회하기(SQL)

    https://school.programmers.co.kr/learn/courses/30/lessons/59034

    동물 보호소에 들어온 모든 동물의 정보를 ANIMAL_ID순으로 조회하는 SQL문을 작성해주세요. SQL을 실행하면 다음과 같이 출력되어야 합니다. 라는 문제이다.

    SELECT *
    FROM ANIMAL_INS
    

    너무 짧고 간단해서 의미가 없는 문제인듯하다

     

    즐겨찾기가 가장 많은 식당 정보 출력하기(SQL)

    https://school.programmers.co.kr/learn/courses/30/lessons/131123

    REST_INFO 테이블에서 음식종류별로 즐겨찾기수가 가장 많은 식당의 음식 종류, ID, 식당 이름, 즐겨찾기수를 조회하는 SQL문을 작성해주세요. 이때 결과는 음식 종류를 기준으로 내림차순 정렬해주세요. 라는 문제이다.

    SELECT FOOD_TYPE, REST_ID, REST_NAME, FAVORITES
    FROM REST_INFO
    GROUP BY 1,4
    HAVING max(FAVORITES) = FAVORITES 
    ORDER BY 1
    
    SELECT FOOD_TYPE, REST_ID, REST_NAME, max(FAVORITES)
    FROM REST_INFO
    GROUP BY 1,4
    ORDER BY 1
    

    처음에 이렇게 했는데 HAVING 절이 제대로 적용 되지 않는 듯 했는데 (주석처리하나 쓰나 결과 동일하고 종류별로 여러 개 나왔음) 정확한 이유를 모르겠음 그렇다고 아래 것처럼하면  

    SQL 실행 중 오류가 발생하였습니다.
    Can't group on 'max(FAVORITES)'
    

    라는 에러가 뜨고

    SELECT FOOD_TYPE, REST_ID, REST_NAME, max(FAVORITES) FAVORITES
    FROM REST_INFO
    GROUP BY 1
    ORDER BY 1 desc
    

    그냥 간단히 이렇게 하면 될 듯하여 해서 결과는 맞게 나온듯한데 제출을 하면, 틀렸다고 나오는데 이유가 뭔지 모르겠다

    SELECT FOOD_TYPE, REST_ID, REST_NAME, FAVORITES
    FROM REST_INFO
    WHERE FAVORITES in (select max(FAVORITES) FROM REST_INFO GROUP BY FOOD_TYPE)
    GROUP BY 1
    ORDER BY 1 desc
    
    SELECT FOOD_TYPE, REST_ID, REST_NAME, FAVORITES
    FROM REST_INFO U
    WHERE FAVORITES = (select max(FAVORITES) FROM REST_INFO W GROUP BY FOOD_TYPE HAVING U.FOOD_TYPE=W.FOOD_TYPE)
    GROUP BY 1
    ORDER BY 1 desc
    

    아마 의도가 거의 반 우연잖게 최대값들만 나오는게 아닌 따로 필터링 해주거나 미리 구해준 테이블로 표시하는 그런 느낌을 의도한 것이라 그런 것 같다 in은 뭔가 느낌이 안 맞아서 아래 방식을 사용하였다.

     

    더보기

    파이썬 코드카타

     

    공원 산책(python) (좌표 형식)

    https://school.programmers.co.kr/learn/courses/30/lessons/172928

    공원의 시작지점과 장애물위치를 표시한 전체 공원 정보를 입력해주고, 이동명령을 입력해주는데 시작지점부터 시작하여 공원을 벗어나거나, 장애물을 만나는 명령을 제외한 나머지 명령들을 수행했을 시 최종 위치를 리턴해주는 문제이다.

    def solution(park, routes):
        s_pos=[0,0] #[y,x] y:북&남 , x:동&서
        cnt_pos=[] #장애물 있는 위치 can't
        y_size=len(park)
        x_size=len(park[0]) #모든 park[y]의 사이즈가 같을 것이니
        for y in range(y_size):
            for x in range(x_size):
                if park[y][x] =='S': #시작 위치 기억
                    s_pos[0],s_pos[1] = y,x
                if park[y][x] =='X': #장애물 위치 기억
                    cnt_pos.append([y,x])
        #print(s_pos, cnt_pos)
        now_pos=s_pos #처음 위치 시작위치로 적용
        for move in routes: #굳이 split으로 나눠서 각각 적용 말고 위치 고정(0,2)일테니 이용
            if move[0] =='E':
                for num in range(1,int(move[2])+1):
                    if now_pos[1]+num >=x_size: #0~size-1까지 가능 범위이니 넘으면 패스
                        break
                    elif [now_pos[0],now_pos[1]+num] in cnt_pos:
                        break
                    else:
                        if num==int(move[2]):
                            now_pos[1]+=num
            elif move[0] =='W':
                for num in range(1,int(move[2])+1):
                    if now_pos[1]-num <0: #0~size-1까지 가능 범위이니 넘으면 패스
                        break
                    elif [now_pos[0],now_pos[1]-num] in cnt_pos:
                        break
                    else:
                        if num==int(move[2]):
                            now_pos[1]-=num
            elif move[0] =='S':
                for num in range(1,int(move[2])+1):
                    if now_pos[0]+num >=y_size: #0~size-1까지 가능 범위이니 넘으면 패스
                        break
                    elif [now_pos[0]+num,now_pos[1]] in cnt_pos:
                        break
                    else:
                        if num==int(move[2]):
                            now_pos[0]+=num
            elif move[0] =='N':
                for num in range(1,int(move[2])+1):
                    if now_pos[0]-num <0: #0~size-1까지 가능 범위이니 넘으면 패스
                        break
                    elif [now_pos[0]-num,now_pos[1]] in cnt_pos:
                        break
                    else:
                        if num==int(move[2]):
                            now_pos[0]-=num
            else:
                print("혹시 모를 오류잡기용")
            
        return now_pos
    
    #가상 범위 하나 더 넣을까하다가 굳이 안넣어도 괜찮지 않을까해서 if로 미리 막으면 되니
    #일단 없이 해봄
    

    첫 큰틀 짜는데 조금 걸리긴 했지만 설계를 다 한뒤로는 꽤나 실수한 부분 제외하면 스무스하게 잘 되었다. 처음에는 index 에러를 생각하여 가상으로 실제 공원보다 하나 크게하여 할까하다가 미리 조건문을 통해 확인하고 사용하면 필요없을 듯하여 제외하였고, routes를 읽어낼 때 split을 통해 나눠서 op, n =routes[move].split()할까 했지만 사실 이것보다는 고정적으로 routes[move][0]과 routes[move][2]의 값을 가지고 있기에 이 정보를 사용하는게 더 좋을 듯하여 split을 사용하지 않았다

     

    신고 결과 받기(python) (딕셔너리(dictionary))

    https://school.programmers.co.kr/learn/courses/30/lessons/92334

    대강 게시판 불량 이용자를 신고하는데 k번이상 신고를 받은 유저는 이용 정지를 당하고 해당 유저를 신고한 유저들에게 정지시켰다고 이메일을 보내는데 각 유저에게 전달되는 이메일 수를 구하는 문제이다.

    def solution(id_list, report, k):
        answer = [0 for i in id_list]
        id_index={id:index for id,index in zip(id_list,range(len(id_list)))}
        report_subject={id:'' for id in id_list} #신고한 대상 저장
        report_count={id:0 for id in id_list} #신고카운트하는 딕셔너리
        suspension=[] #정지명단
        print(id_index)
        for contents in report:
            reporter,subject=contents.split()
            if subject not in report_subject[reporter]:
                if report_subject[reporter]=='':
                    report_subject[reporter]+=subject
                else:
                    report_subject[reporter]+=','+subject
                report_count[subject]+=1
        
        for sub in report_count: #딕셔너리는 for문으로 돌릴 때 인덱스으로는 못하나?
            if report_count[sub] >=k:
                suspension.append(sub)
        for check in suspension:
            for key in report_subject:
                if check in report_subject[key]:
                    answer[id_index[key]]+=1
        
        return answer
    

    처음에 이걸로 했는데 마지막 안에 포함 되어있냐 하는 부분에 아이디가 a, ab, abc의 경우 a를 신고한 사람들에 있냐 확인할 때 in을 사용해서 ab, abc를 신고한 사람의 경우에도 해당한다고 계산해버려서 정답에서 틀린 케이스들이 생기고 말았다.

    def solution(id_list, report, k):
        answer = [0 for i in id_list]
        id_index={id:index for id,index in zip(id_list,range(len(id_list)))}
        report_subject={id:'' for id in id_list} #신고한 대상 저장
        report_count={id:0 for id in id_list} #신고카운트하는 딕셔너리
        suspension=[] #정지명단
        for contents in report:
            reporter,subject=contents.split()
            if subject not in report_subject[reporter]:
                if report_subject[reporter]=='':
                    report_subject[reporter]+=subject
                else:
                    report_subject[reporter]+=','+subject
                report_count[subject]+=1
        for sub in report_count: #딕셔너리는 for문으로 돌릴 때 인덱스으로는 못하나?
            if report_count[sub] >=k:
                suspension.append(sub)
        for check in suspension:
            for key in report_subject:
                for val in report_subject[key].split(','):
                    if check == val:
                        answer[id_index[key]]+=1
        
        return answer
    

    이렇게 했지만 그래도 ["a", "ab", "abc", "b"], ["ab a", "ab abc", "ab b", "abc a", "abc ab", "abc b"], 2 의 경우에 대해 (체크할 때 b에 대해 앞에서 a,abc를 추가했을때 b가 안에 있다고 인식되어서) 같은 케이스에 대해 실패가 떳다

    def solution(id_list, report, k):
        answer = [0 for i in id_list]
        id_index={id:index for id,index in zip(id_list,range(len(id_list)))}
        report_subject={id:'' for id in id_list} #신고한 대상 저장
        report_count={id:0 for id in id_list} #신고카운트하는 딕셔너리
        suspension=[] #정지명단
        for contents in report:
            reporter,subject=contents.split()
            for ch_id in report_subject[reporter].split(','):
                check='y'
                if subject == ch_id: #이미 있으면 패스
                    check="n"
                    break
            if check=='y':
                if report_subject[reporter]=='':
                    report_subject[reporter]+=subject
                    report_count[subject]+=1
                else:
                    report_subject[reporter]+=','+subject
                    report_count[subject]+=1
        for sub in report_count: #딕셔너리는 for문으로 돌릴 때 인덱스으로는 못하나?
            if report_count[sub] >=k:
                suspension.append(sub)
        for check in suspension:
            for key in report_subject:
                for val in report_subject[key].split(','):
                    if check == val:
                        answer[id_index[key]]+=1
        
        return answer
    

    로 같은게 있는지 확인하는 for문 돌리고 그안에 스위치를 포함시켜 스위치 상태에 따라 딕셔너리들을 업데이트시켜주고 안 시켜주고 하니 해결 되었지만 하나의 케이스에 대하여 시간 초과가 떠서 제출실패하였다. 그래서 어떻게 줄일지 고민 하던 와중 다른 부분은 크게 영향이 없을 것 같지만 report는 길이가 200,000까지 가능하므로 관련 부분을 많이 줄여야 할 것 같았다. 그렇게 고민하다가 report_subject의 형태를 조금 바꾸면 되지 않을까 생각되어 시도 해보았다 기존의 아이디: ‘신고대상1, 신고대상2…’에서 아이디: [신고대상1, 신고대상2,…] 이런 식으로 하면 in을 써도 원소별로 구분이 잘 될 것이라 예상하여 그렇게 수정해 보았다

    def solution(id_list, report, k):
        answer = [0 for i in id_list]
        id_index={id:index for id,index in zip(id_list,range(len(id_list)))}
        report_subject={id:[] for id in id_list} #신고한 대상 저장
        report_count={id:0 for id in id_list} #신고카운트하는 딕셔너리
        suspension=[] #정지명단
        for contents in report:
            reporter,subject=contents.split()
            if subject not in report_subject[reporter]:
                report_subject[reporter].append(subject)
                report_count[subject]+=1
                    
        for sub in report_count: #딕셔너리는 for문으로 돌릴 때 인덱스으로는 못하나?
            if report_count[sub] >=k:
                suspension.append(sub)
                
        for check in suspension:
            for key in report_subject:
                if check in report_subject[key]:
                    answer[id_index[key]]+=1
                          
        return answer
    

    최종적으로 이렇게 시도 하니 빠르게 잘 되었고, 시간도 꽤 많이 단축되었다. 데이터들의 변수의 형태를 어떻게 해두냐에 따라 연산 속도가 많이 달라진다는 것을 다시금 느꼇다

    그 아래는 추가로 초반에 적을 때 막혔을 때 가능한 방식에 대해 테스트해본 부분만 적어둔 내용이다.

    #첫 시도 ->안됨
    for check in report_count:
            if int(check.value >=k:
                suspension.append(check.key)
        print(suspension)
    
    #두번째 시도 -> 안됨
    for i in range(len(report_count)):
            if report[i]>=k:
                suspension.append(check.key)
        print(suspension)
    
    #세번째 시도 ->됨
    for sub in report_count.keys(): #딕셔너리는 for문으로 돌릴 때 인덱스으로는 못하나?
            if report_count[sub] >=k:
                suspension.append(sub)
        print(suspension)
    
    #추가 실험 -> 키 들이 순서대로 프린트 됬음
    for j in report_subject:
            print(j)
    >>>>
    muzi
    frodo
    apeach
    neo
    이러면 굳이 keys라고 쓸필요 없을 듯
    
    for check in suspension:
            index=0 #하는 법 모르겠어서 수동 index
            for key in report_subject:
                if check in report_subject[key]:
                    answer[index]+=1
                index+=1
    

    인덱스 처음에 처리 방법 못 떠올려서 이런 식으로 수동으로 돌렸었는데 인덱스 딕셔너리를 새로 만들어서 해결하였다

    추가로 다른 분이 풀이 했던 코드 중에 객체 활용 풀이법이라고 제목이 되어 있던 분이 있으셨는데 객체 활용이 뭔지 몰라 이해하지 못하였지만 따로 공부해서 이해하면 도움이 될 것 같아 공부할 내용에 모아두고 시간 날 때 확인해볼 예정이다

     

    최댓값과 최솟값(python) (map, split)

    https://school.programmers.co.kr/learn/courses/30/lessons/12939

    문자열 s에는 공백으로 구분된 숫자들이 저장되어 있습니다. str에 나타나는 숫자 중 최소값과 최대값을 찾아 이를 "(최소값) (최대값)"형태의 문자열을 반환하는 함수, solution을 완성하세요. 예를들어 s가 "1 2 3 4"라면 "1 4"를 리턴하고, "-1 -2 -3 -4"라면 "-4 -1"을 리턴하면 됩니다. 라는 문제이다.

    def solution(s):
        s=list(map(int,s.split()))
        return str(min(s))+' '+str(max(s))
    

    따로 문자열 길이 제한 같은게 없어서 최대한 연산을 따로 안시키고 내장함수들을 이용해서 연산 할려고 하다보니 간단한 문제지만 생각보다는 조금 시간이 걸렸다

     

    JadenCase 문자열 만들기(python)

    https://school.programmers.co.kr/learn/courses/30/lessons/12951#

    JadenCase란 모든 단어의 첫 문자가 대문자이고, 그 외의 알파벳은 소문자인 문자열입니다. 단, 첫 문자가 알파벳이 아닐 때에는 이어지는 알파벳은 소문자로 쓰면 됩니다. (첫 번째 입출력 예 참고) 문자열 s가 주어졌을 때, s를 JadenCase로 바꾼 문자열을 리턴하는 함수, solution을 완성해주세요. 라는 문제이다.

    def solution(s):
        s=s.replace(' ','#')
        pos=[]
        s=s.lower()
        answer=''
        for i in range(len(s)):
            if (s[i] !='#' and s[i-1]=='#') or i==0:
                answer+=s[i].upper()
                #pos.append(i)
            else:
                answer+=s[i]
        answer=answer.replace('#',' ')
            
        return answer
    

    생각보다 은근 오래 걸려버렸다 (매우 긴 문자열에 대해서도 될 수 있음 좋겠다 생각하여 짜다보니) 하지만 시간이 생각보다 너무 오래걸리고 제한 조건이 길이 1이상 200이하라서 양에 비해 들이는 시간이 너무 비효율적인 것 같아 결국 단순히 끝냈다 공백문자를 굳이 #으로 바꾼 이유는 시각적으로 잘 보이지 않아서 확인차 겸 혹시나 조건 문등에서 놓치고 넘어 갈 수 있어서 입력으로 주어지지 않을 문자중에 아무거나 하나 골랐다. 사실 굳이 #으로 안 바꾸고 공백문자 그대로 해도 될 듯한데 해봐야 알 듯하다. 그리고 더 성능좋게도 가능할 듯한데 나중에 복습할 때 한번 생각해보도록 하겠다.

     

    이진 변환 반복하기(python) (count, replace, 문자열 슬라이스 문자열 뒤집기[::-1])  

    https://school.programmers.co.kr/learn/courses/30/lessons/70129

    0과 1로 이루어진 어떤 문자열에 대하여 이진 변환을 하는데 이진 변환은 먼저 모든 0을 제거하고 그 길이를 2진법으로 표현한 문자열로 정의한다. 이것을 반복하여 1만이 남을 때까지 이진 변환을 반복하는데 그때까지 변환한 횟수, 그리고 제거된 모든 0의 갯수를 각각 배열에 담아 같이 리턴하는 문제이다.

    def con_n(n,q): #n진법 변환기 (conversion to base n)
        rev_base = ''
    
        while n > 0:
            n, mod = divmod(n, q)
            rev_base += str(mod)
    
        return rev_base[::-1] #문자열로 리턴
    
    def solution(s):
        n=0 #변환 횟수
        m=0 #제거된 0의 갯수
        while s!='1':
            m+=s.count('0')
            s=s.replace('0','')
            s=con_n(len(s),2)
            n+=1
        answer = [n,m]
        return answer
    

    전에 찾아서 만들어 두었던 n진법 변환기와 그외는 문제에서 요구하는 그대로 만들었다

     

    피보나치 수(python) (피보나치 수열) (좀 더 깊이 공부할만한 내용들이 많은 내용포함)

    https://school.programmers.co.kr/learn/courses/30/lessons/12945

    n≥2인 n번째 파보나치 수에 대하여 구한뒤 1234567로 나눈 나머지를 리턴해주는 문제이다.

    def fib(n): #피보나치 수열 함수 (메모이제이션 방식) 빠르진 않을 듯
        fibList=[1, 1]
        if n==1 or n==2: #1, 2번째에 대해서는 1
            return 1
        for i in range(2,n): #그 뒤 리스트에 앞 두수를 더한 값을 n번째까지 추가시켜준다
            fibList.append( fibList[i-1] + fibList[i-2] )
            num=fibList[-1] #마지막 n번째 수를 리턴
        return num
    
    def solution(n):
        return fib(n)%1234567
    

    파보나치를 구하는 함수만 만들면 그 뒤로는 사실상 매우 간단하다

    파보나치 알고리즘에 관련하여 내용이 많던데 시간 날때 관련하여 공부하면 좋을 듯하다

     

    카펫(python)

    https://school.programmers.co.kr/learn/courses/30/lessons/42842

    대강 카펫 패턴이 가장자리 한줄 갈색으로 가운데 노란색으로 되어있는데 각 갈색과 노란색 칸의 수를 입력해주고 그것으로 만들어지는 직사각형의 가로와 세로 길이를 구하라는 문제이다.

    def divisors_list(num): #약수들의 리스트인데 살짝 손봐서 제곱수의 제곱근포함한 절반 이하만
        if num==1:
            return [1]
        return [i for i in range(1, (num // 2) + 1) if num % i == 0 and num//i >= i]
    
    def solution(brown, yellow):
        n=0 #가로 길이
        m=0 #세로 길이
        #(n-2)+(m-2)=B/2-2  #B:brown의 값
        #(n-2)*(m-2)=Y      #Y:yellow의 값
        check_num=divisors_list(yellow) #m-2가 될 수 있는 수
        for i in check_num: #i=m-2
            if yellow//i == (brown//2)-2-i:
                n=(yellow//i)+2
                m=i+2
                break
        answer = [n,m]
        return answer
    

    다음과 같은 식으로 인하여 계산해서 들어맞는 수를 m-2, n-2로하여 알맞게 n과 m을 리턴해주면 된다. 수학적으로 조금 풀어둔 뒤 코드를 짜니 간단하게 풀렸다

     

    +추가로 slpit()과 split(' ')의 차이에 대하여

    구분자를 지정해 주면 해당 구분자로 문자열을 구분해서 단어들의 리스트를 돌려주는 메소드인데,

    구분자를 지정해 주지 않으면 간단히 말했을 때는 공백으로 구분자가 지정되는데, 엄밀히 말하자면 연속된 공백 문자는 단일한 구분자로 간주 한다

    sep 이 지정되지 않거나 None 이면, 다른 분할 알고리즘이 적용됩니다: 연속된 공백 문자는 단일한 구분자로 간주하고, 문자열이 선행이나 후행 공백을 포함해도 결과는 시작과 끝에 빈 문자열을 포함하지 않습니다. 결과적으로, 빈 문자열이나 공백만으로 구성된 문자열을 None 구분자로 나누면 [] 를 돌려줍니다.

    라는 내용인데 문자열에 공백문자(띄어쓰기)가 여러개 있으면 그 결과가 명확히 구분 됨을 확인할 수 있다.

    이분의 블로그에서 알게 되었습니다   

    이분 블로그에서 다른 부분 검색하여 보다가 흥미로운 사실을 발견하여 추가로 인지하고 알게되어 간단히 정리하게 되었습니다.

    추가로 오늘은 정현석 튜터님의 SQL 특강이 있었는데 중간 중간 제대로 이해 못한 부분도 있어서 내일 녹화영상이 올라오면 보면서 같이 정리하여 내용을 적어보도록 하겠다

    '스파르타 > TIL(Today I Learned)' 카테고리의 다른 글

    2024-01-04  (0) 2024.01.04
    2024-01-03  (2) 2024.01.03
    2023-12-30~2023-01-01  (0) 2024.01.02
    2023-12-29  (1) 2023.12.29
    2023-12-28  (1) 2023.12.28
Designed by Tistory.