2015-01-15 79 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

谢谢你 - 我会在今天下午进行测试。看起来很有前途! –