iT邦幫忙

2024 iThome 鐵人賽

DAY 10
0
Software Development

Datomic,內建事件溯源的資料庫。系列 第 10

先從 Datalog 談起 -- part 5 (parameterized queries)

  • 分享至 

  • xImage
  •  

之前我們討論過,傳遞單一參數進入查詢的作法了,但是,有時候我們需要傳遞進入查詢的資料並非單一變數,而是陣列 (array)、甚至是數組 (tuple) 的陣列。

上述的情況,使用 SQL 的話,可以搭配 ANY 語法來處理。(註1)(註2)

  • 資料庫設定
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name TEXT,
    category TEXT
);

INSERT INTO products (name, category) VALUES
('Laptop', 'Electronics'),
('Smartphone', 'Electronics'),
('Desk Chair', 'Furniture'),
('Coffee Table', 'Furniture'),
('Headphones', 'Electronics');
  • 傳遞陣列進入 SQL 查詢
SELECT id, name, category
FROM products
WHERE category = ANY(ARRAY['Electronics', 'Furniture']);
  • 傳遞數組的陣列進入 SQL 查詢
SELECT id, name, category
FROM products
WHERE (category, name) = ANY(ARRAY[
    ROW('Electronics', 'Laptop'),
    ROW('Furniture', 'Desk Chair')
]);

同樣的語意,Datalog 都可以用精簡許多的語法來加以表現。

Datalog 的變數綁定形式 (binding form)

Datalog 的變數綁定有四種形式。(註3)

綁定形式 (binding form) 傳入的資料形態
?a 常量 (scalar)
[?a ?b] 數組 (tuple)
[?a ...] 陣列 (collection)
[[?a ?b]] 數組的陣列 (relation)

之前已經示範過傳入常量的變數綁定形式了,現在要來探討其它的綁定形式

傳入數組

如果要查詢『導演是 "James Cameron",演員是 "Arnold Schwarzenegger" 的電影名稱』,我們可以寫成如下的查詢,並且傳遞數組 ["James Cameron" "Arnold Schwarzenegger"] 進去當這個查詢的參數。

[:find ?title
 :in $ [?director ?actor]
 :where
 [?d :person/name ?director]
 [?a :person/name ?actor]
 [?m :movie/director ?d]
 [?m :movie/cast ?a]
 [?m :movie/title ?title]]

在這個例子裡,數組會在 :in 子句被解構 (destructure) ,因而可以讓 "James Cameron" 對應到 ?diretor ,同時讓 "Arnold Schwarzenegger" 對應到 ?actor

傳入陣列

如果要查詢『導演是 "James Cameron" "Ridley Scott" 的電影名稱』,我們可以寫成如下的查詢,並且傳遞 ["James Cameron" "Ridley Scott"] 這個陣列進去。

[:find ?title
 :in $ [?director ...]
 :where
 [?p :person/name ?director]
 [?m :movie/director ?p]
 [?m :movie/title ?title]]

這邊有幾點值得特別注意:

  1. 在查詢的 :in 子句裡的 ... ,這個符號表示『陣列的解構運算』。
  2. 傳入陣列,其實就是一種邏輯的或 (or) 運算

傳入數組的陣列

如果要查詢『導演是 "James Cameron" 的電影名稱與票房收入』,我們可以寫成如下的查詢:

[:find ?title ?box-office
 :in $ ?director [[?title ?box-office]]
 :where
 [?p :person/name ?director]
 [?m :movie/director ?p]
 [?m :movie/title ?title]]

並且傳遞單一的常量 "James Cameron" 與下方數組的陣列進去查詢裡:

[
 ...
 ["Die Hard" 140700000]
 ["Alien" 104931801]
 ["Lethal Weapon" 120207127]
 ["Commando" 57491000]
 ...
]

傳遞數組的陣列,也可以看成是 Datalog 的查詢與『外部的資料表』做 join 。所謂的資料表其實就是有同樣形狀的數組的集合,如果我們忽略陣列是有序的、而集合是無序的實作細節,將數組的陣列視為是資料表是很合理的事。

練習題

註:

  1. 傳遞數組的陣列進入 SQL 查詢,我選了使用 ROW 語法的範例,然而 ROW 語法並非 ANSI SQL 而是 Postgres 特有的語法。然而,我覺得 ROW 語意讀起來相對清晰,就選了非 ANSI SQL 的作法來做範例。實務上,使用 ANSI SQL 的作法,程式比較能夠套用到各種不同版本的 SQL。
  2. Datalog -- part 4 一文裡的的參數化查詢,我是用 prepared statement 來舉例,本文的例子一樣也可以改成 prepared statement 的版本。
  3. scalar, tuple, colllection, relation 這四個英文字是 Datomic 官方文件的用法。儘管 collection 的中文通常不是翻譯成「陣列」;relation 的中文通常也不是翻譯成「數組的陣列」。但此處我用「陣列」一詞,讀者就可以理解,它跟引言裡的「陣列」是同樣的概念,而「數組的陣列」也是同樣的道理。

其它資源

  1. 歡迎訂閱 PruningSuccess 電子報,主要談論軟體開發、資料處理、資料分析等議題。
  2. 歡迎加入 Clojure 社群

上一篇
先從 Datalog 談起 -- part 4 (parameterized queries)
下一篇
先從 Datalog 談起 -- part 6 (more queries)
系列文
Datomic,內建事件溯源的資料庫。30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言