Отметить кого-то, если он приобрел тот же продукт раньше и если он купил какой-либо - PullRequest
3 голосов
/ 02 апреля 2019

Ситуация:

Мне нужно добавить два флага столбца, обозначенных так:

  • Независимо от того, выбрал ли человек то же самое продукт до даты покупки.
  • Независимо от того, приобрел ли человек любой другой продукт до даты покупки.

Выходные данные должны иметь 5 столбцов:

  1. Электронная почта
  2. ProductName
  3. Дата покупки
  4. SameProduct (0 = Нет, 1 = Да)
  5. AnyProduct (0 = Нет, 1 = Да)

Необработанные данные выглядят так:

abc@gmail.com   cucumber    01-02-2019
abc@gmail.com   orange      04-02-2019
abc@gmail.com   grapefruit  15-02-2019
cde@gmail.com   blackberry  06-02-2019
cde@gmail.com   lime        15-02-2019
cde@gmail.com   lime        20-02-2019
zzz@gmail.com   apple       02-02-2019
zzz@gmail.com   apple       18-02-2019
zzz@gmail.com   orange      19-02-2019
zzz@gmail.com   apple       28-02-2019

Цель:

И мой вывод будет выглядеть так:

Email           ProductName DatePurchased   SameProduct     AnyProduct
abc@gmail.com   cucumber    01-02-2019      0               0
abc@gmail.com   orange      04-02-2019      0               1
abc@gmail.com   grapefruit  15-02-2019      0               1
cde@gmail.com   blackberry  06-02-2019      0               0
cde@gmail.com   lime        15-02-2019      0               1
cde@gmail.com   lime        20-02-2019      1               1
zzz@gmail.com   apple       02-02-2019      0               0   
zzz@gmail.com   apple       18-02-2019      1               1   
zzz@gmail.com   orange      19-02-2019      0               1
zzz@gmail.com   apple       28-02-2019      1               1

То, что я пытался: я пытался дважды объединиться и использовать операторы case, но я чувствую, что этот путь крайне неэффективен.

Фиктивные данные:

create table #table1 (email varchar(20), productname varchar(20), datepurchased date)
insert into #table1 values
('abc@gmail.com','cucumber','2019-02-01'),
('abc@gmail.com','orange','2019-02-04'),
('abc@gmail.com','grapefruit','2019-02-15'),
('cde@gmail.com','blackberry','2019-02-06'),
('cde@gmail.com','lime','2019-02-15'),
('cde@gmail.com','lime','2019-02-20'),
('zzz@gmail.com','apple','2019-02-02'),
('zzz@gmail.com','apple','2019-02-18'),
('zzz@gmail.com','orange','2019-02-19'),
('zzz@gmail.com','apple','2019-02-28')

Примечание: Мои фактические данные содержат более 100 миллионов строк.Я не уверен, какой тип запроса сделает обработку данных максимально быстрой.

Ответы [ 4 ]

3 голосов
/ 02 апреля 2019

Еще один вариант получения результата.

Я использую ROW_NUMBER () - 1, чтобы мы могли дать первому вхождению нулевое значение. Затем я использую SIGN () для преобразования любого положительного значения в 1.

SELECT *,
    SameProduct = SIGN(ROW_NUMBER() OVER(PARTITION BY email, productname ORDER BY datepurchased)-1),
    AnyProduct  = SIGN(ROW_NUMBER() OVER(PARTITION BY email ORDER BY datepurchased)-1)
FROM #table1
ORDER BY email, datepurchased;

При необходимости его можно преобразовать в бит, чтобы получить тот же результат, что и при использовании SIGN (), но только в этом случае, когда все значения положительны.

SELECT *,
    SameProduct = CAST(ROW_NUMBER() OVER(PARTITION BY email, productname ORDER BY datepurchased)-1 AS bit),
    AnyProduct  = CAST(ROW_NUMBER() OVER(PARTITION BY email ORDER BY datepurchased)-1 AS bit)
FROM #table1
ORDER BY email, datepurchased;
2 голосов
/ 02 апреля 2019

Мое решение будет использовать LAG() и ROW_NUMBER().

LAG() всегда относится к предыдущей записи, поэтому весьма полезно проверить, равны ли предыдущий и текущий продукты.

ROW_NUMBER() будет использоваться только для отметки первой покупки (номер строки = 1)

Конечно, пункты PARTITION BY и ORDER BY важны для получения записей в правильном порядке.

Я также проверил решение Vamsi Prabhalas, но производительность с IIF, похоже, намного быстрее, чем CASE-WHEN.

SELECT email
      ,productname
      ,datepurchased
      ,IIF(LAG(productname) OVER (PARTITION BY email ORDER BY email, datepurchased) = productname, 1,0) AS SameProduct
      ,IIF(ROW_NUMBER() OVER (PARTITION BY email ORDER BY email, datepurchased) = 1, 0, 1) AS AnyProduct
  FROM #table1
1 голос
/ 02 апреля 2019

Я бы использовал row_number():

select t.*,
       (case when 1 = row_number() over (partition by email, productname order by datepurchased) 
             then 0 else 1
        end) as same_product,
       (case when 1 = row_number() over (partition by email order by datepurchased) 
             then 0 else 1
        end) as any_product
from #table1 t;

Обратите внимание, что единственная разница - row_number().

. Вы также можете сделать это без сравнения case:

select t.*,
       coalesce(max(1) over (partition by email, productname order by datepurchased rows between unbounded preceding and 1 preceding), 0) as same_product,
       coalesce(max(1) over (partition by email order by datepurchased rows between unbounded preceding and 1 preceding), 0) as any_product
from table1 t
order by email, datepurchased;

Здесь - это дБ <> скрипка.

1 голос
/ 02 апреля 2019

Один способ сделать это с помощью count оконной функции или row_number.

--count
select t.*
       ,case when count(*) over(partition by email,productname order by datepurchased) > 1 then 1 else 0 end as same_prev
       ,case when count(*) over(partition by email order by datepurchased) > 1 then 1 else 0 end as any_prev
from tbl t

--row_number
select t.*
           ,case when row_number() over(partition by email,productname order by datepurchased) > 1 then 1 else 0 end as same_prev
           ,case when row_number() over(partition by email order by datepurchased) > 1 then 1 else 0 end as any_prev
from tbl t
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...