栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

每天查询计数,并有多个星期的日期限制

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

每天查询计数,并有多个星期的日期限制

这是一个很长的答案,如何使您的查询简短。:)

table

在我的表上构建(在为表定义提供不同的( 奇数! )数据类型之前:

CREATE TABLE requests (   idint , accounts_id  int  -- (id of the user) , recipient_id int  -- (id of the recipient) , date         date -- (date that the request was made in YYYY-MM-DD) , amount       int  -- (# of requests by accounts_id for the day));

特定日期的活跃用户

某一天 的“活跃用户”列表:

SELECt accounts_idFROM  (   SELECt w.w, r.accounts_id   FROM  (      SELECt w, day - 6 - 7 * w AS w_start, day     - 7 * w AS w_end         FROM  (SELECt '2014-10-31'::date - 1 AS day) d  -- effective date here, generate_series(0,3) w      ) w   JOIN   requests r ON r."date" BETWEEN w_start AND w_end   GROUP  BY w.w, r.accounts_id   HAVINg sum(r.amount) > 10   ) subGROUP  BY 1HAVINg count(*) = 4;

步骤1

在最里面的 子查询

w
(“周”)中
CROSSJOIN
,用给定天1的a来构建4个感兴趣的周的边界,其输出为
generate_series(0-3)

要向/从

date
(而不是从时间戳!)中添加/减去天
integer
数,只需将数字相加/减去即可。该表达式
day - 7 *w
从给定日期减去7天的0-3倍,得出每个星期()的 结束 日期
w_end

分别减去另外6天(而不是7天)来计算相应的 开始时间
w_start
)。
此外,请保留星期数
w
(0-3),以进行以后的汇总。

第2步

子查询中,

sub
联接行从
requests
到4周的集合,其中日期位于开始日期和结束日期之间。
GROUPBY
周号
w
accounts_id

只有总请求数超过10个的星期才有资格。

第三步

外部

SELECT
计数中,每个用户(
accounts_id
)合格的周数。必须为4才能成为“活动用户”

每天活跃用户数

这是 炸药
封装在一个简单的SQL函数中以简化常规用法,但是查询也可以单独使用:

CREATE FUNCTION f_active_users (_now date = now()::date, _days int = 3)  RETURNS TABLE (day date, users int) AS$func$WITH r AS (   SELECT accounts_id, date, sum(amount)::int AS amount   FROM   requests   WHERe  date BETWEEN _now - (27 + _days) AND _now - 1   GROUP  BY accounts_id, date   )SELECt date + 1, count(w_ct = 4 OR NULL)::intFROM  (   SELECt accounts_id, date        , count(w_amount > 10 OR NULL)   OVER (PARTITION BY accounts_id, dow ORDER BY date DESC   ROWS BETWEEN CURRENT ROW AND 3 FOLLOWING) AS w_ct   FROM  (      SELECt accounts_id, date, dow   , sum(amount) OVER (PARTITION BY accounts_id ORDER BY date DESC   ROWS BETWEEN CURRENT ROW AND 6 FOLLOWING) AS w_amount      FROM  (SELECt _now - i AS date, i%7 AS dow  FROM   generate_series(1, 27 + _days) i) d -- period of interest      CROSS  JOIN (  SELECt accounts_id FROM r  GROUP  BY 1  HAVINg count(*) > 3 AND sum(amount) > 39  -- enough rows & requests  AND    max(date) > min(date) + 15) a      -- can cover 4 weeks      LEFT   JOIN r USING (accounts_id, date)      ) sub1   WHERe date > _now - (22 + _days)  -- cut off 6 trailing days now - useful?   ) sub2GROUP  BY dateORDER  BY date DESCLIMIT  _days$func$ LANGUAGE sql STABLE;

该函数需要任何天(

_now
),默认为“ today”,
_days
结果中的天数(),默认为3。称呼:

SELECt * FROM f_active_users('2014-10-31', 5);

或不带参数以使用默认值:

SELECt * FROM f_active_users();

该方法 不同于第一个查询

SQL Fiddle 具有查询和表定义的变体。

步骤0

为了获得更好的效果,在CTE中,仅针对感兴趣的期间

r
预先汇总了每笔金额
(accounts_id, date)
。该表仅扫描 一次,建议的索引(请参见打击)将在此处插入。

步骤1

在内部子查询中,

d
生成必要的天数列表:
27 + _days
行,其中,
_days
是输出中所需的行数,有效地是28天或更长时间。
进行此操作时,请计算
dow
要在步骤3中进行汇总的星期几()
i%7
与每周间隔一致,但是查询适用于 任何 间隔。

在内部子查询中,

a
生成一个唯一的用户列表(
accounts_id
),这些用户存在于CTE中,
r
并通过了一些初步的表面测试(足够多的行跨越了足够的时间,并且有足够的总请求数)。

第2步

生成一个笛卡尔积

d
,并
a
CROSS JOIN
一排为每个用户相关的每一个相关的一天
LEFTJOIN
r
追加请求的量(如果有的话)。没有
WHERe
条件,我们希望每天都在结果,即使有根本没有活跃用户。

w_amount
使用带有
自定义框架
的Window函数,在同一步骤中计算上周()的总金额。

第三步

现在关闭最近6天;这是 可选的 ,可能会也可能不会帮助您提高性能。测试一下:

WHERe date >= _now - (21 + _days)

w_ct
在类似的窗口函数中计算满足最低金额的星期(),此时间除以
dow
另外的间隔,以使框架中的过去4周仅具有相同的工作日(其中包含各自过去一周的总和)。该表达式
count(w_amount10 OR NULL)
仅计算具有10个以上请求的行。

第4步

在外部

SELECT
群组中,按
date
并计算通过了全部4周(
count(w_ct = 4 ORNULL)
)的用户。在日期上加1以补偿不等于1的日期,
ORDER
LIMIT
加到请求的天数中。

表现与展望

这两个查询的理想索引是:

CREATE INDEX foo ON requests (date, accounts_id, amount);

性能应该不错,但是由于新的 移动聚合* 支持,即将推出的Postgres 9.4 甚至会更好(甚至更多): *

*Postgres Wiki中的 *移动聚合支持

在9.4手册中移动聚集体

另外:请勿将

timestamp
列称为“日期”,而是
timestamp
,而不是
date
。更好的是,永远不要使用诸如
date
timestamp
作为标识符的基本类型名称。曾经。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/400263.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号