2015-01-15 81 views
0

我有數據如下:需要從PostgreSQL表動態地選擇一個JSON數組元素

ID Name Data 
1 Joe  ["Mary","Joe"] 
2 Mary ["Sarah","Mary","Mary"] 
3 Bill ["Bill","Joe"] 
4 James ["James","James","James"] 

我想要編寫從陣列,這不等於名稱字段選擇的最後一個元素的查詢。例如,我想查詢返回的結果如下:

ID Name Last 
1 Joe Mary 
2 Mary Sarah 
3 Bill Joe 
4 James (NULL) 

我越來越近 - 我可以選擇使用以下查詢的最後一個元素:

SELECT ID, Name, 
(Data::json->(json_array_length(Data::json)-1))::text AS Last 
FROM table; 

ID Name Last 
1  Joe  Joe 
2  Mary Mary 
3  Bill Joe 
4  James James 

不過,我需要一個更公平 - 評估最後一個項目,如果它與名稱字段相同,則嘗試倒數第二個字段,等等。

任何幫助或指針將不勝感激!

+1

Crucial:你的Postgres版本?數據類型爲「json」還是「jsonb」? –

+0

PostgreSQL版本? –

回答

2

json了在Postgres 9.3

這是很難在PG 9.3,因爲有用的功能缺失。

方法1

UNNEST在LEFT JOIN LATERAL鑄造text後(清潔和符合標準的),修剪從json雙引號。請參閱以下鏈接。

SELECT DISTINCT ON (1) 
     t.id, t.name, d.last 
FROM tbl t 
LEFT JOIN LATERAL (
    SELECT ('[' || d::text || ']')::json->>0 AS last 
    FROM json_array_elements(t.data) d 
) d ON d.last <> t.name 
ORDER BY 1, row_number() OVER() DESC; 

雖然這個工作,並且我從未見過它失敗,未發生的元素的順序取決於無證行爲。看到下面的鏈接!
改進了從jsontext的轉換,表達式爲provided by @pozs in the comment。仍然hackish,但應該是安全的。

方法2

SELECT DISTINCT ON (1) 
     id, name, NULLIF(last, name) AS last 
FROM (
    SELECT t.id, t.name 
     ,('[' || json_array_elements(t.data)::text || ']')::json->>0 AS last 
     , row_number() OVER() AS rn 
    FROM tbl t 
    ) sub 
ORDER BY 1, (last = name), rn DESC; 
  • UNNEST在SELECT列表(非標準)。
  • 並行連接行號(rn)(更可靠)。
  • 轉換爲text像上面一樣。
  • 子句中的表達式(last = name)最後(但在NULL之前)對匹配名稱進行排序。因此只有在沒有其他名稱可用的情況下才會選擇匹配的名稱。最後一個鏈接 在SELECT列表中,NULLIF將替換與NULL匹配的名稱,得到與上述相同的結果。

SQL Fiddle.

jsonjsonb了在Postgres 9.4

PG 9.4船舶所有必要的改進:

SELECT DISTINCT ON (1) 
     t.id, t.name, d.last 
FROM tbl t 
LEFT JOIN LATERAL json_array_elements_text(data) WITH ORDINALITY d(last, rn) 
     ON d.last <> t.name 
ORDER BY d.rn DESC; 

使用jsonb_array_elements_text()jsonb。其他所有相同。

json/jsonb functions in the manual

相關答案與更多的解釋:

+1

很好的答案,但修剪'json'字符串可能會失敗,如果該字符串包含一些轉義。有一個(hack-ish,我知道,但是)在9.3中進行「cast」更可靠:'('['|| d :: text ||']'):: json - >> 0' http://sqlfiddle.com/#!15/99d11/1 – pozs

+0

謝謝。這也是[Marti Raudsepp在關於plpgsql-general的討論中提出的建議](http://www.postgresql.org/message-id/flat/[email protected]om)。這比我以前的替代品更好,所以我更新了你的表情。 –

+0

謝謝你 - 我會在今天下午進行測試。看起來很有前途! –