PostgreSQL中如何使用数组
PostgreSQL中如何使用数组,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
这种情况几星期前在Heap出现了。我们在Heap为每个跟踪用户维护一个事件数组,在这个数组中我们用一个hstore datum代表每个事件。我们有一个导入管道来追加新事件到对应的数组。为了使这一导入管道是幂等的,我们给每个事件设定一个event_id,我们通过一个功能函数重复运行我们的事件数组。如果我们要更新附加到事件的属性的话,我们只需使用相同的event_id转储一个新的事件到管道中。
所以,我们需要一个功能函数来处理hstores数组,并且,如果两个事件具有相同的event_id时应该使用数组中最近出现的那个。刚开始尝试这个函数是这样写的:
--Thisisslow,andyoudon'twanttouseit!----Filteranarrayofeventssuchthatthereisonlyoneeventwitheachevent_id.--Whenmorethanoneeventwiththesameevent_idispresent,takethelatestone.CREATEORREPLACEFUNCTIONdedupe_events_1(eventsHSTORE[])RETURNSHSTORE[]AS$$SELECTarray_agg(event)FROM(--Filterforrank=1,i.e.selectthelatesteventforanycollisionsonevent_id.SELECTeventFROM(--Rankelementswiththesameevent_idbypositioninthearray,descending.
这个查询在拥有2.4GHz的i7CPU及16GB Ram的macbook pro上测得,运行脚本为:https://gist.github.com/drob/9180760。
在这边究竟发生了什么呢? 关键在于PostgreSQL存贮了一个系列的hstores作为数组的值, 而不是指向值的指针. 一个包含了三个hstores的数组看起来像
{“event_id=>1,data=>foo”,“event_id=>2,data=>bar”,“event_id=>3,data=>baz”}
相反的是
{[pointer],[pointer],[pointer]}
对于那些长度不一的变量, 举个例子. hstores, json blobs, varchars,或者是 text fields, PostgreSQL 必须去找到每一个变量的长度. 对于evaluateevents[2], PostgreSQL 解析从左侧读取的事件直到读取到第二次读取的数据. 然后就是 forevents[3], 她再一次的从第一个索引处开始扫描,直到读到第三次的数据! 所以, evaluatingevents[sub]是 O(sub), 并且 evaluatingevents[sub]对于在数组中的每一个索引都是 O(N2), N是数组的长度.
PostgreSQL能得到更加恰当的解析结果, 它可以在这样的情况下分析该数组一次. 真正的答案是可变长度的元素与指针来实现,以数组的值, 以至于,我们总能够处理 evaluateevents[i]在不变的时间内.
即便如此,我们也不应该让PostgreSQL来处理,因为这不是一个地道的查询。除了generate_subscripts我们可以用unnest,它解析数组并返回一组条目。这样一来,我们就不需要在数组中显式加入索引了。
--Filteranarrayofeventssuchthatthereisonlyoneeventwitheachevent_id.--Whenmorethanoneeventwiththesameevent_id,ispresent,takethelatestone.CREATEORREPLACEFUNCTIONdedupe_events_2(eventsHSTORE[])RETURNSHSTORE[]AS$$SELECTarray_agg(event)FROM(--Filterforrank=1,i.e.selectthelatesteventforanycollisionsonevent_id.SELECTeventFROM(--Rankelementswiththesameevent_idbypositioninthearray,descending.SELECTevent,row_numberASindex,rank()OVER(PARTITIONBY(event->'event_id')::BIGINTORDERBYrow_numberDESC)FROM(--Useunnestinsteadofgenerate_subscriptstoturnanarrayintoaset.SELECTevent,row_number()OVER(ORDERBYevent->'time')FROMunnest(events)ASevent)unnested_data)deduped_eventsWHERErank=1ORDERBYindexASC)to_agg;$$LANGUAGESQLIMMUTABLE;
结果是有效的,它花费的时间跟输入数组的大小呈线性关系。对于100K个元素的输入它需要大约半秒,而之前的实现需要40秒。
这实现了我们的需求:
一次解析数组,不需要unnest。
按event_id划分。
对每个event_id采用最新出现的。
按输入索引排序。
教训:如果你需要访问PostgreSQL数组的特定位置,考虑使用unnest代替。
SELECTevents[sub]ASevent,sub,rank()OVER(PARTITIONBY(events[sub]->'event_id')::BIGINTORDERBYsubDESC)FROMgenerate_subscripts(events,1)ASsub)deduped_eventsWHERErank=1ORDERBYsubASC)to_agg;$$LANGUAGESQLIMMUTABLE;
这样奏效,但大输入是性能下降了。这是二次的,在输入数组有100K各元素时它需要大约40秒!
这个查询在拥有2.4GHz的i7CPU及16GB Ram的macbook pro上测得,运行脚本为:https://gist.github.com/drob/9180760。
在这边究竟发生了什么呢? 关键在于PostgreSQL存贮了一个系列的hstores作为数组的值, 而不是指向值的指针. 一个包含了三个hstores的数组看起来像
{“event_id=>1,data=>foo”,“event_id=>2,data=>bar”,“event_id=>3,data=>baz”}
相反的是
{[pointer],[pointer],[pointer]}
对于那些长度不一的变量, 举个例子. hstores, json blobs, varchars,或者是 text fields, PostgreSQL 必须去找到每一个变量的长度. 对于evaluateevents[2], PostgreSQL 解析从左侧读取的事件直到读取到第二次读取的数据. 然后就是 forevents[3], 她再一次的从第一个索引处开始扫描,直到读到第三次的数据! 所以, evaluatingevents[sub]是 O(sub), 并且 evaluatingevents[sub]对于在数组中的每一个索引都是 O(N2), N是数组的长度.
PostgreSQL能得到更加恰当的解析结果, 它可以在这样的情况下分析该数组一次. 真正的答案是可变长度的元素与指针来实现,以数组的值, 以至于,我们总能够处理 evaluateevents[i]在不变的时间内.
即便如此,我们也不应该让PostgreSQL来处理,因为这不是一个地道的查询。除了generate_subscripts我们可以用unnest,它解析数组并返回一组条目。这样一来,我们就不需要在数组中显式加入索引了。
--Filteranarrayofeventssuchthatthereisonlyoneeventwitheachevent_id.--Whenmorethanoneeventwiththesameevent_id,ispresent,takethelatestone.CREATEORREPLACEFUNCTIONdedupe_events_2(eventsHSTORE[])RETURNSHSTORE[]AS$$SELECTarray_agg(event)FROM(--Filterforrank=1,i.e.selectthelatesteventforanycollisionsonevent_id.SELECTeventFROM(--Rankelementswiththesameevent_idbypositioninthearray,descending.SELECTevent,row_numberASindex,rank()OVER(PARTITIONBY(event->'event_id')::BIGINTORDERBYrow_numberDESC)FROM(--Useunnestinsteadofgenerate_subscriptstoturnanarrayintoaset.SELECTevent,row_number()OVER(ORDERBYevent->'time')FROMunnest(events)ASevent)unnested_data)deduped_eventsWHERErank=1ORDERBYindexASC)to_agg;$$LANGUAGESQLIMMUTABLE;
结果是有效的,它花费的时间跟输入数组的大小呈线性关系。对于100K个元素的输入它需要大约半秒,而之前的实现需要40秒。
这实现了我们的需求:
一次解析数组,不需要unnest。
按event_id划分。
对每个event_id采用最新出现的。
按输入索引排序。
关于PostgreSQL中如何使用数组问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。