2009-04-16 10 views
2

Je suis confrontée à un problème que j'ai passé beaucoup de temps à essayer de résoudre, et bien que j'ai une solution, elle est maladroite et implique le traitement pl/sql, et je me demandais ce que les autres pourraient proposer.Récupération d'une valeur précédente courante et la plus récente (Oracle)

Je travaille avec un jeu de données qui crée une nouvelle ligne chaque fois qu'un enregistrement est modifié, conservant ainsi un historique. La version la plus récente est ensuite affichée dans notre application. Considérons une table avec les données suivantes:

 
Person ID Last_Name Address_line1  Effective_Start_Date Effective_End_Date 
4913  Jones  1 First Street  03-aug-02    31-dec-12 
4913  Cross  1 First Street  01-feb-02    02-aug-02 
4913  Cross  86 Green Avenue  01-mar-01    31-jan-02 
4913  Cross  87 Devonshire Road 01-jan-90    28-feb-02 

Dans le cadre d'un rapport, je dois extraire les détails qui ont changé entre un ensemble donné de dates. Par exemple, disons que je veux extraire le address_line1 actuel et le address_line1 précédent avec la date du changement (effective_start_date lorsque la nouvelle adresse a été ajoutée). L'avertissement est que si d'autres données de colonne changent, cela créera une nouvelle ligne aussi. Par exemple, dans l'exemple ci-dessus, le last_name a été modifié après la modification de l'adresse. Malheureusement, la requête doit être générique afin qu'elle puisse être exécutée dans le cadre d'un rapport, c'est-à-dire. ne pas avoir à spécifier explicitement les dates de début et de fin.

Espérons que tout a un sens. J'espère que vous êtes toujours avec moi. Donc, étant donné l'ensemble de données ci-dessus, je me attends de voir les résultats suivants dans mon rapport:

 
Person ID Surname Address_line1 Prev_Address_line1 Effective Start date of New Address Line 1 
4913  Jones 1 First Street 86 Green Avenue  01-feb-02 

Mon approche implique le traitement avec pl/sql et en bouclant sur un grand nombre de dossiers, mais je me demandais si cela peut être fait dans une seule requête sql.

Est-ce que quelqu'un a des idées sur le fait que cela peut être fait en utilisant seulement sql?

Répondre

5
SELECT personID, surname, address_line1, 
     LAG(address_line1) OVER (PARTITION BY personID ORDER BY effectiveDate) AS prev_address_line1 
FROM mytable 
WHERE personID = :myid 
ORDER BY 
     effectiveDate 

Pour revenir à la dernière valeur efficace, utilisez:

SELECT * 
FROM (
     SELECT personID, surname, address_line1, 
       LAG(address_line1) OVER (PARTITION BY personID ORDER BY effectiveDate) AS prev_address_line1, 
       ROW_NUMBER() OVER (PARTITION BY personID ORDER BY effectiveDate DESC) AS rn, 
     FROM mytable 
     WHERE personID = :myid 
     ) 
WHERE rn = 1 
0

Ne pas complètement compris votre question.

Dans le cadre d'un rapport, je dois extraire les détails qui ont changé entre un ensemble donné de dates de.

Versus

Unfortunatey, la requête doit être générique afin qu'il puisse être exécuté dans le cadre d'un rapport , i.e.. ne pas avoir à spécifier explicitement le début effectif et la fin dates.

1) Votre rapport fournit-il une date de début et une date de fin, et cherche-t-il des changements dans la plage? 2) Ou cherchez-vous un rapport général pour tous les changements sans contrainte de date?

également

En supposant que le nom Dernière modification dans l'ensemble des dates pour lesquelles vous exécutez le rapport.

1) Dans votre résultat attendu êtes-vous ignorant complètement le fait que le Last_Name Changé

2) Ou sera-ce une autre ligne dans votre rapport.

du type

Person ID Surname Prev_Surname Effective_Start_date_of_New Surname 
4913  Jones Cross   03-aug-02 

3) Ou est-ce encore plus compliqué que vous allez capturer tous les changements dans une ligne.

Person ID Surname Prev_Surname Effective_Start_date_of_New Surname Address_line1 Prev_Address_line1 Effective_Start_date_of_New_Address_Line_1 
4913  Jones Cross   03-aug-02       1 First Street 86 Green Avenue  01-feb-02 

Pour un rapport qui montre chaque modification, avec les valeurs précédentes pour le nom et l'adresse. Essaye ça. Vous pouvez choisir la dernière modification par un externe et en sélectionnant Change_rank = 1

SELECT PERSON_ID, CHANGED, NEW_VAL, OLD_VAL, EFFECTIVE_START_DATE 
     -- The Rank just lets you pick by order of change incase you only want the last change, or last 2 changes 
     ,RANK() OVER (ORDER BY EFFECTIVE_START_DATE DESC) CHANGE_RANK 
    FROM (
      -- A select over Partition for each column where Old_val and New_Val need to be retrieved. 
      -- In this Select the Address Column and Changed Address Are retrieved. 
      SELECT PERSON_ID, 'ADDRESS_LINE1 CHANGE' CHANGED, ADDRESS_LINE1 NEW_VAL 
       ,LEAD (ADDRESS_LINE1) OVER (PARTITION BY PERSON_ID ORDER BY EFFECTIVE_START_DATE DESC) AS OLD_VAL 
       ,EFFECTIVE_START_DATE 
      FROM TMP_RM_TEST 
      -- Usually You want to run a report for a period, Or remove this entire filter 
      WHERE EFFECTIVE_START_DATE BETWEEN TO_DATE (:REPORT_START_DATE, 'YYYYMMDD') 
              AND TO_DATE (:REPORT_END_DATE, 'YYYYMMDD') 
      UNION 
      -- In this Select the Name Column and Changed Name Are retrieved. 
      SELECT PERSON_ID, 'LAST_NAME CHANGE' CHANGED, LAST_NAME NEW_VAL 
       ,LEAD (LAST_NAME) OVER (PARTITION BY PERSON_ID ORDER BY EFFECTIVE_START_DATE DESC) AS OLD_VAL 
       ,EFFECTIVE_START_DATE 
      FROM TMP_RM_TEST 
      WHERE EFFECTIVE_START_DATE BETWEEN TO_DATE (:REPORT_START_DATE, 'YYYYMMDD') 
              AND TO_DATE (:REPORT_END_DATE, 'YYYYMMDD')) 
    -- Since the Partition By lists all changes over Person_ID, Irrespective of whether the Column changed, Filter to Remove Lines where no change was made to the Coloumn monitored 
WHERE NEW_VAL <> OLD_VAL 
ORDER BY CHANGE_RANK 
Questions connexes