데이터과학 유망주의 매일 글쓰기 — 46일차

배우는 자(Learner Of Life)
11 min readNov 16, 2020

--

생각보다 복잡한 데이터베이스의 가족 관계

# schema, #관계, #데이터베이스, #SQLite

데이터베이스는 생각보다 서로 복잡한 관계를 갖고 있다.

오늘 한일:

오늘 부터는 SQL을 탐구하는, 일주일간의 새로운 작은 여정을 시작했다. SQL은 Structured Query Language(구조화된 쿼리 언어)의 약자로써, 관계형 데이터베이스(Relational Database)에 접근할 수 있는 언어이다. 파이썬에서 Pandas를 통해 데이터를 탐구하는 방법과 매우 비슷하지만, 파이썬에서는 프로그램이 동작하는 동안의 Runtime이 끝나면 데이터가 저장되지 않는 단점이있었다. 이를 보완하고, 데이터를 장기적으로 저장하여 원할 때마다 접근할 수 있게 해주는 것이 SQL 기반 관계형 데이터베이스였다.

관계형 데이터베이스(Relational Database)의 관계

Primary Key & Foreign Key

SQL기반 관계형 데이터베이스는 생각보다 상당히 복잡한 관계를 가질 수 있는데, 데이터가 서로 primary key와 foreign key라는 것으로 연결되어 있기 때문이다. 예를 들면 아래와 같은 Schema를 가질 수 있다.

SQL기반 관계형 데이터베이스의 전체적인 관계를 보여주는 스키마(schema) (1)

예를 들어, 위 customers라는 테이블과 employees라는 테이블 사이에는, SupportRepId와 EmployeeId라는 key가 연결된 것을 알 수 있다. 여기서 employees를 기준으로 볼 때, customer와 연결하는 primary key는 EmployeeId이며, customers에 존재하는 SupportRepId는 foreign key로 볼 수 있다. Primary Key는 다른 테이블과 연결하기 위해 employees테이블이 갖는 키이며, SupportRepId는 customers테이블이 가지고 있지 않은 key이지만, EmployeeId와 같은 값을 가지고 있다고 보기 때문에 Foreign Key인 것이다. 여기서 주의할 것은 두 테이블의 Key 이름이 다르더라도, 두 키에 해당하는 값이 모두 같기 때문에 연결이 가능하다는 것이다.

관계 종류

관계형 데이터베이스의 관계는 크게 4가지로 나뉜다. 자가 참조(self-referencing)는 한 테이블 내에서의 관계를 말하며, 이외의 세 경우는 두 개 이상의 테이블 간 관계로 볼 수 있다. 각 관계를 좀 더 명확하게 보여주기 위해 SQL대신 파이썬을 사용해 표현하였다. 주로 행(row)간의 관계를 표현하는데, 데이터의 각 행은 레코드(record)라 부른다.

  • 자가 참조(self-referencing)
  • 1:1 관계
  • 1:N 관계
  • N:N 관계
  1. 자가 참조(self-referencing):

테이블 내에서 관계를 정의할 필요가 있는 경우가 있다. 예를 들어 내가 속한 회사에서, 내가 보고 해야할 상사들과 나의 관계를 명확하게 할 필요가 있을 수 있다. 또한, 넷플릭스같은 인터넷 서비스에 가입할 때, 추천인을 입력하여 보너스 크레딧 등을 얻고자 하는 경우에 활용될 수 있다.

# 자가 참조의 예from pandas import DataFrameprint("넷플릭스 추천인\n")print(DataFrame({"Customers":["승태", "도일", "명한"], "reference_id":['없음', 0, 0]}))
자가 참조 예시. 도일은 명한을 추천하고, 명한도 도일을 추천했으나, 승태는 아무도 추천하지 않았다.

추천인 입력의 성격상, 한 명은 각자 한 사람만 추천을 할 수 있지만, 추천인을 입력하는데 있어서는 제한이 없다. 승태를 아무도 추천인으로 입력하지 않았지만, 도일과 명한은 추천인으로 입력이 된 것을 알 수 있다. 아무래도 도일은 명한을 추천하고, 명한도 도일을 추천했으나, 승태는 아무도 추천하지 않은 것임을 알 수 있다. 이 처럼 데이터 안에서도, 서로 다른 행(row)끼리의 관계를 정의할 수 있게 하는 것이 자가 참조(self-referencing)관계이다.

