通过一个查询获得优化,获取日,月,年,终生总记录

时间:2011-05-25 16:28:45

标签: sql postgresql sqlperformance sqldatetime sql-date-functions

我有一个运行7.4的Postgres数据库(是的,我们正在升级)

我有四个单独的查询来获取每日,每月,每年和终身记录计数

SELECT COUNT(field)
FROM database
WHERE date_field
    BETWEEN DATE_TRUNC('DAY' LOCALTIMESTAMP) 
    AND DATE_TRUNC('DAY' LOCALTIMESTAMP) + INTERVAL '1 DAY'

对于月份,只需在查询中将DAY替换为MONTH,依此类推每个时间段。

寻找有关如何通过一个查询和任何优化获得所有预期结果的想法。

提前致谢!

注意:date_field是没有时区的时间戳

更新:

很抱歉,我会过滤掉带有其他查询限制的记录,只是想给出date_field比较的要点。对不起任何混淆

3 个答案:

答案 0 :(得分:1)

我对使用预准备语句和简单统计(record_count_t)表有一些想法:

-- DROP TABLE IF EXISTS record_count_t;
-- DEALLOCATE record_count;
-- DROP FUNCTION updateRecordCounts();

CREATE TABLE record_count_t (type char, count bigint);
INSERT INTO record_count_t (type) VALUES ('d'), ('m'), ('y'), ('l');

PREPARE record_count (text) AS
UPDATE record_count_t SET count =
(SELECT COUNT(field)
FROM database
WHERE
CASE WHEN $1 <> 'l' THEN
    DATE_TRUNC($1, date_field) = DATE_TRUNC($1, LOCALTIMESTAMP)
ELSE TRUE END)
WHERE type = $1;

CREATE FUNCTION updateRecordCounts() RETURNS void AS
$$
    EXECUTE record_count('d');
    EXECUTE record_count('m');
    EXECUTE record_count('y');
    EXECUTE record_count('l');
$$
LANGUAGE SQL;

SELECT updateRecordCounts();
SELECT type,count FROM record_count_t;

只要您需要更新统计信息,请使用updateRecordCounts()函数。

答案 1 :(得分:0)

我猜这是不可能进一步优化它。

如果您正在收集每日/每月/每年的统计数据,正如我假设您正在做的那样,一个选项(当然,在升级之后)是with statement和相关联接,例如:

with daily_stats as (
(what you posted)
),
monthly_stats as (
(what you posted monthly)
),
etc.
select daily_stats.stats,
       monthly_stats.stats,
       etc.
stats
left join yearly_stats on ...
left join monthly_stats on ...
left join daily_stats on ...

但是,这实际上比在生产环境中单独运行每个查询的效果要差,因为您将在数据库中引入左连接,这可以在中间件中完成(即每天显示,然后每月显示,然后年度和终身统计数据)。 (如果不是更好,因为你将避免全表扫描。)

通过保持好像,您将节省宝贵的数据库资源来处理对实际数据的读写操作。权衡(数据库和应用程序之间的网络流量减少)几乎肯定不值得。

答案 2 :(得分:-1)

糟糕!不要这样做!不是因为你不能做你所要求的,而是因为你可能不应该以这种方式做你所要求的。我猜你在你的例子中得到date_field的原因是因为你有一个date_field附加到用户或其他一些元数据。

想想看:你要求PostgreSQL扫描100%与给定用户相关的记录。除非这是一次性操作,否则你几乎肯定不想这样做。如果这是一次性操作,并且您计划将此值缓存为元数据,那么谁在关心优化?空间很便宜,可以节省大量的执行时间。

您应该添加4x每用户(或其他任何)元数据字段,以帮助总结数据。你有两个选择,我会让你弄清楚如何使用它来保持历史记录,但这是简单的版本:

CREATE TABLE user_counts_only_keep_current (
  user_id , -- Your user_id
  lifetime INT DEFAULT 0,
  yearly INT DEFAULT 0,
  monthly INT DEFAULT 0,
  daily INT DEFAULT 0,
  last_update_utc TIMESTAMP WITH  TIME ZONE,
  FOREIGN KEY(user_id) REFERENCES "user"(id)
);
CREATE UNIQUE INDEX this_tbl_user_id_udx ON user_counts_only_keep_current(user_id);

根据last_update_utc设置一些存储过程,如果NOW()与当前日期不匹配,则会将单个列清零。你可以从这里获得创意,但增加这样的记录将是最佳选择。

处理任何关系数据库中的时间序列数据需要特殊处理和维护。如果你想要良好的时态数据管理,请查看PostgreSQL的表继承....但实际上,不要做你要对你的应用程序做什么,因为它几乎肯定会导致坏事(tm)。