从 Postgres jsonb 属性获取不同记录的计数和总和

Man*_*rte 2 postgresql json

我有这个 Postgres 9.6 表定义:

CREATE TABLE research (
   id int,
   data jsonb
);
Run Code Online (Sandbox Code Playgroud)

并在其中包含以下示例数据

 id |data
 ---|--------------------------------------------------------
 1  |{"name": "Sim Ltd", "sector": "business", "personnel": {"headcount": {"total": 100, "male": 50, "female": 50}}}
 2  |{"name": "EcoSmart", "sector": "business", "personnel": {"headcount": {"total": 500, "male": 460, "female": 40}}} 
 3  |{"name": "HIDN", "sector": "government", "personnel": {"headcount": {"total": 431, "male": 121, "female": 310}}} 
 4  |{"name": "RevDev", "sector": "government", "personnel": {"headcount": {"total": 15, "male": 10, "female": 5}}} 
 5  |{"name": "NEFPAN", "sector": "non-profit", "personnel": {"headcount": {"total": 5, "male": 4, "female": 1}}}
Run Code Online (Sandbox Code Playgroud)

我想得到 DISTINCT 部门的 COUNT 和 DISTINCT 部门的男性和女性总数的总和。我想要这样的结果:

Sector      |Count  |Total  |Male   |Female
------------|-------|-------|-------|--------
business    |2      |600    |510    |90
government  |2      |446    |131    |315
non-profit  |1      |5      |4      |1
Run Code Online (Sandbox Code Playgroud)

如果有人能指导我为它编写 SQL,将不胜感激。我正在使用 Postgres 9.6。谢谢

joa*_*olo 11

你可以只使用:

SELECT
    data->>'sector' AS "Sector", 
    count(data->>'sector') AS "Count", 
    sum((data->'personnel'->'headcount'->>'total')::integer)  AS "Total", 
    sum((data->'personnel'->'headcount'->>'male')::integer)   AS "Male", 
    sum((data->'personnel'->'headcount'->>'female')::integer) AS "Female"
FROM
    research
GROUP BY
    data->>'sector'
ORDER BY
    data->>'sector' ;
Run Code Online (Sandbox Code Playgroud)

您使用两个PostgreSQLJSONB运算符

jsonb -> field  => gets the field out of the json(b), returning a json(b) object
jsonb ->> field => gets the field out of the json(b), returning it as text
Run Code Online (Sandbox Code Playgroud)

你只会得到:

部门 | 计数 | 总计 | 男 | 女性
:--------- | ----: | ----: | ---: | -----:
业务 | 2 | 600 | 510 | 90
政府| 2 | 第446话 131 | 315
非盈利 | 1 | 5 | 4 | 1

但是,对于这种数据结构完美的场景,如果使用规范化 SQL会更有意义。这将是您的表的结构(没有冗余数据):

 CREATE TABLE normalized_research
 ( 
     id integer PRIMARY KEY,
     name text,
     sector text,
     male_headcount integer,
     female_headcount integer
 ) ;
Run Code Online (Sandbox Code Playgroud)

这是您填写的方式:

 INSERT INTO 
     normalized_research 
     (id, name, sector, male_headcount, female_headcount)
 SELECT
     id, 
     data->>'name',
     data->>'sector',
     (data->'personnel'->'headcount'->>'male')::integer,
     (data->'personnel'->'headcount'->>'female')::integer 
 FROM
     research ;
Run Code Online (Sandbox Code Playgroud)

这是您将要进行的(更好、更快、更安全)的查询:

SELECT
     sector AS "Sector", 
     count(sector) AS "Count", 
     sum(male_headcount)+sum(female_headcount) AS "Total", 
     sum(male_headcount) AS "Male", 
     sum(female_headcount) AS "Female"
 FROM
     normalized_research
 GROUP BY
     sector
 ORDER BY
     sector ;
Run Code Online (Sandbox Code Playgroud)

......这会给你完全相同的结果。

你可以在这个 DBFiddle找到所有的逻辑


旁注

JSON 主要是为了在 Web 服务和 Web 消费者(通常用 JavaScript 编写)之间传输和交换数据(通过 Ajax)发明的。

引用JSON.org

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。人类很容易阅读和写作。机器很容易解析和生成。它基于 JavaScript 编程语言标准 ECMA-262 第 3 版 - 1999 年 12 月的一个子集。

(强调我的)

它非常适合其最初的用途。但它并没有被设计为一种在数据库中存储数据的方式。不知何故,这是事后的想法,因为您可以非常轻松地做到这一点。您可以将 JSON 存储为文本,并且您已经可以使用任何数据库,即使它不知道 JSON。

在某些场合(通常链接到非常可变的数据结构,以及事先不知道的数据结构)它是一个不错的选择。但是,当您的数据结构完美时,您的架构(=结构)是众所周知的并且不需要很大的灵活性:普通的旧规范化 SQL 是更好的选择:您拥有数据类型安全性、一致性、更容易索引,参照完整性,更快的访问,...