간단한 Python 구현을 통해 Prela의 핵심 원리와 관계 합성, 필터링, 다중 속성 선택을 알아보는 짧은 튜토리얼입니다.
Prela는 UCLA RePL에서 개발 중인 새로운 질의 언어입니다. 이 언어는 SQL과 상당히 다르지만, 핵심 아이디어는 매우 간단합니다. 이 짧은 튜토리얼에서는 핵심 원리를 이해하기 위해 Python으로 Prela의 장난감 버전을 만들어 보겠습니다. 튜토리얼을 마치면 다음 질의가 어떻게 작동하는지 알게 될 것입니다:
movie.where(company.s(country).eq("[us]") &
keyword.eq("character-name-in-title"))
.select(title & cast.s(person).s(alias).s(text))
무엇을 하는지 이미 짐작할 수 있을 것입니다. 이 질의는 미국 회사가 제작했고 제목에 등장인물 이름이 포함된 모든 영화를 찾아, 제목과 각 출연진 구성원의 별칭을 함께 출력합니다. 같은 기능의 SQL 질의는 20줄이 넘는다는 점에 주목하세요.
Prela의 첫 번째 특징은 이항 관계, 즉 두 개의 열을 가진 테이블만 사용한다는 것입니다. 처음에는 매우 제한적으로 들릴 수 있지만, 여러 열을 가진 넓은 테이블은 쉽게 "이항화"할 수 있습니다. 다음과 같은 영화 테이블이 있다고 가정해 봅시다:
| ID | 제목 | 연도 |
|---|---|---|
| 646 | 대부 | 1972 |
| 478 | 7인의 사무라이 | 1954 |
| 583 | 카사블랑카 | 1942 |
이 3열 테이블은 3개의 이항 관계로 분해할 수 있으며,1 각 관계는 행 번호를 열 값에 대응시킵니다:
팁
이 튜토리얼은 snip을 사용하여 코드 셀을 노트북과 유사한 환경으로 연결합니다.2 한 셀에서 변경한 내용은 이후 셀에 반영됩니다.
위의 movie, title, year 관계는 각각 원래 테이블의 ID, title, year 열을 나타냅니다. title과 year에서는 행 번호가 앞에 오지만, movie에서는 두 번째에 온다는 점에 주목하세요(movie는 ID라는 이름도 아닙니다). 그 이유는 나중에 명확해질 것입니다.
이항 관계에 집중하는 이유는 이들이 함수를 일반화하기 때문입니다. 함수는 _합성_될 수 있으므로 강력하며, 프로그램의 구성 요소가 됩니다. 함수는 모든 입력을 하나의 고유한 출력에 대응시키는 반면, 이항 관계는 하나의 입력을 여러 다른 출력에 대응시킬 수 있습니다. 어떤 의미에서 이항 관계는 비결정적 함수로 볼 수 있으며, 함수와 마찬가지로 합성할 수 있습니다.
이것은 모두 매우 추상적이므로, 예제로 돌아가 봅시다. 단순하게 유지하기 위해 모든 입력을 정확히 하나의 출력에 대응시키는 관계, 즉 우연히 모두 함수인 관계에 집중하겠습니다. 그러면 관계를 "호출"하는 것은 해당 관계를 사전으로 바꾸고 값을 조회하는 것으로 귀결됩니다:
이제 Prela의 첫 번째이자 가장 중요한 연산자인 관계 합성을 소개할 준비가 되었습니다. 함수 합성은 먼저 한 함수를 적용한 뒤, 그 출력을 다른 함수에 적용하는 방식으로 작동합니다. 두 관계 r과 s의 합성 역시 관계입니다. 먼저 r로 x를 대응시켜 어떤 y를 얻고, 이어서 y를 s로 대응시켜 최종 "출력"을 얻습니다. 이는 s를 사전 d로 바꾸고, r의 (x, y) 쌍을 순회한 다음, d에서 y를 찾을 수 있으면 (x, d[y])를 출력하여 구현할 수 있습니다:
예제를 사용하면, 아래 질의는 movie와 title을 합성하여 각 영화 ID를 제목에 대응시키는 관계를 얻습니다:3
title을 year로 바꾸어 어떤 결과가 나오는지 확인해 보세요. 합성의 힘은 여러 .select 호출을 연결할 때 진정으로 드러납니다. 각 영화를 제작 회사에 대응시키는 외래 키 열과 영화 회사용 테이블을 추가한다고 가정해 봅시다:
| ID | 제목 | 연도 | 회사 |
|---|---|---|---|
| ... | ... | ... | 0 |
| ... | ... | ... | 1 |
| ... | ... | ... | 2 |
| ID | 이름 | 국가 |
|---|---|---|
| 0 | 파라마운트 | [us] |
| 1 | 도호 | [jp] |
| 2 | 워너 브라더스 | [us] |
같은 방식으로 분해하면 네 개의 관계가 더 생깁니다:
그러면 .s로 줄여 쓰는 .select 호출 체인을 통해 영화 제작 회사의 국가를 찾을 수 있습니다:
외래 키를 통한 조인은 거의 항상 ID를 행으로 "해결"해야 하므로, Prela는 그 단계를 자동으로 삽입합니다. 따라서 다음과 같이 작성할 수 있으며,4 이는 마치 "영화의 회사의 국가"라고 읽히는 것과 같습니다!
이것은 튜토리얼 처음의 코드 조각 마지막 줄에 있는 cast.s(person).s(alias).s(text)에서도 일어난 일입니다.
지금까지 모든 질의는 단일 값 열을 반환했습니다. 여러 속성을 선택하기 위해 & 연산자를 도입하겠습니다.
.select가 r의 두 번째 열을 s의 첫 번째 열과 맞추는 반면, &는 r과 s 양쪽의 첫 번째 열을 기준으로 조인한 뒤 두 번째 열들을 쌍으로 묶습니다:
따라서 title & year는 모든 영화 행을 두 속성에 한 번에 대응시킵니다:
결과는 여전히 이항 관계이며, &는 단지 값을 튜플로 중첩할 뿐입니다. 이는 다른 관계와 마찬가지로 계속 합성할 수 있음을 의미하며, 질의가 둘 이상의 열을 반환하는 방식이기도 합니다:
다음으로 어떤 행을 원하는지 지정할 방법이 필요합니다. 술어 .eq(v)는 관계를 필터링하여 두 번째 열이 v와 같은 쌍만 유지합니다:
.eq만으로는 적용된 관계만 좁힙니다. 아래 질의는 여전히 영화 행을 국가에 대응시키지만, 더 이상 모든 행을 대응시키지는 않습니다:
마지막으로 제한 연산자 .where는 위와 같은 술어를 받아 다른 관계를 이를 기준으로 필터링합니다.
술어를 .where에 전달하면 영화에 대한 필터가 됩니다:
이는 코드 그대로 읽힙니다. "회사의 국가가 [us]인 영화"입니다.
질의가 길어지고 있으므로 리팩터링해 봅시다:
잠깐, 평범한 Python 변수로 CTE를 만든 것일까요? 맞습니다! Prela 질의는 연산자로 이루어져 있고 모든 부분식이 유효한 질의이므로 이것이 가능합니다.
여러 조건은 어떻게 지정할까요? &가 인수를 조인한다는 우연한 특성 덕분에, .where 안에 중첩되면 논리곱 역할도 합니다:
카사블랑카만이 미국 영화이면서 동시에 1942년 작품입니다. 모두 합치면, .select는 필터를 통과한 영화에서 보고 싶은 열을 가져옵니다:
더 깔끔한 질의를 위해 술어를 select 절로 밀어 넣을 수도 있습니다:
이것이 거의 언어의 전부입니다! Prela는 그룹화와 집계, 그 밖의 일반적인 연산자도 지원합니다. 언어의 전체 문서를 작성 중이므로, 지금은 더 자세한 내용은 논문을 참조하세요. 연습으로,5 맨 위의 코드 조각이 실행되도록 필요한 관계를 정의해 볼 수 있습니다.
장난감 Prela의 독립 실행형 Python 프로그램은 여기에서 찾을 수 있습니다.