需要从 postgresql table 动态地 select 一个 JSON 数组元素
Need to select a JSON array element dynamically from a postgresql table
我有如下数据:
ID Name Data
1 Joe ["Mary","Joe"]
2 Mary ["Sarah","Mary","Mary"]
3 Bill ["Bill","Joe"]
4 James ["James","James","James"]
我想编写一个查询,其中 select 是数组中的最后一个元素,它不等于名称字段。比如我要查询到return下面的结果:
ID Name Last
1 Joe Mary
2 Mary Sarah
3 Bill Joe
4 James (NULL)
我越来越接近了 - 我可以 select 使用以下查询的最后一个元素:
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
但是,我还需要一个级别——评估最后一项,如果它与名称字段相同,则尝试倒数第二个字段,依此类推。
任何帮助或指点将不胜感激!
json
在 Postgres 9.3
这在 pg 9.3 中很难,因为缺少有用的功能。
方法一
在 LEFT JOIN LATERAL
中取消嵌套(干净且符合标准),trim 双引号从 json
转换为 text
后。请参阅下面的 links。
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;
虽然这行得通,而且我从未见过它失败,但未嵌套元素的顺序取决于未记录的行为。请参阅下面的 links!
使用表达式 改进了从 json
到 text
的转换。仍然很黑,但应该是安全的。
方法二
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;
- 在
SELECT
列表中取消嵌套(非标准)。
- 并行附加行号(
rn
)(更可靠)。
- 像上面那样转换为
text
。
-
ORDER BY
子句中的表达式 (last = name)
将匹配的名称最后排序(但在 NULL 之前)。因此只有在没有其他名称可用时才会选择匹配的名称。最后 link 下面。
在 SELECT
列表中,NULLIF
将匹配名称替换为 NULL
,得到与上面相同的结果。
json
或 jsonb
在 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
使用 jsonb_array_elements_text()
。其他都一样。
json / jsonb functions in the manual
更多解释的相关答案:
- How to turn json array into postgres array?
- PostgreSQL unnest() with element number
- Index for finding an element in a JSON array
- Time based priority in Active Record Query
- Select first row in each GROUP BY group?
我有如下数据:
ID Name Data
1 Joe ["Mary","Joe"]
2 Mary ["Sarah","Mary","Mary"]
3 Bill ["Bill","Joe"]
4 James ["James","James","James"]
我想编写一个查询,其中 select 是数组中的最后一个元素,它不等于名称字段。比如我要查询到return下面的结果:
ID Name Last
1 Joe Mary
2 Mary Sarah
3 Bill Joe
4 James (NULL)
我越来越接近了 - 我可以 select 使用以下查询的最后一个元素:
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
但是,我还需要一个级别——评估最后一项,如果它与名称字段相同,则尝试倒数第二个字段,依此类推。
任何帮助或指点将不胜感激!
json
在 Postgres 9.3
这在 pg 9.3 中很难,因为缺少有用的功能。
方法一
在 LEFT JOIN LATERAL
中取消嵌套(干净且符合标准),trim 双引号从 json
转换为 text
后。请参阅下面的 links。
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;
虽然这行得通,而且我从未见过它失败,但未嵌套元素的顺序取决于未记录的行为。请参阅下面的 links!
使用表达式 json
到 text
的转换。仍然很黑,但应该是安全的。
方法二
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;
- 在
SELECT
列表中取消嵌套(非标准)。 - 并行附加行号(
rn
)(更可靠)。 - 像上面那样转换为
text
。 -
ORDER BY
子句中的表达式(last = name)
将匹配的名称最后排序(但在 NULL 之前)。因此只有在没有其他名称可用时才会选择匹配的名称。最后 link 下面。 在SELECT
列表中,NULLIF
将匹配名称替换为NULL
,得到与上面相同的结果。
json
或 jsonb
在 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
使用 jsonb_array_elements_text()
。其他都一样。
json / jsonb functions in the manual
更多解释的相关答案:
- How to turn json array into postgres array?
- PostgreSQL unnest() with element number
- Index for finding an element in a JSON array
- Time based priority in Active Record Query
- Select first row in each GROUP BY group?