2017-06-05 3 views
0

J'utilise Redshift et j'ai besoin d'une alternative pour une sous-requête corrélée. Je reçois la sous-requête corrélée erreur non prise en charge. Cependant, pour cet exercice particulier d'essayer d'identifier toutes les transactions de ventes faites par le même client dans une heure donnée de la transaction d'origine, je ne suis pas sûr qu'une jointure gauche traditionnelle fonctionnerait non plus. C'est-à-dire que la requête dépend du contexte ou de la valeur courante de la sélection parent. J'ai également essayé quelque chose de similaire en utilisant la fonction de fenêtre row_number() mais encore une fois, j'ai besoin d'un moyen de fenêtre/partition sur une plage de dates - pas seulement customer_id.Redshift Alternative pour la sous-requête corrélée

L'objectif global est de trouver la première transaction de vente pour un identifiant de client donné, puis de trouver toutes les transactions ultérieures effectuées dans les 60 minutes suivant la première transaction. Cette logique se poursuivra pour le reste des transactions pour le même client (et finalement tous les clients de la base de données). C'est-à-dire qu'une fois la fenêtre initiale de 60 minutes établie à partir de la première transaction, une seconde fenêtre de 60 minutes débuterait à la fin de la première fenêtre de 60 minutes et toutes les transactions de la seconde fenêtre seraient également identifiées et combinées puis répétez pour le reste des transactions.

La sortie indiquerait le premier identifiant de transaction ayant lancé la fenêtre de 60 minutes, puis les autres identifiants de transaction ultérieurs qui ont été créés dans la fenêtre de 60 minutes. La deuxième rangée affichera le premier identificateur de transaction fait par le même client dans la fenêtre de 60 minutes suivante (encore une fois, la première transaction après la première fenêtre de 60 minutes serait le début de la seconde fenêtre de 60 minutes). dans la seconde fenêtre de 60 minutes.

L'exemple de requête dans sa forme de base ressemble à la requête suivante:

select 
s1.customer_id, 
s1.transaction_id, 
s1.order_time, 
(
    select 
     s2.transaction_id 
    from 
     sales s2 
    where 
     s2.order_time > s1.order_time and 
     s2.order_time <= dateadd(m,60,s1.order_time) and 
     s2.customer_id = s1.customer_id 
    order by 
     s2.order_time asc 
    limit 1 
) as sales_transaction_id_1, 
(
    select 
     s3.transaction_id 
    from 
     sales s3 
    where 
     s3.order_time > s1.order_time and 
     s3.order_time <= dateadd(m,60,s1.order_time) and 
     s3.customer_id = s1.customer_id 
    order by 
     s3.order_time asc 
    limit 1 offset 1 
) as sales_transaction_id_2, 
(
    select 
     s3.transaction_id 
    from 
     sales s4 
    where 
     s4.order_time > s1.order_time and 
     s4.order_time <= dateadd(m,60,s1.order_time) and 
     s4.customer_id = s1.customer_id 
    order by 
     s4.order_time asc 
    limit 1 offset 1 
) as sales_transaction_id_3 
from 
    (
     select 
      sales.customer_id, 
      sales.transaction_id, 
      sales.order_time 
     from 
      sales 
     order by 
      sales.order_time desc 
    ) s1; 

Par exemple, si un client a fait les opérations suivantes:

customer_id  transaction_id  order_time   
1234    33453   2017-06-05 13:30 
1234    88472   2017-06-05 13:45 
1234    88477   2017-06-05 14:10 

1234    99321   2017-06-07 8:30 
1234    99345   2017-06-07 8:45 

Le résultat attendu serait aussi:

customer_id  transaction_id sales_transaction_id_1 sales_transaction_id_2 sales_transaction_id_3 
1234    33453   88472     88477     NULL 
1234    99321   99345     NULL     NULL 

En outre, il semble que Redshift ne supporte pas les jointures latérales qui semblent s de restreindre davantage les options à ma disposition. Toute aide serait grandement appréciée.

+2

Modifiez votre question et fournissez des exemples de données et les résultats souhaités. . . et une explication de ce que la logique est supposée faire. –

+0

Supporte-t-il les fonctions de fenêtre? CTE? BTW: vous ne faites que sélectionner 's1', donc les deux autres parties de la requête peuvent être remplacées par 'EXISTS (...)' (se débarrasser aussi de l'horrible LIMIT 1) – wildplasser

+1

combine_transaction_id_1 est à la fois utilisé comme alias et comme colonne ce qui est déroutant, comme @GordonLinoff a dit s'il vous plaît fournir des exemples de données et ce que vous voulez faire exactement. – sia

Répondre

0

De votre description, vous voulez juste group by et une sorte de différence de date. Je ne sais pas comment vous voulez combiner les lignes, mais voici l'idée de base:

select s.customer_id, 
     min(order_time) as first_order_in_hour, 
     max(order_time) as last_order_in_hour, 
     count(*) as num_orders 
from (select s.*, 
      min(order_time) over (partition by customer_id) as min_ot 
     from sales s 
    ) s 
group by customer_id, floor(datediff(second, min_ot, order_time)/(60 * 60)); 

Cette formulation (ou quelque chose de similaire parce Postgres n'a pas datediff()) serait également beaucoup plus rapide dans Postgres.

+0

Ceci est sur la bonne voie, cependant, les transactions_id individuelles doivent être listées explicitement. C'est-à-dire que les identifiants de transaction qui tombent dans une fenêtre particulière de 60 minutes doivent être conservés et ne pas être immédiatement agrégés via un groupe. – user3701000

+0

@ utilisateur3701000. . . Ensuite, supprimez le «groupe par» et faites ce que vous voulez. C'est l'idée de ce dont vous avez besoin. –

+0

Avec ou sans le groupe, cela inclurait toutes les transactions. Au lieu de cela, la première transaction serait identifiée, énumérer tous les identifiants de transaction subséquents dans cette fenêtre de 60 minutes, puis passer à la fenêtre suivante de 60 minutes et faire la même chose - c.-à-d. deuxième fenêtre de 60 minutes et ainsi de suite. – user3701000

0

Vous pouvez utiliser les fonctions de fenêtre pour obtenir les transactions suivantes pour chaque transaction. La fenêtre sera client/heure et vous pouvez classer les enregistrements pour obtenir la première transaction "ancre" et obtenir toutes les transactions suivantes dont vous avez besoin:

with 
transaction_chains as (
    select 
    customer_id 
    ,transaction_id 
    ,order_time 
    -- rank transactions within window to find the first "anchor" transaction 
    ,row_number() over (partition by customer_id,date_trunc('minute',order_time) order by order_time) 
    -- 1st next order 
    ,lead(transaction_id,1) over (partition by customer_id,date_trunc('minute',order_time) order by order_time) as transaction_id_1 
    ,lead(order_time,1) over (partition by customer_id,date_trunc('minute',order_time) order by order_time) as order_time_1 
    -- 2nd next order 
    ,lead(transaction_id,2) over (partition by customer_id,date_trunc('minute',order_time) order by order_time) as transaction_id_2 
    ,lead(order_time,2) over (partition by customer_id,date_trunc('minute',order_time) order by order_time) as order_time_2 
    -- 2nd next order 
    ,lead(transaction_id,3) over (partition by customer_id,date_trunc('minute',order_time) order by order_time) as transaction_id_3 
    ,lead(order_time,3) over (partition by customer_id,date_trunc('minute',order_time) order by order_time) as order_time_3 
    from sales 
) 
select 
customer_id 
,transaction_id 
,transaction_id_1 
,transaction_id_2 
,transaction_id_3 
from transaction_chains 
where row_number=1;