2. 1:1 관계

한 테이블의 레코드와 다른 테이블의 한 레코드가 연결되어 있는 관계를 말한다. 즉 1:1로 맵핑이 되어있는 경우를 말한다.

# 1:1 관계의 예from pandas import DataFrameprint("넷플릭스 추천인 정보 1\n")print(DataFrame({"Customers":["승태", "도일", "명한"], "Job":["데이터 사이언티스트", "머신러닝 엔지니어", "딥러닝 엔지니어"], "contact_id":[1000, 1001, 1002]}))print(DataFrame({"Customers":["승태", "도일", "명한"], "reference_id":['없음', 0, 0]}))print('\n\n')print("넷플릭스 추천인 정보 2\n")print(DataFrame(index=[1000, 1001, 1002], data={"Contact":["010-1234-5678", "010-2345-6789", "011-3456-7890"]}))
1:1의 관계를 표현한 예

첫 번째 테이블에서 contact_id의 Key가 그대로 두 번째 테이블의 index와 똑같은 값으로 맵핑되어 있는 것을 볼 수 있다. 첫 번째 테이블에서 각각의 레코드가 두 번째 테이블의 각각의 레코드에 맵핑되어 있으므로 1:1의 관계로 볼 수 있다. 여기서 첫 번째 테이블의 contact_id가 두 번째 테이블의 Foreign key 역할을 하며, 두 번재 테이블의 index는 Primary Key라고 할 수 있다.

하지만, 자세히 보면 이렇게 데이터를 관리하는 것이 상당히 비효율적임을 알 수 있다. 하나의 테이블에 전화번호 정보인 contact 정보도 같이 포함할 수 있기 때문이다. 용량이 너무 커서 같이 저장하기 힘든 경우가 아니라면, 또는 특별한 관리 목적이 있는 것이 아니라면, 굳이 두 개의 테이블을 나누어서 봐야할 필요성이 떨어진다는 것이다. 왠만하면 1:1에서는 서로 연결되어있는 Key를 기준으로(첫 번째 테이블의 contact_id와 두 번째 테이블의 index) 같이 입력해 주는 것이 좋다. 따라서 1:1 관계를 정의하는 것은, 관계형 데이터베이스를 사용하는 의의를 상대적으로 떨어뜨리기 때문에, 상당히 드문 경우라 볼 수 있다.

3. 1:N 관계

한 테이블에서 하나의 레코드가 다른 테이블에 있는 두 개 이상의 레코드에 맵핑된 경우를 말한다.

# 1:N 관계의 예from pandas import DataFrameprint("넷플릭스 추천인 정보 1\n")print(DataFrame(index=[1000, 1001, 1002], data={"Order Item":["500일의 여름", "킹덤", "데어데블"], "customer_id":[0, 0, 1]}))print('\n\n')print("넷플릭스 추천인 정보 2\n")print(DataFrame({"Customers":["승태", "도일", "명한"], "Job":["데이터 사이언티스트", "머신러닝 엔지니어", "딥러닝 엔지니어"]}))
1:N의 관계의 예

위 예를 보면, 한 사람이 여러가지를 주문할 수 있지만, 각 주문은 한 사람에게만 맵핑될 수 있다는 것을 알 수 있다. 예를 들어, 첫 번째 테이블에서 customer_id는 두 개가 0인 것을 볼 수 있다. 이는 승태가 “500일의 여름"과 “킹덤" 두 가지의 상품을 주문했기 때문이다. 한 사람이 여러개를 주문했지만, 각 주문은 오직 한 사람에게만 지정된다. 나머지 하나인 “데어데블"은 도일이 주문한 것을 알 수 있다. 관계형 데이터베이스에서 가장 흔히 볼 수 있는 경우라 할 수 있다.

4. N:N 관계

한 테이블의 여러 레코드가 다른 테이블의 여러 레코드와 연결되는 관계를 말한다. 하나의 레코드가 여러개에 연결될 수 있고, 연결된 레코드도 역시 여러개의 다른 레코드에 연결될 수 있기 때문에, 가장 복잡한 형태라 할 수 있다.

