Выберите самую последнюю покупку перед последней покупкой

У меня есть таблица, которая выглядит примерно так:

customer_id  purchase_date  category
1            9/1/2018       Electronics
1            9/2/2018       Art
1            9/3/2018       Books
1            9/5/2018       CPG
2            9/2/2018       Books
2            9/4/2018       Electronics
2            9/20/2018      CPG     

Отсюда я пытаюсь получить самую последнюю покупку до покупки CPG. Чтобы объяснить дальше, вот мои шаги:

Шаг 1. Создайте таблицу покупок не в категории CPG:

WITH OTHERS AS(
    SELECT customer_id,
           category as others_category,
           purchase_date
    FROM orders o
    WHERE category IN ('Electronics', 'Books', 'Art')
),

Шаг 2. Создайте таблицу покупок в категории CPG:

CPG AS( 
    SELECT customer_id,
           category as cpg_category,
           purchase_date
    FROM orders o
    WHERE category = 'CPG'
)

Шаг 3. Левое присоединение: вот где я застрял. Я хотел бы создать таблицу, в которой есть последняя покупка ДРУГОЙ до покупки CPG. т.е. результат должен выглядеть так:

others_category  count_distinct_customers
Electronics            1
Books                  1

В идеале я бы не хотел использовать CTE. Тип SQL - SQL Server 2017.


person user11227022    schedule 19.03.2019    source источник
comment
Просто для пояснения, будет ли count_distinct_customers когда-либо иметь более одного, если вы просматриваете покупку до CPG?   -  person GreyOrGray    schedule 19.03.2019
comment
ROW_NUMBER() должно легко решить эту проблему.   -  person Eric    schedule 19.03.2019
comment
@GreyOrGray Да! Если несколько человек купили книгу до CPG (как в моем полном наборе данных), count_distinct_customer будет ›1.   -  person user11227022    schedule 19.03.2019


Ответы (5)


Именно так я бы сделал это в SQL Server 2017, однако я не уверен, сработает ли это в 2005 году (к сожалению, как я уже сказал, у меня больше нет тестовой среды 2005 года). Я думаю APPLY был добавлен в SQL Server 2008. Конечно, "VTE" не будет работать в 2005 году, поскольку предложение конструктора VALUES было добавлено в 2008 году (если я правильно помню), однако вы иметь таблицу для тестирования по крайней мере:

WITH VTE AS(
    SELECT V.customer_id,
           CONVERT(date,V.purchase_date,101) AS purchase_date,
           V.category
    FROM (VALUES(1,'9/1/2018 ','Electronics'),
                (1,'9/2/2018 ','Art'),
                (1,'9/3/2018 ','Books'),
                (1,'9/5/2018 ','CPG'),
                (2,'9/2/2018 ','Books'),
                (2,'9/4/2018 ','Electronics'),
                (2,'9/20/2018','CPG')) V(customer_id,purchase_date,category))
SELECT V2.category,
       COUNT(DISTINCT V2.customer_id) AS DistinctCustomers
FROM VTE V1
     CROSS APPLY (SELECT TOP 1
                         customer_id,
                         purchase_date,
                         category
                  FROM VTE ca 
                  WHERE ca.customer_id = V1.customer_id
                    AND ca.purchase_date < V1.purchase_date
                  ORDER BY ca.purchase_date DESC) V2

WHERE V1.category = 'CPG'
GROUP BY V2.category;
person Larnu    schedule 19.03.2019
comment
CROSS APPLY был добавлен в SQL Server 2005, поэтому он должен иметь возможность его использовать. mssqltips.com/sqlservertip/1958/ < / а> - person Marc Guillot; 19.03.2019

Перефразируя, вам нужны только покупки, за которыми сразу же следовала «покупка CPG» (тем же клиентом?).

Аналитическая функция LEAD() позволяет вам найти то, что находится в "следующей" строке, без необходимости присоединять данные обратно к себе.

WITH
  orders_with_lookup AS
(
  SELECT
    *,
    LEAD(category) OVER (PARTITION BY customer_id ORDER BY purchase_date)   AS customers_next_purchase_category
  FROM
    orders
)
SELECT
  category,
  COUNT(DISTINCT customer_id)   AS count_distinct_customers
FROM
  orders_with_lookup
WHERE
  customers_next_purchase_category = 'CPG'
GROUP BY
  category
ORDER BY
  category
person MatBailie    schedule 19.03.2019

Попробуй это

;WITH CTE(customer_id , purchase_date , category)
AS
(
SELECT 1,'9/1/2018' ,'Electronics' UNION ALL
SELECT 1,'9/2/2018' ,'Art'         UNION ALL
SELECT 1,'9/3/2018' ,'Books'       UNION ALL
SELECT 1,'9/5/2018' ,'CPG'         UNION ALL
SELECT 2,'9/2/2018' ,'Books'       UNION ALL
SELECT 2,'9/4/2018' ,'Electronics' UNION ALL
SELECT 2,'9/20/2018','CPG'     
)
,CTE2 
AS
(
SELECT customer_id,purchase_date,category, 
       ROW_NUMBER()OVER(PARTITION BY customer_id ORDER BY purchase_date DESC) AS MostRecentPurchase
FROM
(
SELECT customer_id , 
       CAST( purchase_date AS DATE) purchase_date, 
       category
FROM CTE
)dt
)
SELECT Category AS Others_category,
       COUNT(DISTINCT customer_id) AS Count_distinct_customers
FROM  CTE2
WHERE  MostRecentPurchase = 2
GROUP BY category

Результат

Others_category     Count_distinct_customers
-----------------------------------------
Books                   1
Electronics             1
person Sreenu131    schedule 19.03.2019

Вы можете использовать OUTER APPLY (доступный с SQL Server 2005) для присоединения каждого заказа CPG к его предыдущей покупке OTHERS, а затем вы можете просто вернуть данные этого заказа OTHERS.

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

   SELECT DISTINCT others.*
   FROM orders cpg
        OUTER APPLY (SELECT top 1 others.*
                     FROM orders as others
                     WHERE category <> 'CPG' and
                           others.purchase_date < cpg.purchase_date
                           order by others.purchase_date desc) as others
   WHERE category = 'CPG'
person Marc Guillot    schedule 19.03.2019

Вот еще один подход ... так много способов снять шкуру с этой кошки.

declare @Something table
(
    customer_id int
    , purchase_date date
    , category varchar(20)
)
insert @Something values
(1, '9/1/2018 ', 'Electronics')
, (1, '9/2/2018 ', 'Art')
, (1, '9/3/2018 ', 'Books')
, (1, '9/5/2018 ', 'CPG')
, (2, '9/2/2018 ', 'Books')
, (2, '9/4/2018 ', 'Electronics')
, (2, '9/20/2018', 'CPG')

, (3, '9/2/2018 ', 'Books') --added customer 3 
, (3, '9/4/2018 ', 'Electronics')
, (3, '9/20/2018', 'CPG')

select category
    , DistinctCustomerCount = count(*)
from
(
    select *
        , RowNum = row_number()over(partition by customer_id, case when category = 'CPG' then 1 else 0 end order by purchase_date desc)
    from @Something
) x
where x.category <> 'CPG'
    and x.RowNum = 1
group by x.category
person Sean Lange    schedule 19.03.2019