2015-03-19 166 views
2

我在AdWords中存储AdWords报告数据。每个报告存储在一个名为Reports的表中,其中有一个名为'data'的jsonb列。每个报告都有存储在它的“数据”字段的JSON看起来,看起来像这样:如何计算Postgres中的json数据

[ 
    { 
    match_type: "exact", 
    search_query: "gm hubcaps", 
    conversions: 2, 
    cost: 1.24 
    }, 
    { 
    match_type: "broad", 
    search_query: "gm auto parts", 
    conversions: 34, 
    cost: 21.33 
    }, 
    { 
    match_type: "phrase", 
    search_query: "silverdo headlights", 
    conversions: 63, 
    cost: 244.05 
    } 
] 

我想要做的就是查询关闭这些数据散列和总结转换的总数为给定的报告。我查看了Postgresql文档,看起来你只能真正对散列进行计算,而不是像这样的散列数组。我正在尝试在postgres中做什么?我是否需要从这个数组中创建一个临时表并进行计算?或者我可以使用存储过程吗?

我使用PostgreSQL 9.4

编辑 我不只是使用普通的,标准化的表的原因是,这仅仅是一个的报告数据是如何被结构化的例子。在我的项目中,报告必须允许使用任意键,因为用户使用任何他们喜欢的列上传CSV。这基本上只是一种绕过任意多个用户创建表的方式。

+1

也许你应该把它作为一个永久表,因为这个结构完全符合关系模型并保持在JSON中意味着每个查询都将变得更加复杂和低效。 – 2015-03-19 15:05:50

+0

表格定义(psql中的'\ d tbl')和你的Postgres版本对于这个问题是必不可少的*。另外:总和惯例*每个报告*或总体? – 2015-03-19 15:10:13

回答

1

我想要做的就是查询关闭这些数据散列和总结转换

的最快方法应与jsonb_populate_recordset()。但是你需要一个注册行类型。

CREATE TEMP TABLE report_data (
-- match_type text -- commented out, because we only need .. 
-- , search_query text -- .. conversions for this query 
    conversions int 
-- , cost numeric 
); 

临时表是一种注册行类型ad-hoc的方法。在这个相关答案更多的解释:

假设表reportreport_id为PK缺乏inforamtion的。

SELECT r.report_id, sum(d.conversions) AS sum_conversions 
FROM report r 
LEFT JOIN LATERAL jsonb_populate_recordset(null::report_data, r.data) d ON true 
-- WHERE r.report_id = 12345 -- only for given report? 
GROUP BY 1; 

LEFT JOIN确保你得到一个结果,即使data为空或空或JSON数组为空。

对于从单列在基础表的总和,这是更快的:

SELECT d.sum_conversions 
FROM report r 
LEFT JOIN LATERAL (
    SELECT sum(conversions) AS sum_conversions 
    FROM jsonb_populate_recordset(null::report_data, r.data) 
    ) d ON true 
WHERE r.report_id = 12345; -- enter report_id here 

替代与jsonb_array_elements()(无需注册行类型):

SELECT d.sum_conversions 
FROM report r 
LEFT JOIN LATERAL (
    SELECT sum((value->>'conversions')::int) AS sum_conversions 
    FROM jsonb_array_elements(r.data) 
    ) d ON true 
WHERE r.report_id = 12345; -- enter report_id here 

通常你可以将它作为简单的,标准化的表来实现。这里我没有看到JSON的好处(除了你的应用程序似乎需要它,就像你添加的那样)。

+0

我听到你正常化。我采用这种方法的原因是因为该项目要求用户可以上传任何csv文档,而不管结构如何以及列中包含哪些列。我只是以此AdWords报告为例。 – 2015-03-19 15:42:01

+0

另外,谢谢你指出缺乏细节。我将编辑该问题。 – 2015-03-19 15:42:50

1

你可以使用unnest

select sum(conv) from 
(select d->'conversion' as conv from 
(select unnest(data) as d from <your table>) all_data 
) all_conv 

免责声明:我没有PG 9.2,所以我无法测试它自己。

编辑:这是假设您提到的数组是Postgresql数组,即您的data列的数据类型是character varying[]。如果你的意思是data是一个json数组,你应该可以使用json_array_elements而不是unnest

+0

'unnest()'只能应用于Postgres数组。不是'json'或'jsonb'类型中的JSON数组。 – 2015-03-19 15:20:35

+0

该数组实际上是jsonb类型。我会尝试json_array_elements。 – 2015-03-19 15:37:41

+1

@AdamKolkman:或'jsonb'的'jsonb_array_elements'。 – 2015-03-19 22:38:07