SAP HANA SQL соответствует шаблонам строк и комбинациям - PullRequest
0 голосов
/ 18 марта 2020

Я использую HANA и пытаюсь извлечь данные из таблицы на основе нескольких комбинаций значений в столбце: Ниже приведена схема расположения моих клиентов:

Таблица A:

Продукт клиента

AB C P1 AB C P2 AB C P3 AB C G2 AB C G4 AB C G6 AB C G5

Ниже приведены правила продаж кампании:

Комбинации:

Campaign_Name Products_Purchased

Кампания_1 (P1 & P2 и P3) Кампания_2 (G2 или G4) и G6

Если Клиент приобрел все продукты P1, P2 и P3, он будет иметь право на Campaign_1. Если Клиент приобрел какой-либо из продуктов у (G2 или G4) и G6, он будет иметь право на Campaign_2. В этом примере, поскольку клиент «AB C» приобрел комбинации продуктов, упомянутых в кампании, он будет квалифицирован для обеих кампаний.

Ожидаемый результат:

Customer Sales_Campaign

AB C Campaign_1 AB C Campaign_2

Ниже приведены шаги, которые я выполнил до сих пор: Шаг 1: Я извлек все продукты, приобретенные на уровне клиента. Шаг 2: Я объединил все продукты, приобретенные клиентом, в один ряд, разделенный запятой. Шаг 3: Оставьте все возможные комбинации вручную в выписке по делу и установите столбец флага для каждой из кампаний, чтобы определить, приобрел ли клиент какие-либо подходящие sales_campaigns. Шаг 4 : Если флажком является «Y», я извлек название соответствующей кампании. Но если количество продуктов увеличится, то описанный выше шаг 3 будет невозможно вывести.

Step2 Результат:

Заказчик P_COMBO G_COMBO

AB C #, 1, P1, P2, P3 #, 1, G2, G4, G5, G6

Step4 Результат:

Клиент Campaign1_flag Campaign2_flag

AB C YY

SELECT DISTINCT
  B.CUSTOMER
  CASE WHEN P_COMBO ='#,1,P1,P2,P3' THEN 'Y' ELSE 'N' END) AS campaign1_flag,
  (CASE 
     WHEN G_COMBO  ='#,1,G2,G6'       THEN 'Y'
     WHEN G_COMBO  ='#,1,G4,G6'       THEN 'Y'
     WHEN G_COMBO  ='#,1,G2,G5,G6'    THEN 'Y'
     WHEN G_COMBO  ='#,1,G4,G5,G6'    THEN 'Y'
     WHEN G_COMBO  ='#,1,G2,G4,G5,G6' THEN 'Y'
     ELSE 'N'
  END) AS Campaign2_flag
FROM (
  SELECT DISTINCT
    A.CUSTOMER ,
    (   MAX(CASE WHEN A.PRODUCT= '1'  THEN A.PRODUCT ELSE '#' END)
      || MAX(CASE WHEN A.PRODUCT= 'P1'  THEN ',' || A.PRODUCT ELSE '' END)
      || MAX(CASE WHEN A.PRODUCT= 'P2'  THEN ',' || A.PRODUCT ELSE '' END)
      || MAX(CASE WHEN A.PRODUCT= 'P3'  THEN ',' || A.PRODUCT ELSE '' END)
    ) AS P_COMBO  ,
    (   MAX(CASE WHEN A.PRODUCT= '1'  THEN A.PRODUCT ELSE '#' END)
      || MAX(CASE WHEN A.PRODUCT= 'G2'  THEN ',' || A.PRODUCT ELSE '' END)
      || MAX(CASE WHEN A.PRODUCT= 'G4'  THEN ',' || A.PRODUCT ELSE '' END)
      || MAX(CASE WHEN A.PRODUCT= 'G5'  THEN ',' || A.PRODUCT ELSE '' END)
      || MAX(CASE WHEN A.PRODUCT= 'G6'  THEN ',' || A.PRODUCT ELSE '' END)
    ) AS G_COMBO
    FROM
    (SELECT DISTINCT CUSTOMER,PRODUCT FROM customer) A
    GROUP BY CUSTOMER,2,3
 ) B

Пожалуйста, помогите мне найти лучшее решение. Любые идеи очень ценятся.

1 Ответ

0 голосов
/ 26 марта 2020

Если вы разрешите изменить способ хранения определения групп go в кампании, тогда возможно следующее решение:

  1. Кампании определяются набором групп.
  2. Группы содержат идентификаторы или названия продуктов.
  3. Любая группа может рассматриваться как группа OR, что означает, что для соответствия этой группе необходимо купить хотя бы один продукт из этой группы. Кроме того, группы могут быть AND группами, что означает все продуктов, которые необходимо купить для соответствия группе.

Это может выглядеть так:

