Postgresql 10 jsonb到多行的表
我有一个json数组,其中包含一些"用户"结构体。这些json数组在Postgresql数据库中是stroe,我想得到所有行(对于所有数组的所有行)。
数据样本:
我的数据库实例:
docker run --name postgresql-10 -e POSTGRES_PASSWORD=mysecretpassword -d postgres:10-alpine
docker run -it --rm --link postgresql-10:postgres postgres:10-alpine psql -h postgres -U postgres
我的表:
CREATE TABLE "Reports" (
"name" TEXT NOT NULL,
"report" JSONB NOT NULL,
"timestamp" TIMESTAMP NOT NULL,
PRIMARY KEY ("name", "timestamp")
)
;
一些数据:
insert into "Reports" (timestamp, "name", "report")
values ('2017-11-05'::timestamp,
'appA',
'{
"dateComputed": "2017-11-06 10:06:29 UTC",
"name": "appA",
"users": [
{
"DATE": "2017-11-03",
"EMPLID": "415",
"NAME": "Smith"
},
{
"DATE": "2017-11-03",
"EMPLID": "4",
"NAME": "Jane"
},
{
"DATE": "2017-11-03",
"EMPLID": "31",
"NAME": "Doe"
}
]
}'::jsonb
) ;
insert into "Reports" (timestamp, "name", "report")
values ('2017-11-04'::timestamp,
'appA',
'{
"dateComputed": "2017-11-04 11:34:13 UTC",
"name": "appA",
"users": [
{
"DATE": "2017-11-03",
"EMPLID": "4",
"NAME": "Jane"
},
{
"DATE": "2017-11-03",
"EMPLID": "31",
"NAME": "Doe"
}
]
}'::jsonb
) ;
insert into "Reports" (timestamp, "name", "report")
values ('2017-11-01'::timestamp,
'appA',
'{
"dateComputed": "2017-11-01 02:32:49 UTC",
"name": "appA",
"users": [
{
"DATE": "2017-11-01",
"EMPLID": "415",
"NAME": "Smith"
},
{
"DATE": "2017-11-01",
"EMPLID": "31",
"NAME": "Doe"
}
]
}'::jsonb
) ;
insert into "Reports" (timestamp, "name", "report")
values ('2017-11-03'::timestamp, 'appB', '[{"other": "useless"}]'::jsonb) ;
我想要的是列出所有匹配"报告"谁的名字"是AppA' :
+------------+-------+--------+
| DATE | NAME | EMPLID |
+------------+-------+--------+
| 2017-11-03 | Smith | 415 |
+------------+-------+--------+
| 2017-11-03 | Jane | 4 |
+------------+-------+--------+
| 2017-11-03 | Doe | 31 |
+------------+-------+--------+
| 2017-11-03 | Jane | 4 |
+------------+-------+--------+
| 2017-11-03 | Doe | 31 |
+------------+-------+--------+
| 2017-11-01 | Smith | 415 |
+------------+-------+--------+
| 2017-11-01 | Doe | 31 |
+------------+-------+--------+
+------------+------------+-------+--------+
| timestamp | DATE | NAME | EMPLID |
+------------+------------+-------+--------+
| 2017-11-05 | 2017-11-03 | Smith | 415 |
+------------+------------+-------+--------+
| 2017-11-05 | 2017-11-03 | Jane | 4 |
+------------+------------+-------+--------+
| 2017-11-05 | 2017-11-03 | Doe | 31 |
+------------+------------+-------+--------+
| 2017-11-04 | 2017-11-03 | Jane | 4 |
+------------+------------+-------+--------+
| 2017-11-04 | 2017-11-03 | Doe | 31 |
+------------+------------+-------+--------+
| 2017-11-03 | 2017-11-01 | Smith | 415 |
+------------+------------+-------+--------+
| 2017-11-03 | 2017-11-01 | Doe | 31 |
+------------+------------+-------+--------+
当我只匹配一行时,我可以使用jsonb_to_recordset来获取与该行匹配的所有json行。 例如,通过创建视图来过滤最新的时间戳列:
CREATE INDEX "ReportsGIN" on "Reports" USING gin ("report") ;
CREATE VIEW "Reports_Latest_timestamp"
AS
SELECT "name"
, max("Reports"."timestamp") AS "timestamp_latest"
FROM "Reports"
GROUP BY "name"
;
CREATE VIEW "Reports_Latest"
AS
SELECT "Reports"."name"
, "Reports"."report"
, "Reports"."timestamp"
FROM "Reports"
WHERE ("Reports"."timestamp" = (SELECT "Reports_Latest_timestamp"."timestamp_latest" FROM "Reports_Latest_timestamp" WHERE "Reports_Latest_timestamp"."name" = "Reports"."name"))
;
select *
from
jsonb_to_recordset
(
(select report#>'{users}'
from "Reports_Latest"
where "name" = 'appA'
)
) as x(
"EMPLID" integer
, "NAME" text
, "DATE" timestamp with time zone
)
;
EMPLID | NAME | DATE
--------+-------+------------------------
415 | Smith | 2017-11-03 00:00:00+00
4 | Jane | 2017-11-03 00:00:00+00
31 | Doe | 2017-11-03 00:00:00+00
(3 rows)
jsonb_to_recordset按预期工作。
如何使用jsonb_to_recordset列出所有"报告"行吗
显示答案"时间戳" on" Reports_Latest"将是(但仍然没有完全"报告"线)的线索:
select t."timestamp"
, r."EMPLID"
, r."NAME"
, r."DATE"
from
(
select "timestamp", report#>'{users}'
from "Reports_Latest"
where "name" = 'appA'
) as t
, (
select *
from
jsonb_to_recordset
(
(select report#>'{users}'
from "Reports_Latest"
where "name" = 'appA'
)
) as x(
"EMPLID" integer
, "NAME" text
, "DATE" timestamp with time zone
)
) as r
;
timestamp | EMPLID | NAME | DATE
---------------------+--------+-------+------------------------
2017-11-05 00:00:00 | 415 | Smith | 2017-11-03 00:00:00+00
2017-11-05 00:00:00 | 4 | Jane | 2017-11-03 00:00:00+00
2017-11-05 00:00:00 | 31 | Doe | 2017-11-03 00:00:00+00
(3 rows)
SQL Fiddle on Postgresql 9.6 to quick test
Breathe提供的解决方案是:
select r."timestamp", x.*
from "Reports" as r
cross join lateral jsonb_to_recordset (r.report#>'{users}')
as x(
"EMPLID" integer
, "NAME" text
, "DATE" timestamp with time zone
)
where r."name" = 'appA'
;
timestamp | EMPLID | NAME | DATE
---------------------+--------+-------+------------------------
2017-11-05 00:00:00 | 415 | Smith | 2017-11-03 00:00:00+00
2017-11-05 00:00:00 | 4 | Jane | 2017-11-03 00:00:00+00
2017-11-05 00:00:00 | 31 | Doe | 2017-11-03 00:00:00+00
2017-11-04 00:00:00 | 4 | Jane | 2017-11-03 00:00:00+00
2017-11-04 00:00:00 | 31 | Doe | 2017-11-03 00:00:00+00
2017-11-01 00:00:00 | 415 | Smith | 2017-11-01 00:00:00+00
2017-11-01 00:00:00 | 31 | Doe | 2017-11-01 00:00:00+00
(7 rows)
答案 0 :(得分:0)
基本上,您要做的是生成与“Reports”的单行中的用户一样多的记录。在您想要的表结构中,前两列是“Reports”中的前两列。所以你的查询需要:
select a.timestamp, a."name"
FROM "Reports" a
然后,您要为每个记录创建记录的“子集”。通过将生成子集的函数应用于litterally所有行,可以实现这一点。子集由函数jsonb_to_recordset()生成,因此:
SELECT a.timestamp, a."name", b. *
FROM "Reports" a
CROSS JOIN lateral jsonb_to_recordset(a.report->'Users')
as b("EMPLID" integer
, "NAME" text
, "DATE" timestamp with time zone)
编辑:我添加了交叉连接横向