1

我有一个带有 JSONB 列的表,其中包含如下数据:

create table car_stats (
    id int primary key,
    city varchar,
    date timestamp,
    info varchar
    stats jsonb
)

统计示例:

[
  {
    "brand": "AUDI",
    "status": "NEW"
  },
  {
    "brand": "BMW",
    "status": "PRODUCTION"
  },
  {
    "brand": "BMW",
    "status": "NEW"
  },
  {
    "brand": "BMW",
    "status": "NEW"
  },
  {
    "brand": "BMW",
    "status": "DELIVERED"
  }
]

我想计算按城市和月份分组的汽车品牌的/生产/交付百分比

 CITY   MONTH BRAND NEW  PRODUCTION DELIVERED
 LONDON  3    AUDI  100%   0          0
 PARIS   2    BMW   50%    25%        25%

我尝试了以下方法,但我不知道如何计算 JSON 中的元素(例如,所有 BMW 都处于 NEW 状态)

with cte as (
   select stats ->> 'brand',
          stats ->> 'status',
          city, 
          date
   from car_stats
   group by city
), 
 grouped as (
   select cte.brand,
          cte.country,
          cte.date,
          ARRAY_TO_JSON(ARRAY_AGG(base)) as statistics
   from cte
   group by cte.brand, cte.city, cte.date       
 ),
 stats as (
    count % statistics somehow here.. ? 
 )

)
4

2 回答 2

0

您可以将每个元素stats与其对应的城市相关联,然后使用sumwith group by

with recursive cte(id, c, p, i, d) as (
   select c.id, c.city, (c.stats ->> 0)::jsonb, 1, c.stats from car_stats c
   union all
   select c.id, c.c, (c.d ->> c.i)::jsonb, c.i+1, c.d from cte c where c.i < jsonb_array_length(c.d)
)
select c.c, extract(month from c1.date), c.p -> 'brand', c.p -> 'factory'
   round(sum(case when (c.p -> 'status')::text = '"NEW"' then 1 else 0 end)/count(*)::decimal,2), 
   round(sum(case when (c.p -> 'status')::text = '"PRODUCTION"' then 1 else 0 end)/count(*)::decimal,2), 
   round(sum(case when (c.p -> 'status')::text = '"DELIVERED"' then 1 else 0 end)/count(*)::decimal,2)
from cte c join car_stats c1 on c.id = c1.id 
group by c.c, extract(month from c1.date), c.p -> 'brand', c -> 'factory'

见演示

于 2022-01-02T01:30:57.827 回答
0

首先使用条件聚合展开brandstatus分成单独的行cross join lateral,然后使用count filter条件聚合。

with t as 
(
 select city, date_trunc('month', "date")::date y_month, brand, status
 from car_stats 
 cross join lateral
 (
  select j ->> 'brand' brand,
         j ->> 'status' status
  from jsonb_array_elements(stats) j
 ) t
)
select city, y_month, brand,
  count(*) filter (where status = 'NEW')::numeric/count(*)*100 "NEW",
  count(*) filter (where status = 'PRODUCTION')::numeric/count(*)*100 "PRODUCTION",
  count(*) filter (where status = 'DELIVERED')::numeric/count(*)*100 "DELIVERED"
from t
group by city, y_month, brand;
于 2022-01-02T10:48:10.040 回答