我已经大大简化了这些示例,希望能够提出一个可以回答的明确问题:
考虑一个事件表
CREATE TABLE alertable_events
(
unique_id text NOT NULL DEFAULT ''::text,
generated_on timestamp without time zone NOT NULL DEFAULT now(),
message_text text NOT NULL DEFAULT ''::text,
CONSTRAINT pk_alertable_events PRIMARY KEY (unique_id),
)
包含以下数据:
COPY alertable_events (unique_id,message_text,generated_on) FROM stdin;
one message one 2014-03-20 06:00:00.000000
two message two 2014-03-21 06:00:00.000000
three message three 2014-03-22 06:00:00.000000
four message four 2014-03-23 06:00:00.000000
five message five 2014-03-24 06:00:00.000000
\.
对于每个事件,都有一个字段列表
CREATE TABLE alertable_event_fields
(
unique_id text NOT NULL DEFAULT ''::text,
field_name text NOT NULL,
field_value text NOT NULL DEFAULT ''::text,
CONSTRAINT pk_alertable_event_fields PRIMARY KEY (unique_id, field_name),
CONSTRAINT fk_alertable_event_fields_0 FOREIGN KEY (unique_id)
REFERENCES alertable_events (unique_id) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE,
)
包含以下数据:
COPY alertable_event_fields (unique_id,field_name,field_value) FROM stdin;
one field1 a
one field2 b
two field1 z
two field2 y
three field1 a
three field2 m
four field1 a
four field2 b
five field1 z
five field2 y
\.
我想定义一个产生以下内容的视图:
| unique_id | fields | message_text | generated_on | updated_on | count |
| five | z|y | message five | 2014-03-21 06:00:00.000000 | 2014-03-24 06:00:00.000000 | 2 |
| four | a|b | message four | 2014-03-20 06:00:00.000000 | 2014-03-23 06:00:00.000000 | 2 |
| three | a|m | message three | 2014-03-22 06:00:00.000000 | 2014-03-22 06:00:00.000000 | 1 |
值得注意的是:
a|b
与b|a
不匹配我已经制作了这个视图,它适用于小型数据集,但是,随着alertable_events表的增长,它变得异常缓慢。我只能假设我在视图中做错了事,因为我从未处理过任何非常丑陋的事情。
更新3/30美国东部时间下午12:15 看起来我可能遇到服务器调优问题导致运行时间过长,请参阅已添加explain
以获取更多信息。如果你在那里看到一个明显的问题,我对调整服务器的配置非常感兴趣。
任何人都可以将一个能够很好地处理大型数据集并且运行时间明显好于此的视图拼凑在一起吗?也许使用hstore? (我最好运行9.2,如果我可以对这些字段进行良好的json编码,则为9.3。)
更新3/30 11:30 AM 我开始认为我的问题可能是服务器调整(这意味着我需要与SA交谈)这里'一个非常简单的explain (analyze,buffers)
,它显示了一个荒谬的运行时间,在unduplicated_event_fields中只有8k行
更新3/30 7:20 PM 我使用SET WORK_MEM='5MB'
将可用内存提高到5MB(这对于下面的查询来说很多),奇怪的是,即使计划程序进入内存快速排序,它实际上平均延长了100毫秒!
explain (analyze,buffers)
SELECT a.unique_id,
array_to_string(array_agg(a.field_value order by a.field_name),'|') AS "values"
FROM alertable_event_fields a
GROUP BY a.unique_id;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------
GroupAggregate (cost=771.11..892.79 rows=4056 width=80) (actual time=588.679..630.989 rows=4056 loops=1)
Buffers: shared hit=143, temp read=90 written=90
-> Sort (cost=771.11..791.39 rows=8112 width=80) (actual time=588.591..592.622 rows=8112 loops=1)
Sort Key: unique_id
Sort Method: external merge Disk: 712kB
Buffers: shared hit=143, temp read=90 written=90
-> Seq Scan on alertable_event_fields a (cost=0.00..244.40 rows=8112 width=80) (actual time=0.018..5.478 rows=8112 loops=1)
Filter: (message_name = 'LIMIT_STATUS'::text)
Buffers: shared hit=143
Total runtime: 632.323 ms
(10 rows)
更新3/30美国东部时间上午4:10 我仍然不完全满意,并且对任何进一步优化感兴趣。我需要支持500毫秒/秒的稳定状态,虽然大多数情况不应该是"事件",但是当我进行压力测试时,我会得到一点积压。
更新3/30 12:00 PM EDT 这是我最可读的迭代,不幸的是,对于4000行我仍然看着600ms的运行时间! ...(见上文,因为它主要包含在最内部的查询中)这里的任何帮助都将非常感激
CREATE OR REPLACE VIEW views.unduplicated_events AS
SELECT a.unique_id,a.message_text,
b."values",b.generated_on,b.updated_on,b.count
FROM alertable_events a
JOIN (
SELECT b."values",
min(a.generated_on) AS generated_on,
max(a.generated_on) AS updated_on,
count(*) AS count
FROM alertable_events a
JOIN (
SELECT a.unique_id,
array_to_string(array_agg(a.field_value order by a.field_name),'|') AS "values"
FROM alertable_event_fields a
GROUP BY a.unique_id
) b USING (unique_id)
GROUP BY b."values"
) b ON a.generated_on=b.updated_on
ORDER BY updated_on DESC;
更新3/30 12:00 PM EDT 删除了旧东西,因为这太长了
答案 0 :(得分:1)
一些指针
您当前的查询不正确,除非generated_on
是唯一的,而问题中未声明,可能不是这样:
CREATE OR REPLACE VIEW views.unduplicated_events AS
SELECT ...
FROM alertable_events a
JOIN ( ... ) b ON a.generated_on=b.updated_on -- !! unreliable
SELECT DISTINCT ON (f.fields)
unique_id -- most recent
, f.fields
, e.message_text -- most recent
, min(e.generated_on) OVER (PARTITION BY f.fields) AS generated_on -- "first"
, e.generated_on AS updated_on -- most recent
, count(*) OVER (PARTITION BY f.fields) AS ct
FROM alertable_events e
JOIN (
SELECT unique_id, array_to_string(array_agg(field_value), '|') AS fields
FROM (
SELECT unique_id, field_value
FROM alertable_event_fields
ORDER BY 1, field_name -- a bit of a hack, but much faster
) f
GROUP BY 1
) f USING (unique_id)
ORDER BY f.fields, e.generated_on DESC;
结果目前按fields
排序。如果您需要不同的排序顺序,则需要将其包装在另一个子查询中...
更新:输出列名generated_on
与输入列generated_on
冲突。您必须对列e.generated_on
进行表限定才能引用输入列。我在任何地方都添加了表限定以明确它,但实际上只需要ORDER BY
子句。 Per documentation:
如果
ORDER BY
表达式是一个与输出匹配的简单名称 列名和输入列名称ORDER BY
将其解释为 输出列名称。这与GROUP的选择相反 BY将在同样的情况下。这种不一致就是这样 与SQL标准兼容。
我一直在回答几个问题。现在它也得到了我,抱歉
更新的查询也应该更快(一直如此)。再次运行EXPLAIN ANALYZE
。
对于整个查询,索引几乎无法使用。仅当您选择特定行时...一个可能的例外:alertable_event_fields
的覆盖索引:
CREATE INDEX f_idx1
ON alertable_event_fields (unique_id, field_name, field_value);
array_agg(field_value ORDER BY ...)
对于大集合而言往往比在子查询中预排序慢。
DISTINCT ON
在这里很方便,但不确定它是否真的更快,因为ct
和generated_on
必须在单独的窗口函数中计算,这需要另一个排序步骤。
work_mem
:将设置为高可能会严重损害性能。 More in the Postgres Wiki.或"Craig's list"。
通常,难以优化。索引失败,因为排序顺序取决于两个表。如果您可以使用快照,请考虑MATERIALIZED VIEW
。