![](/img/trans.png)
[英]How to set composite type array containing JSONB elements with a literal value?
[英]How to match elements in an array of composite type?
假設我們有兩個表:
CREATE TABLE element (
pk1 BIGINT NOT NULL,
pk2 BIGINT NOT NULL,
pk3 BIGINT NOT NULL,
-- other columns ...
PRIMARY KEY (pk1, pk2, pk3)
);
CREATE TYPE element_pk_t AS (
pk1 BIGINT,
pk2 BIGINT,
pk3 BIGINT
);
CREATE TABLE collection (
id BIGINT,
elements element_pk_t[] NOT NULL,
);
該element
具有復合 PK。 自定義類型element_pk_t
注冊匹配的復合類型。 collection
表包含element_pk_t
數組。
我想在單個查詢中查詢表element
中 PK 與所選collection.elements
中的元素匹配的所有行。
我試過的:
SELECT *
FROM element
WHERE (pk1, pk2, pk3) IN (SELECT unnest(elements)
FROM collection
WHERE id = 1);
我在IN
子句中收到錯誤消息:
錯誤:子查詢的列太少
但是,這有效:
SELECT *
FROM element
WHERE (pk1, pk2, pk3) IN ((1, 2, 3), (4, 5, 6));
所以看起來問題是如何將自定義類型element_pk_t
擴展到可以匹配(pk1, pk2, pk3)
3 列。
這有效:
SELECT *
FROM element
WHERE (pk1, pk2, pk3) IN (SELECT (unnest(elements)).*
FROM collection
WHERE id = 1);
或者更詳細,但更可取:
SELECT *
FROM element
WHERE (pk1, pk2, pk3) IN (SELECT (e).*
FROM collection c, unnest(c.elements) e
WHERE c.id = 1);
更健壯並避免多次評估unnest()
。 看:
這也有效:
SELECT *
FROM element
WHERE ROW((pk1, pk2, pk3)) IN (SELECT unnest(elements)
FROM collection
WHERE id = 1);
問題的核心是采用子查詢的IN
知道兩種不同的形式。 引用手冊:
expression IN (subquery)
row_constructor IN (subquery)
您失敗的查詢解析為第二種形式,而您(可以理解)期望第一種形式。 但是第二種形式是這樣做的:
這種形式的
IN
的左側是一個行構造函數,如第 4.2.13 節所述。 右側是一個帶括號的子查詢,它必須返回與左側行中的表達式一樣多的列。 左邊的表達式被評估並與子查詢結果的每一行逐行比較。 [...]
我的第一個和第二個查詢通過分解運算符右側的行類型來使其工作。 所以 Postgres 有 left 和 right 三個bigint
值並且滿足。
我的第三個查詢通過將行類型嵌套到另一個行構造函數的左側來使其工作。 Postgres 只分解第一層並最終得到一個單一的復合類型——匹配右邊的單一復合類型。
請注意,我們要包裝的單個字段需要關鍵字ROW
。 手冊:
當列表中有多個表達式時,關鍵字
ROW
是可選的。
您的工作查詢略有不同,因為它提供了右側的值列表,而不是子查詢( set )。 這是采用不同代碼路徑的不同實現。 它甚至在手冊中有單獨的章節。 此變體對左側的 ROW 構造函數沒有特殊處理。 所以它只是按預期工作(由你)。
更多等效(工作)語法變體= ANY
:
SELECT * FROM element
WHERE (pk1, pk2, pk3) = ANY ('{"(1,2,3)","(2,3,4)"}'::element_pk_t[]);
SELECT * FROM element
WHERE (pk1, pk2, pk3) = ANY (ARRAY[(1,2,3)::element_pk_t,(2,3,4)::element_pk_t]);
SELECT * FROM element
WHERE (pk1, pk2, pk3) = ANY (ARRAY[(1,2,3),(2,3,4)]::element[]);
也適用於(pk1, pk2, pk3)::element_pk_t
或ROW(pk1, pk2, pk3)::element_pk_t
看:
由於您的源是一個array ,丹尼爾的第二個查詢(e.pk1, e.pk2, e.pk3) = ANY(c.elements)
自然適用。
但是對於最快查詢的賭注,我的錢是在我的第二個變體上,因為我希望它能夠最佳地使用 PK 索引。
就像概念證明一樣。 就像 a_horse 評論的那樣:標准化的數據庫設計可能會最好地擴展。
您需要進行額外級別的解包,以便使用 IN 子句使其工作。 需要從子查詢返回一組常規字段以與外部行進行比較。
SELECT *
FROM element
WHERE row(pk1, pk2, pk3) IN (
SELECT (x.el).pk1, (x.el).pk2, (x.el).pk3
FROM collection
CROSS JOIN LATERAL (SELECT unnest(elements) el FROM collection) x
WHERE id = 1
)
清潔器將通過檢查元素是否在連接子句中的數組中來連接。
SELECT e.*
FROM element e
INNER JOIN collection c ON row(e.pk1, e.pk2, e.pk3) = ANY(c.elements)
AND c.id = 1
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.