SQL回答:基于激活/停用记录,哪个客户在给定月份中处于活动状态

时间:2011-06-03 01:38:09

标签: sql postgresql group-by

给出一个表

custid | date       | action
1      | 2011-04-01 | activate
1      | 2011-04-10 | deactivate
1      | 2011-05-02 | activate
2      | 2011-04-01 | activate
3      | 2011-03-01 | activate
3      | 2011-04-01 | deactivate

数据库是PostgreSQL。

我想要一个SQL查询来显示在5月的任何阶段都处于活动状态的客户。

所以,在上面,那将是1和2.

我无法理解我的方法来解决这个问题。有什么指针吗?

更新

客户2在5月期间处于活动状态,因为他在5月之前被激活,并且自从他被激活后未被停用。就像在,我本月活着,但本月没出生,我没有死。

select distinct custid
from MyTable
where action = 'active' and date >= '20110501' and date < '20110601'

这种方法不起作用,因为它只显示在may期间的激活,而不是'actives'。

3 个答案:

答案 0 :(得分:3)

注意:这将是一个起点,仅适用于2011年。

忽略任何遗留的错误,此代码(针对每个客户)查看1)客户在5月之前的最新状态更新和2)客户在5月期间是否变为活动状态?

SELECT
  Distinct CustId

FROM
 MyTable -- Start with the Main table

 -- So, was this customer active at the start of may?
 LEFT JOIN  -- Find this customer's latest entry before May of This Year
   (select 
     max(Date) 
   from
     MyTable
   where
     Date < '2011-05-01') as CustMaxDate_PreMay on CustMaxDate_PreMay.CustID = MyTable.CustID

 -- Return a record "1" here if the Customer was Active on this Date
 LEFT JOIN 
   (select
      1 as Bool,
      date
    from
      MyTable
   ) as CustPreMay_Activated on CustPreMay_Activated.Date = CustMaxDate_PreMay.Date and CustPreMay_Activated.CustID = MyTable.CustID and CustPreMay_Activated = 'activated'

 -- Fallback plan: If the user wasn't already active at the start of may, did they turn active during may? If so, return a record here "1"
 LEFT JOIN  
   (select 
     1 as Bool 
   from
     MyTable
   where
     Date <= '2011-05-01' and Date < '2011-06-01' and action = 'activated') as TurnedActiveInMay on TurnedActiveInMay .CustID = MyTable.CustID

 -- The Magic: If CustPreMay_Activated is Null, then they were not active before May
 --            If TurnedActiveInMay is also Null, they did not turn active in May either
 WHERE
   ISNULL(CustPreMay_Activated.Bool, ISNULL(TurnedActiveInMay.Bool, 0)) = 1

注意:

您可能需要将

From (Select distinct CustID from MyTable) as Customers

我不清楚只看这个代码是否A)太慢或B)以某种方式引起欺骗或问题,因为启动FROM子句@ MYTable可能包含每个客户的许多记录。 DISTINCT子句可能会解决这个问题,但我想提到这个解决方法。

最后,我会让你在不同年份完成这项工作。

答案 1 :(得分:2)

试试这个

select t2.custid from
(
-- select the most recent entry for each customer
select custid, date, action 
from cust_table t1 
where date = (select max(date) 
    from cust_table where custid = t1.custid)
) as t2
where t2.date < '2011-06-01'
-- where the most recent entry is in May or is an activate entry
-- assumes they have to have an activate entry before they get a deactivate entry 
and (date > '2011-05-01' or [action] = 'activate')

答案 2 :(得分:0)

在PostgreSQL 8.4 +中:

WITH ActivateDates AS (
  SELECT
    custid,
    date,
    ROW_NUMBER() OVER (PARTITION BY custid ORDER BY date) AS rownum
  FROM atable
  WHERE action = 'activate'
),
DeactivateDates AS (
  SELECT
    custid,
    date,
    ROW_NUMBER() OVER (PARTITION BY custid ORDER BY date) AS rownum
  FROM atable
  WHERE action = 'deactivate'
),
ActiveRanges AS (
  SELECT
    a.custid,
    a.date AS activated,
    COALESCE(b.date, '21000101'::date) AS deactivated
  FROM ActivateDates a
    LEFT JOIN DeactivateDates d ON a.custid = d.custid AND a.rownum = d.rownum
)
SELECT DISTINCT custid
FROM ActiveRanges
WHERE deactivated > '20110501'
  AND activated   < '20110601'