Найдите точные даты начала для клиентов, имеющих несколько учетных записей в SQL Server 2014 - PullRequest
1 голос
/ 27 января 2020

У меня есть таблица текущего счета, которая содержит столбцы Cust_id (идентификатор клиента), Open_Date (дата начала) и Closed_Date (дата окончания). Существует одна строка для каждой учетной записи. Клиент может открыть несколько счетов в любой точке. Я хотел бы знать, как долго человек был клиентом.

например, 1:

CREATE TABLE [Cust]
(
    [Cust_id] [varchar](10) NULL,
    [Open_Date] [date] NULL,
    [Closed_Date] [date] NULL
)

insert into [Cust] values ('a123', '10/01/2019', '10/15/2019')
insert into [Cust] values ('a123', '10/12/2019', '11/01/2019')

В идеале я хотел бы вставить это в таблицу с одной строкой, которая говорит это человек был клиентом с 01.10.2009 по 01.11.2009. (поскольку он открыл свой второй аккаунт, прежде чем закрыл свой предыдущий.

Аналогично, например, 2:

insert into [Cust] values ('b245', '07/01/2019', '09/15/2019')
insert into [Cust] values ('b245', '10/12/2019', '12/01/2019')

Я хотел бы видеть 2 строки в этом случае - один, который показывает, что он был клиент с 07/01 по 09/15, а затем снова с 10/12 по 12/01.

Можете ли вы указать мне лучший способ получить это?

Ответы [ 3 ]

3 голосов
/ 27 января 2020

Я бы подошел к этому как к проблеме пробелов и островков. Вы хотите сгруппировать группы смежных строк, чьи периоды перекрываются.

Вот один из способов решить эту проблему, используя lag() и совокупный sum(). Каждый раз, когда открытая дата больше закрытой даты предыдущей записи, начинается новая группа.

select 
    cust_id,
    min(open_date) open_date,
    max(closed_date) closed_date
from (
    select 
        t.*,
        sum(case when not open_date <= lag_closed_date then 1 else 0 end) 
            over(partition by cust_id order by open_date) grp
    from (
        select 
            t.*,
            lag(closed_date) over (partition by cust_id order by open_date) lag_closed_date
        from cust t
    ) t
) t
group by cust_id, grp

In эта скрипта БД с вашими данными выборки, запрос выдает:

cust_id | open_date  | closed_date
:------ | :--------- | :----------
a123    | 2019-10-01 | 2019-11-01 
b245    | 2019-07-01 | 2019-09-15 
b245    | 2019-10-12 | 2019-12-01 
1 голос
/ 28 января 2020

Я бы решил это с помощью рекурсии. Хотя это, конечно, очень тяжело, в нем должны учитываться даже самые сложные тайминги аккаунта (при условии, что ваши данные имеют такой). Однако, если предоставленные образцы данных настолько сложны, насколько вам необходимо решить, я настоятельно рекомендую придерживаться решения, представленного выше. Это гораздо более кратко и ясно.

WITH x (cust_id, open_date, closed_date, lvl, grp) AS (
   SELECT cust_id, open_date, closed_date, 1, 1
     FROM (
             SELECT cust_id
                  , open_date
                  , closed_date
                  , row_number()
                    OVER (PARTITION BY cust_id ORDER BY closed_date DESC, open_date) AS rn
               FROM cust
          ) AS t
    WHERE rn = 1
    UNION ALL
   SELECT cust_id, open_date, closed_date, lvl, grp
     FROM (
             SELECT c.cust_id
                  , c.open_date
                  , c.closed_date
                  , x.lvl + 1                                                              AS lvl
                  , x.grp + CASE WHEN c.closed_date < x.open_date THEN 1 ELSE 0 END        AS grp
                  , row_number() OVER (PARTITION BY c.cust_id ORDER BY c.closed_date DESC) AS rn
               FROM cust c
                    JOIN x
                         ON x.cust_id = c.cust_id
                            AND c.open_date < x.open_date
          ) AS t
    WHERE t.rn = 1
     )
SELECT cust_id, min(open_date) AS first_open_date, max(closed_date) AS last_closed_date
  FROM x
 GROUP BY cust_id, grp
 ORDER BY cust_id, grp

Я бы также добавил предостережение, которое я не запускаю на SQL Сервере, поэтому могут быть различия в синтаксисе, которые я не учел. Надеюсь, они незначительные, если они есть.

0 голосов
/ 27 января 2020

вы можете попробовать что-то вроде этого:

select distinct
  cust_id,
  (select min(Open_Date)
   from Cust as b
   where b.cust_id = a.cust_id and 
   a.Open_Date <= b.Closed_Date and
   a.Closed_Date >= b.Open_Date
  ),
  (select max(Closed_Date)
   from Cust as b
   where b.cust_id = a.cust_id and 
   a.Open_Date <= b.Closed_Date and
   a.Closed_Date >= b.Open_Date
  )
from Cust as a

так, для каждой строки - вы выбираете минимальные и максимальные даты из всех перекрывающихся диапазонов, позже отдельные отфильтровывают дубликаты

...