如何在PostgreSQL中创建循环通过另一个函数的函数?
我使用的是PostgreSQL 9.3.9,我有一个名为list_all_upsells的过程,该过程在一个月的开始和结束时进行。(有关示例数据,请参见sqlfiddle.com/#!15/abd02)例如,下面的代码将列出10月份追加销售的帐户数:如何在PostgreSQL中创建循环通过另一个函数的函数?,postgresql,Postgresql,我使用的是PostgreSQL 9.3.9,我有一个名为list_all_upsells的过程,该过程在一个月的开始和结束时进行。(有关示例数据,请参见sqlfiddle.com/#!15/abd02)例如,下面的代码将列出10月份追加销售的帐户数: select COUNT(up.*) as "Total Upsell Accounts in October" from list_all_upsells('2015-10-01 00:00:00'::timestamp, '2015-10-3
select COUNT(up.*) as "Total Upsell Accounts in October" from
list_all_upsells('2015-10-01 00:00:00'::timestamp, '2015-10-31 23:59:59'::timestamp) as up
where up.user_id not in
(select distinct user_id from paid_users_no_more
where concat(extract(month from payment_stop_date),'-',extract(year from payment_stop_date))<>
concat(extract(month from payment_start_date),'-',extract(year from payment_start_date)));
我有一个查询,以获取我们开展业务的月份中每个月的第一个月和最后一个月:
select distinct date_trunc('month', payment_start_date)::date as startmonth
from paid_users ORDER BY startmonth;
本月最后一个月:
SELECT distinct (date_trunc('MONTH', payment_start_date) +
INTERVAL '1 MONTH - 1 day')::date as endmonth from paid_users
ORDER BY endmonth;
现在,我如何创建一个函数来循环查看列表\u all\u upsells
,并获取每个月的计数?也就是说,startmonth
的第一次查询给出了2014-03-012014-04-01,…到2015-10-01,而endmonth
的第二次查询给出了2014-03-312014-04-30,…到2015-10-31。我想在每个月都运行列表\u all\u seals
,这样我就可以得到每个月我们有多少追加销售的账户的总数
我的付费用户
表如下所示:
DECLARE
payor_email_2 text;
BEGIN
FOR payor_email_2 in select distinct payor_email from paid_users LOOP
return query
execute
'select paid_users.* from paid_users,
(
select payment_start_date as first_time from paid_users
where payor_email = $3
order by payment_start_date limit 1
) as dummy
where payor_email = $3
and payment_start_date > first_time
and payment_start_date between $1 and $2
and first_time < $1'
using a, b, payor_email_2;
END LOOP;
return;
END
Month | Total Upselled Accounts
---------------------------------
08/2014 | 23
09/2014 | 35
ETC...
10/2015 | 56
CREATE TABLE paid_users
(
user_id integer,
user_email character varying(255),
payor_id integer,
payor_email character varying(255),
payment_start_date timestamp without time zone DEFAULT now()
)
付费用户\u无更多
:
CREATE TABLE paid_users_no_more
(
user_id integer,
payment_stop_date timestamp without time zone DEFAULT now()
)
你的函数有几个问题,让我们从这里开始。它的缺点是(1)您只需要一个参数来指示月份,使用月的开始和结束设置您自己的问题;(2) 您不需要动态查询,因为您没有更改标识符(表或列名);(3) 你不需要一个循环;(4)你的逻辑是错误的。我还可以提到PostgreSQL使用函数,它们都是从一行开始的,比如
CREATE FUNCTION list\u all\u upsells(…)
,但这太挑剔了
首先,逻辑是:显然,由其电子邮件地址标识的用户从某个付款开始日期
到某个付款停止日期
都可以进行多次订阅。您要查找的是那些在相关月份之前首次订阅的用户,以及在相关月份开始新订阅但不是首次订阅的用户。在这种情况下,筛选器付款开始日期>首次时间
是无用的,因为您已经筛选出在所述月份之前的首次订阅(首次时间1
)和新订阅(付款开始日期介于$1和$2之间
)
第(1)、(2)和(3)点只有在重写函数内部的查询时才变得明显:
CREATE FUNCTION list_all_upsells(timestamp) RETURNS SETOF paid_users AS $$
SELECT paid_users.*
FROM paid_users
JOIN ( -- This JOIN keeps only those rows where the payor_email has a prior subscription
SELECT DISTINCT payor_email,
first_value(payment_start_date) OVER (PARTITION BY payor_email ORDER BY payment_start_date) AS dummy
FROM paid_users
WHERE payment_start_date < date_trunc('month', $1)
) dummy USING (payor_email)
-- This filter keeps only those rows with new subscriptions in the month
WHERE date_trunc('month', payment_start_date) = date_trunc('month', $1)
$$ LANGUAGE sql STRICT;
顺便说一句,这确实回避了一个问题:为什么表中有付费用户。为什么不简单地在表付费用户
中添加一列付费用户
?如果该列为NULL
,则用户仍在订阅。但是整个查询相当奇怪,因为list\u all\u upsells()
会在一个月内返回新的订阅,那么为什么还要在其他时间处理取消的订阅呢
现在谈谈你真正的问题:
SELECT months.m "Month", coalesce(count(up.*), 0) "Total Upselled Accounts"
FROM generate_series('2014-08-01'::timestamp,
date_trunc('month', LOCALTIMESTAMP),
'1 month') AS months(m)
LEFT JOIN list_all_upsells(months.m) AS up ON date_trunc('month', payment_start_date) = m
GROUP BY 1
ORDER BY 1;
从某个起始月到当前月,然后计算每个月的新订阅数,可能为0
我真的不擅长postgres,但是用适当的连接代替execute难道不可能吗?嘿@GSazheniuk我不知道:当查看循环层时,几乎总是这样,转换为使用子查询、连接等组合查询要快得多。这是一个非常好的答案,逻辑非常合理。我试着运行你的list\u all\u upsells create函数,但是在“SELECT”(第5行)附近出现了语法错误-这是为什么@帕特里克,那是个严重的错误。与
选择无关
。我从您的代码开始工作,并将主查询的select列表中的标量子查询更改为常规的连接
:您需要从付费用户中删除之后第二行中的,
。我花了一段时间才找到那个挑剔的小家伙!嘿,帕特里克-应该是当前日期而不是当前时间吗?我得到“错误:函数list\u all\u upsells(带时区的时间)不存在第2行:从list\u all\u upsells(当前时间)开始^HINT:没有函数与给定的名称和参数类型匹配。您可能需要添加显式类型转换。”当我执行当前时间时。当我尝试当前约会时,我得到0 thoAhCURRENT_TIME
给出带有时区的时间戳
。改用LOCALTIMESTAMP
;这会给出一个正常的时间戳。我以为演员是自动的。答案更新。嗯,我不知道为什么,但我得到的结果是0,当我运行最后一个查询时,我只得到10月份的返回
SELECT months.m "Month", coalesce(count(up.*), 0) "Total Upselled Accounts"
FROM generate_series('2014-08-01'::timestamp,
date_trunc('month', LOCALTIMESTAMP),
'1 month') AS months(m)
LEFT JOIN list_all_upsells(months.m) AS up ON date_trunc('month', payment_start_date) = m
GROUP BY 1
ORDER BY 1;