2017-06-30 1 views
0

Mon script devrait être en cours d'exécution, mais il peut ne pas être «efficace» et le problème principal est je suppose que cela prend trop de temps à courir quand je l'exécute au travail toute la session est annulée avant qu'elle ne finisse.Optimiser le script SQL: obtenir la valeur de plage d'une autre table

J'ai essentiellement 2 tables Tableau A - contient toutes les transactions une personne ne

Person's_ID Transaction TransactionDate 
--------------------------------------- 
123    A   01/01/2017 
345    B   04/06/2015 
678    C   13/07/2015 
123    F   28/10/2016 

Tableau B - contient l'ID de la personne et GraduationDate

Ce que je veux faire est de vérifier si une personne est active . Active = s'il y a au moins une transaction effectuée par la personne 1 mois avant son GraduationDate

Le temps d'exécution est trop long imaginer parce que si j'ai des millions de personnes et chaque personne effectuer plusieurs transactions et ces transactions sont enregistrées en ligne par ligne dans le tableau A

SELECT 
PERSON_ID 
FROM 
    (SELECT PERSON_ID, TRANSACTIONDATE FROM TABLE_A) A 
LEFT JOIN 
    (SELECT CIN, GRAD_DATE FROM TABLE_B) B 
ON A.PERSON_ID = B.PERSON_ID 
AND TRANSACTIONDATE <= GRAD_DATE 
WHERE TRANSACTIONDATE BETWEEN GRAD_DATE - INTERVAL '30' DAY AND GRAD_DATE; 

* Tableau A et B sont des produits de tables jointes par conséquent, ils sont subqueried.

+0

Et à quoi devrait ressembler la sortie? –

+0

Juste comme un FYI, votre clause where se traduira par une jointure interne. Si vous voulez vraiment une jointure externe, la clause where doit être déplacée vers la clause on. – Andrew

+0

Salut @GordonLinoff, la sortie que je m'attends est une liste de PERSON_ID qui sont "actifs" - cela signifie avec des transactions dans les 30 jours de la GraduationDate respective de la personne – Joy

Répondre

0

Si vous voulez juste les clients actifs, je voudrais essayer exists:

SELECT PERSON_ID 
FROM TABLE_A A 
WHERE EXISTS (SELECT 1 
       FROM TABLE_B B 
       WHERE A.PERSON_ID = B.PERSON_ID AND 
        A.TRANSACTIONDATE BETWEEN B.GRAD_DATE - INTERVAL '30' DAY AND GRAD_DATE 
      ); 

La performance, cependant, est susceptible d'être similaire à votre requête. Si les tables étaient vraiment des tables, je suggérerais des index. En réalité, vous aurez probablement besoin de comprendre les vues (afin de pouvoir créer de meilleurs index) ou peut-être utiliser des tables temporaires.

+0

Merci, @GordonLinoff, en essayant de regarder dans ce. Ça marche parfaitement! Plus rapide que mon script original. Regarder dans les résultats maintenant. – Joy

0

Un non-équi-jointure peut être tout à fait inefficace (peu importe si elle est codé comme joindre ou non Exists), mais la logique peut être réécrite à:

SELECT 
    PERSON_ID 
FROM 
(-- combine both Selects 
    SELECT 0 AS flag -- indicating source table 
     PERSON_ID, TRANSACTIONDATE AS dt 
    FROM TABLE_A 
    UNION ALL 
    SELECT 1 AS flag, 
     PERSON_ID, GRAD_DATE 
    FROM TABLE_B 
) A 
QUALIFY 
    flag = 1 -- only return a row from table B 
AND Min(dt) -- if the previous row (from table A) is within 30 days 
    Over (PARTITION BY PERSON_ID 
      ORDER BY dt, flag 
      ROWS BETWEEN 1 Preceding AND 1 Preceding) >= dt - 30 

Cela suppose qu'il n'y a qu'une seule ligne à partir du tableau A par personne, sinon le MIN doit être remplacé par:

AND MAX(CASE WHEN flag = 1 THEN dt END) -- if the previous row (from table A) is within 30 days 
    Over (PARTITION BY PERSON_ID 
      ORDER BY dt, flag 
      ROWS UNBOUNDED Preceding) >= dt - 30 
+0

Salut @dnoeth, merci pour cela! Je vérifie maintenant si les données extraites sont correctes. Mais juste pour comprendre, comment est le min (dt) capable de capturer le dans les 30 jours? la transaction doit être effectuée dans les 30 jours suivant la date d'obtention du diplôme. Merci de votre aide! – Joy

+0

@Joy: Le 'MIN' regarde simplement la ligne 'précédente' (aurait pu être' MAX', aussi, le résultat est équivalent au 'LAG' du SQL standard) et le compare à la ligne courante. Pour une ligne de table_A, la ligne précédente contient la date la plus récente (en supposant qu'il n'y a qu'une seule ligne par personne, sinon MIN doit être changé, voir ma modification) – dnoeth