Postgresql 10 jsonb到多行的表

时间:2017-11-07 11:25:23

标签: json postgresql

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)

http://sqlfiddle.com/#!17/cd4df/9/0

1 个答案:

答案 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)

编辑:我添加了交叉连接横向