# N:N 관계의 예from pandas import DataFrameprint("넷플릭스 추천인 정보 1\n")print(DataFrame(index=[1000, 1001, 1002], data={"Order Item":["500일의 여름", "킹덤", "데어데블"], "customer_id":[0, 0, 1]}))print('\n\n')print("넷플릭스 추천인 정보 2\n")print(DataFrame({"Customers":["승태", "도일", "명한"], "Job":["데이터 사이언티스트", "머신러닝 엔지니어", "딥러닝 엔지니어"]}))print('\n\n')print("넷플릭스 추천인 정보 3\n")print(DataFrame(data={"customer_id":[0, 0, 1, 2, 1], "product_id":[1000, 1001, 1002, 1001, 1000]}))
N:N 관계의 예

위 예에서는 한 사람이 여러 개의 상품을 주문할 수 있으며, 한 상품 역시 여러 사람들에게 주문 될 수 있다. 세 번째 테이블은 각 사람을 구분하는 customer_id와 각 상품을 구분하는product_id를 연결해 주는 역할이며, 이를 통해 각 사람이 어떤 상품을 주문했고, 각 상품이 어떤 사람들에게 주문되었는지 알 수 있다. 주의할 것은, 이렇게 세 번째 테이블처럼 이전 테이블과 새로운 Key를 바탕으로 테이블을 생성하게되면, 동일하게 Primary Key가 있어야 한다는 것이다. (두 번째 테이블에서의 Index와 세 번째 테이블의 customer_id, 또는 첫 번째 테이블의 index와 세 번째 테이블의 product_id)

앞으로 할일:

오늘은 관계형 데이터베이스의 다양한 관계의 종류에 대해 알아보았다. 막상 SQL언어를 배웠을 때는 어렵지 않았지만, 이를 통해 테이블 사이의 좀 더 복잡한 관계를 표현하려고 하니, 정말 쉽게 “더 어려워졌다.” 물론, 여전히 주어진 과제를 하는데는 성공하기는 했지만.

일단 SQL은 pandas에 비해 훨씬 더 언어가 직관적이다. 그래서, 오히려 언어 자체는 좀 더 쉽게 느껴졌던 것 같다. 물론, 파이썬처럼 pandas같은 놀라운 라이브러리를 지원하여 사용자가 편리하게 알아서 다 처리해주지는 않는다. 그래서, 오히려 언어를 사용하여 내가 원하는 것을 표현하는데 있어서는, 상대적으로 더 단순한 SQL이 더 어렵게 느껴진 것 같다.

오늘 SQL을 하면서, 좀 더 낮은 레벨의 언어를 배우는 게 그렇게 나쁘지는 않다는 생각이 들었다. 한편으로는, 좀 더 높은 레벨의 언어이지만, 많은 반복적인 일을 자동화 해주는 파이썬의 편리한 여러 라이브러리들이 좀 더 감사하게 느껴지기도 했다. 정말, 이런 라이브러리를 자동화하여 배포한 사람들에게 큰 존경심이 들지 않을 수 없었다. 그들 덕분에 정말 코딩을 배우고 사용하기 편해졌다.

내일까지는 SQL기반 데이터베이스를 배우고, 모래부터는 NoSQL기반 데이터베이스를 배운다고 한다. 대표적으로 MongoDB가 있는데, 참 반가운 소식이다. 내가 예전에 참여한 주말반 부트캠프에서 MongoDB를 활용하여 개인 프로젝트를 해 본 경험이 있기 때문이다. 그래서, 좀 더 기대된다. 과연 수요일에 배울 내용은 오늘과 내일 보다 어떻게 다를지.

일단, 조금 어렵게 느껴졌던 SQL과 좀 더 친해져서 다행이다. 앞으로도 친해질 수 있도록 좀 더 많은 연습을 해야겠다고 다짐해본다.

참조:

(1) https://www.sqlitetutorial.net/sqlite-sample-database/

--

--

배우는 자(Learner Of Life)
배우는 자(Learner Of Life)

Written by 배우는 자(Learner Of Life)

배움은 죽을 때까지 끝이 없다. 어쩌면 그게 우리가 살아있다는 증거일지도 모른다. 배움을 멈추는 순간, 혹은 배움의 기회가 더 이상 존재하지 않는 순간, 우리의 삶은 어쩌면 거기서 끝나는 것은 아닐까? 나는 배운다 그러므로 나는 존재한다. 배울 수 있음에, 그래서 살아 있음에 감사한다.

No responses yet