create column table camp_grps
    (camp NVARCHAR(20) not null
    , grp integer not null
    , grp_type NVARCHAR(3) not null
    , product NVARCHAR(20) not null);


insert into camp_grps values
    ('Campaign 1', '1', 'AND', 'P1');
insert into camp_grps values
    ('Campaign 1', '1', 'AND', 'P2');
insert into camp_grps values
    ('Campaign 1', '1', 'AND', 'P3');

insert into camp_grps values
    ('Campaign 2', '1', 'OR', 'G2');
insert into camp_grps values
    ('Campaign 2', '1', 'OR', 'G4');
insert into camp_grps values
    ('Campaign 2', '2', 'AND', 'G6');  


CAMP       | GRP    | GRP_TYPE| PRODUCT
-----------|--------|---------|-------  
Campaign 1 |    1   |   AND   |     P1     
Campaign 1 |    1   |   AND   |     P2     
Campaign 1 |    1   |   AND   |     P3     
Campaign 2 |    1   |   OR    |     G2     
Campaign 2 |    1   |   OR    |     G4     
Campaign 2 |    2   |   AND   |     G6  

Теперь мы можем получить простой обзор

  • кампаний,
  • групп в каждой из них, включая
  • тип группы,
  • сколько групп составляют кампанию и
  • сколько продуктов составляют каждую группу.

SQL для этого выглядит следующим образом:

select 
    camp
  , grp
  , grp_type
  , count(distinct grp) over
      (partition by camp)     camp_grp_cnt
  , count(*) prd_cnt
from
    camp_grps
group by
    camp, grp, grp_type;   

CAMP        |GRP    |GRP_TYPE   |CAMP_GRP_CNT   |PRD_CNT
------------|-------|-----------|---------------|-------
Campaign 1  |1      |AND        |1              |3      
Campaign 2  |1      |OR         |2              |2      
Campaign 2  |2      |AND        |2              |1      

Остальное немного утомительно, но не слишком сложно. Нам нужно
- подсчитать, сколько совпадающих покупок у каждого клиента для каждой группы - в зависимости от типа группы (AND / OR) нам нужно считать группу как совпавшей , когда количество совпадений с группой больше 0 (OR группа) или, по крайней мере, количество товаров в группе (AND группа).

Объединенный SQL выглядит следующим образом:

with grp_ref as
    (select 
         camp
       , grp
       , grp_type
       , count(distinct grp) over
           (partition by camp)     camp_grp_cnt
       , count(*) prd_cnt
    from
        camp_grps
    group by
        camp, grp, grp_type),
grp_match as
(select 
     p.customer, p.product
   , gr.camp camp_ref, gr.grp grp_ref, gr.grp_type grp_type_ref, gr.prd_cnt prd_cnt_ref
   , gr.camp_grp_cnt
   , count(*) over
        (partition by p.customer, cg.camp, gr.grp) camp_total_matches
from 
        purchases p
    left outer join camp_grps cg
            on p.product = cg.product
    inner join grp_ref gr
        on (cg.camp, cg.grp) = (gr.camp, gr.grp)),
match_cnter as 
 (select
      customer, product
    , camp_ref, grp_ref, grp_type_ref, prd_cnt_ref, camp_total_matches
    , camp_grp_cnt
    , case 
        when grp_type_ref = 'AND' 
            and ((camp_total_matches - prd_cnt_ref) >= 0) then
                'AND grp matched'
        when grp_type_ref = 'OR' 
            and (camp_total_matches > 1) then
                'OR grp matched'
      end matched_grp_info
    , case 
        when grp_type_ref = 'AND' 
            and ((camp_total_matches - prd_cnt_ref) >= 0) then
                1
        when grp_type_ref = 'OR' 
            and (camp_total_matches > 1) then
                1
      end matched_grp
 from 
    grp_match)
select  
    customer, camp_ref, SUM(matched_grp), MIN(camp_grp_cnt)
from 
    match_cnter
group by 
    customer, camp_ref;

Результат выглядит следующим образом:

CUSTOMER    |CAMP_REF   |SUM(MATCHED_GRP)   | MIN(CAMP_GRP_CNT)
------------|-----------|-------------------|---------------------------
ABC         |Campaign 1 |3                  |1                
ABC         |Campaign 2 |3                  |2                
XYZ         |Campaign 1 |?                  |1                

За CUSTOMER и CAMP_REF (ссылка на кампанию) мы видим, сколько групп кампании было найдено (SUM(MATCHED_GRP)) ) и сколько необходимо сопоставить, чтобы иметь право на участие в кампании (MIN(CAMP_GRP_CNT)).

Клиент ABC имеет право участвовать в обеих кампаниях 1 и 2, в то время как клиент XYZ не соответствует ни одной группе ( см. ? как NULL в результате).

...