對 SQL 有點研究的讀者,應該多少知道 SQL 有許多種不同的 join。但是,要說 SQL 有 7 種不同的 join 方式的話,我覺得也有點太誇張了。首先,觀察下圖可以發現,left join 與 right join 有對稱性;left join ... is null 與 right join ... is null 也有對稱性,所以立刻可以刪去兩種 join 的方式。
更進一步簡化的話,我們其實只需要三種語法,就足以湊出 7 種 join 的結果:
在之前的文章,我們已經可以發現,一般的 Datalog 語法,就已經可以做出 inner join 。換言之,我們想要利用 Datalog 來做出各種 SQL join 的結果,只要找出對應 left join ... is null 以及合併多個查詢結果 (union) 的 Datalog 語法即可。
假設我們要用 SQL 來查詢『那些在 person 表中存在,但是在 movie 表中沒有演出的演員』,可以寫成如下:
SELECT person.name
FROM person
LEFT JOIN movie
ON person.id = movie.cast
WHERE movie.cast IS NULL;
上述的寫法,我們可以先用另一種等價的 SQL 的寫法來加以改寫:
SELECT person.name
FROM person
WHERE NOT EXISTS (
SELECT 1
FROM movie
WHERE movie.cast = person.id
);
而改成對應的 Datalog 的話,則可以利用 not
或是 not-join
子句(註1):
[:find ?name
:where
[?p :person/name ?name]
(not [?m :movie/cast ?p])]
讀者有沒有覺得 SQL 與 Datalog 的寫法也長得有點像呢?仔細比較的話, Datalog 更有一致性喔。
假設我們要用 SQL 來查詢『所有人的名字與電影的名字之集合』,可以寫成如下:
SELECT name FROM person
UNION
SELECT title AS name FROM movie;
而改成對應的 Datalog 的話,則可以利用 or
或是 or-join
子句(註2):
[:find ?name
:where
(or
[?p :person/name ?name]
[?m :movie/title ?name])]
如果讀者順著前面的邏輯思考,要湊出 left join 最直覺的作法,大概是如下:
但是,其實不用這麼麻煩,因為還有更偷懶的表現方式,比方說,利用 Datalog 的 pull 查詢。(註3)
以下方的這個例子來講,這個查詢是先找出所有的人,等有了人的資料實體編碼 (entity id) 之後,再用 pull
去看他們是否有被任何的電影找去當演員。
[:find [(pull ?e [:person/name
:movie/_cast]) ...]
:where
[?e :person/name]]
註: