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-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)));

list_all_upsells 过程如下所示:

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

我有一个查询要获取我们开展业务的每个月的第一天和最后一天:

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;

现在我如何创建一个函数来循环遍历 list_all_upsells 并获取每个月的计数? IE。 startmonth 的第一个查询给了我 2014-03-01, 2014-04-01, ...to 2015-10-01 而 endmonth 的第二个查询给了我2014-03-31、2014-04-30、...至 2015-10-31。我想在这几个月的每个月都运行 list_all_sells,这样我就可以得到每个月我们有多少追加销售帐户的总计数

我的 paid_users 表如下所示:

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()
)

paid_users_no_more:

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_all_upsells(...) 的行开头但这太挑剔了。

从逻辑开始:显然,一个由他的电子邮件地址识别的用户从某个 payment_start_date 订阅了一个订阅。直到某个payment_stop_date并且可以多次执行此操作。您正在寻找那些在相关月份之前进行首次订阅,并且在相关月份开始新订阅但不是首次订阅的用户。在这种情况下,过滤器 payment_start_date > first_time是无用的,因为您已经筛选出在相关月份之前的第一个订阅 ( first_time < $1 ) 和一个新订阅 ( payment_start_date BETWEEN $1 AND $2)。

第 (1)、(2) 和 (3) 点只有在函数内部重写查询时才会变得明显:

CREATE FUNCTION list_all_upsells(<b>timestamp</b>) 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;

由于函数体已缩减为单个 SQL 语句,函数现在是一个 sql。语言功能,比plpgsql更高效.您现在只提供一个参数,可以是您想要数据的月份中的任何时刻,因此 list_all_upsells(LOCALTIMESTAMP)会给你当月的结果。根据您发布的查询,它将是:

SELECT count(up.*) AS "Total Upsell Accounts in October"
FROM list_all_upsells(LOCALTIMESTAMP) up
WHERE up.user_id NOT IN 
  (SELECT DISTINCT user_id FROM paid_users_no_more 
   WHERE date_trunc('month', payment_stop_date) <>
         date_trunc('month', up.payment_start_date)
  );

顺便说一下,这真的回避了为什么你有 table paid_users_no_more 的问题.为什么不简单地添加一列 payment_stop_date到表 paid_users ?该列在哪里 NULL用户仍然订阅。但是整个查询很奇怪,因为 list_all_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;

Generate a series of months从某个开始月份到当前月份,然后计算每个月的新订阅数,可能为 0。

SQLFiddle

关于postgresql - 如何创建一个循环遍历 PostgreSQL 中另一个函数的函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33001854/

相关文章:

postgresql - pl/pgsql 中的一组值

postgreSQL表设计

sql - PostgreSQL:LIMIT + ORDER 返回不一致的结果

postgresql - Azure 上 Postgresql 数据库的 Azure_superuser 帐户密码

postgresql - 存储过程的锁定和并发执行

python - 使用 postgresql、python 和存储过程的日期时间

java - 无法为 hibernate 项目创建请求的服务 [org.hibernate.engine.jdbc.env.spi.JdbcEnvironment]

sql - 多分区Postgres表的高效查询

ruby-on-rails - Ruby On Rails - 地理编码器 - 接近条件

postgresql - 将超过 5000 万从 Pyspark df 写入 PostgresQL,最有效的方法