[英]Tricky PostgreSQL join and order query
我在PostgreSQL 9.3.6數據庫中有四個表:
CREATE TABLE section (
id serial PRIMARY KEY,
title text,
"group" integer
);
CREATE TABLE fields (
id serial PRIMARY KEY,
title text,
section integer,
type text,
"default" json
);
CREATE TABLE entries (
id serial PRIMARY KEY,
section integer
);
CREATE TABLE data (
id serial PRIMARY KEY,
data json,
field integer,
entry integer
);
我正在嘗試生成一個如下所示的頁面:
section title
field 1 title | field 2 title | field 3 title
entry 1 | data 'as' json | data 1 json | data 3 json <-- table
entry 2 | data 'df' json | data 5 json | data 6 json
entry 3 | data 'gh' json | data 8 json | data 9 json
我現在設置的方式是,每個“數據”都有一個鏈接到的條目,一個對應的字段(該字段具有確定應如何解釋數據的json字段的列),一個json字段,用於存儲不同類型的數據和ID(表中此處為1-9)。
在此示例中,有3個條目和3個字段,並且中間的每個單元都有一個數據段。
之所以這樣設置,是因為一個部分的字段類型和數量可以與另一部分不同,因此數據的數量和類型也不同。
挑戰1:
我正在嘗試以一種可按任何列(該字段的json列的數據內容)排序的方式將表連接在一起。 例如,我希望能夠以相反的順序對字段3(第三列)進行排序,該表將如下所示:
section title
field 1 title | field 2 title | field 3 title
entry 3 | data 'gh' json | data 8 json | data 9 json
entry 2 | data 'df' json | data 5 json | data 6 json
entry 1 | data 'as' json | data 1 json | data 3 json <-- table
如果有更好的方法,我也樂於采用另一種方法。
挑戰2:
每個字段都有一個“默認值”列-理想情況下,當它們的值不是該默認值時,我只需要創建“數據”條目即可。 因此,如果字段2的默認值為'asdf',則該表實際上可能看起來像這樣:
section title
field 1 title | field 2 title | field 3 title
entry 3 | data 'gh' json | data 8 json | data 9 json
entry 2 | data 'df' json | 'asdf' | data 6 json
entry 1 | data 'as' json | 'asdf' | data 3 json <-- table
編寫此查詢的關鍵是要理解,您只需要獲取單個節的所有數據以及您剛剛加入的其余數據。 您也無法使用架構直接按節過濾數據,因此您僅需要為此而加入條目:
SELECT d.* FROM data d JOIN entries e ON (d.entry = e.id)
WHERE e.section = ?
然后,您可以將字段連接到每一行以獲取默認值,類型和標題:
SELECT d.*, f.title, f.type, f."default"
FROM data d JOIN entries e ON (d.entry = e.id)
JOIN fields f ON (d.field = f.id)
WHERE e.section = ?
或者,您可以在單獨的查詢中選擇字段以節省一些網絡流量。
所以這是一個答案,這里是獎金:
使用外鍵而不是整數來引用其他表,它將使數據庫檢查一致性。
關系(表)應按慣例以單數形式調用,因此它是section
, entry
和field
。
引用字段也稱為<name>_id
,例如,按照慣例也稱為field_id
或section_id
。
JSON字段的全部重點是存儲一個非靜態定義的數據的集合,因此,不使用entries
和data
表,而是使用包含所有字段的JSON的單個表,會更有意義。
像這樣:
CREATE TABLE row ( -- less generic name would be even better
id int primary key,
section_id int references section (id),
data json
)
data
字段包含以下內容:
{
"title": "iPhone 6",
"price": 650,
"available": true,
...
}
@Suor提供了很好的建議,您已經接受了其中的一些建議。 我建立在更新的架構上 。
CREATE TABLE section (
section_id serial PRIMARY KEY,
title text,
grp integer
);
CREATE TABLE field (
field_id serial PRIMARY KEY,
section_id integer REFERENCES section,
title text,
type text,
default_val json
);
CREATE TABLE entry (
entry_id serial PRIMARY KEY,
section_id integer REFERENCES section
);
CREATE TABLE data (
data_id serial PRIMARY KEY,
field_id integer REFERENCES field,
entry_id integer REFERENCES entry,
data json
);
我又更改了兩個細節:
section_id
代替id
,等等。“ id”作為列名是一種反模式,因為幾個ORM都在使用它。 別。 描述性名稱要好得多。 相同內容的相同名稱是有用的指南。 它還允許在join子句中使用快捷方式USING
:
不要使用保留字作為標識符。 僅使用合法的小寫,無引號的名稱可以使您的生活更輕松。
您的設計還有另一個固有的弱點。 是什么阻止data
條目引用field
和不在一起的entry
? 關於dba.SE的密切相關的問題
不知道您是否需要復雜的設計。 但是要回答這個問題,這是基本查詢:
SELECT entry_id, field_id, COALESCE(d.data, f.default_val) AS data
FROM entry e
JOIN field f USING (section_id)
LEFT JOIN data d USING (field_id, entry_id) -- can be missing
WHERE e.section_id = 1
ORDER BY 1, 2;
LEFT JOIN
對於允許丟失數據條目並使用默認值至關重要。
crosstab()
最后一步是交叉制表。 由於未安裝其他模塊tablefunc
因此無法在SQL Fiddle中顯示此內容。
crosstab()
基礎知識:
SELECT * FROM crosstab(
$$
SELECT entry_id, field_id, COALESCE(d.data, f.default_val) AS data
FROM entry e
JOIN field f USING (section_id)
LEFT JOIN data d USING (field_id, entry_id) -- can be missing
WHERE e.section_id = 1
ORDER BY 1, 2
$$
,$$SELECT field_id FROM field WHERE section_id = 1 ORDER BY field_id$$
) AS ct (entry int, f1 json, f2 json, f3 json) -- static
ORDER BY f3->>'a'; -- static
這里最棘手的部分是函數的返回類型。 我為3個字段提供了一個靜態類型,但您確實希望該動態類型。 另外,我引用的是json
類型的字段,該字段可能存在或可能不存在。因此,動態構建該查詢並在第二次調用中執行它。
有關更多信息:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.