之前我們討論過,傳遞單一參數進入查詢的作法了,但是,有時候我們需要傳遞進入查詢的資料並非單一變數,而是陣列 (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');
SELECT id, name, category
FROM products
WHERE category = ANY(ARRAY['Electronics', 'Furniture']);
SELECT id, name, category
FROM products
WHERE (category, name) = ANY(ARRAY[
ROW('Electronics', 'Laptop'),
ROW('Furniture', 'Desk Chair')
]);
同樣的語意,Datalog 都可以用精簡許多的語法來加以表現。
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]]
這邊有幾點值得特別注意:
:in
子句裡的 ...
,這個符號表示『陣列的解構運算』。如果要查詢『導演是 "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 。所謂的資料表其實就是有同樣形狀的數組的集合,如果我們忽略陣列是有序的、而集合是無序的實作細節,將數組的陣列視為是資料表是很合理的事。
註:
ROW
語法的範例,然而 ROW
語法並非 ANSI SQL 而是 Postgres 特有的語法。然而,我覺得 ROW
語意讀起來相對清晰,就選了非 ANSI SQL 的作法來做範例。實務上,使用 ANSI SQL 的作法,程式比較能夠套用到各種不同版本的 SQL。