SQL для ответа: какие клиенты были активны в течение определенного месяца, на основе записей активации / деактивации - PullRequest
1 голос
/ 03 июня 2011

Имеется таблица

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 отображал клиентов, которые были активны на любом этапе в течение мая.

Итак, в приведенном выше примере это были бы 1 и 2.

Я просто не могу найти способ приблизиться к этому.Любые указатели?

обновление

Клиент 2 был активным в течение мая, так как он был активирован до мая, и не деактивирован, так как он был активирован.Например, я жив в этом месяце, но не родился в этом месяце, и я не умер.

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

Этот подход не будет работать, так как он показывает только активации во время мая, а не'' активные вещества.

Ответы [ 3 ]

3 голосов
/ 03 июня 2011

Примечание. Это будет отправная точка и будет действовать только в 2011 году.

Игнорируя любые давние ошибки, этот код (для каждого клиента) просматривает 1) последнее обновление статуса клиента до мая и 2) активировался ли клиент в течение мая?

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 MyTable 'на

From (Select distinct CustID from MyTable) as Customers

Мне неясно, просто глядя на этот код, будет ли он A) слишком медленным или B) каким-либо образом приводить к ошибкам или проблемам из-за запуска предложения FROM @ MYTable, которое может содержать много записей на одного клиента. Предложение DISTINCT, вероятно, позаботится об этом, но решил, что я бы упомянул об этом обходном пути.

Наконец, я оставлю вам возможность заниматься этой работой в разные годы.

2 голосов
/ 03 июня 2011

Попробуйте это

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')
0 голосов
/ 03 июня 2011

В 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'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...