【问题标题】:Transform SQL JSONB array into columns by object key按对象键将 SQL JSONB 数组转换为列
【发布时间】:2015-11-25 09:19:12
【问题描述】:

我的表格结构如下:

CREATE TABLE artists (artist TEXT UNIQUE, facts JSONB);

INSERT INTO artists (artist, facts) 
  VALUES ('adele', '[{"type": "full_name", "value": "Adele Laurie"}, {"type": "age", "value": "25"}]');

INSERT INTO artists (artist, facts) 
  VALUES ('taylor', '[{"type": "age", "value": "25"}, {"type": "last_album", "value": "1989"}]');

事实“类型”的数量是固定的,但并不是每个艺术家都会拥有每个事实。如何选择包含每种事实类型的列和缺失事实名称的空值的结果?

期望的输出:

| artist |  full_name    | age  | last_album |
|--------|---------------|------|------------|
| adele  | Adele Laurie  | 25   | null       |
| taylor | null          | 25   | 1989       |

【问题讨论】:

  • 表轨道从何而来?
  • 哎呀,复制粘贴错误,现已修复
  • 第一行有一个定义的对象{"type": "full_name", "value": "Adele Laurie"},而第二行没有{"age": "25"}是第二个模式,或者是缺少一个类型,比如{"type": "age", "value": "25"}
  • 好的,明白了!这就是问题...
  • 天哪,你又是对的。对不起,错别字。是的,json 在两行中都是相同的结构(两者都有类型和值键)。我修好了。

标签: sql arrays postgresql jsonb


【解决方案1】:

你可以这样做:

select a.artist, 
     max(case when b.value->>'type' = 'full_name' 
         then b.value->>'value' 
         else b.value->>'full_name' end) as full_name,
     max(case when b.value->>'type' = 'age' 
         then b.value->>'value' 
         else b.value->>'age' end) as age,
     max(case when b.value->>'type' = 'last_album' 
         then b.value->>'value' 
         else b.value->>'last_album' end) as last_album
from artists a, 
     json_array_elements(a.facts) b
group by a.artist
order by a.artist

在这里查看:http://sqlfiddle.com/#!15/e376b/2

在小提琴中,我将字段创建为 JSON,因为没有可用的 JSONB 类型

如果您需要添加更多类型,只需像其他类型一样将其添加为案例条件。我想你可以从这里弄清楚:)

编辑

即使您更改了格式,此查询也应该可以解决您的问题。刚刚编辑了小提琴。在这里看到它:http://sqlfiddle.com/#!15/1c2b6/2

唯一的区别是你并不真的需要 else 的 case 语句。

这是没有 else 语句的查询

select a.artist, 
     max(case when b.value->>'type' = 'full_name' 
         then b.value->>'value' end) as full_name,
     max(case when b.value->>'type' = 'age' 
         then b.value->>'value' end) as age,
     max(case when b.value->>'type' = 'last_album' 
         then b.value->>'value' end) as last_album
from artists2 a, 
     json_array_elements(a.facts) b
group by a.artist
order by a.artist;

我在这里编辑了 SqlFiddle 链接。

【讨论】:

  • 效果很好!我在查看 sqlfiddle 时遇到问题。您可以修复链接并进行编辑以按原样回答问题吗?
  • 我已使用查询对其进行了编辑。 sqlfiddle 链接在这里对我有用。它有时会停止工作,最近它不稳定。再试一次。
【解决方案2】:

我会使用crosstab() 函数。 @Eriwn 真的有good answers 使用这种方法。

例子:

SELECT * FROM crosstab(
    'WITH data AS (
      SELECT artist,fact
      FROM artists a, jsonb_array_elements(a.facts) fact
    )
    SELECT artist,type,value
    FROM data,jsonb_to_record(data.fact) AS x(type text,value text)',
    'WITH data AS (
      SELECT artist,fact
      FROM artists a, jsonb_array_elements(a.facts) fact
     )
    SELECT DISTINCT type
   FROM data,jsonb_to_record(data.fact) AS x(type text,value text)'
) AS ct (artist text,age text,full_name text,last_album TEXT);

结果:

 artist | age  |  full_name   | last_album 
--------+------+--------------+------------
 adele  |      | Adele Laurie | 25
 taylor | 1989 |              | 25
(2 rows)

【讨论】:

  • 您正在使用四个 select 语句来执行只需要一个的操作。在大型数据集中,这将是一种矫枉过正。始终使用 K.I.S.S.方法:)
  • 是的,使用交叉表,您无法重用实际的文本 SQL 查询,除非您显示该查询。使用 2 个选择的缺点,但在 Json SQL 中添加更多属性不应该有很大的改变@JorgeCampos
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-18
  • 1970-01-01
  • 2021-02-14
  • 1970-01-01
相关资源
最近更新 更多