检索当前和最近的先前值(Oracle)

时间:2009-04-16 13:09:12

标签: sql oracle

我遇到了一个问题,我花了很多时间试图解决,虽然我有一个解决方案,它很笨重,涉及pl / sql处理,我想知道别人可能会想出什么。

我正在处理一个数据集,每次更改记录时都会创建一个新行,从而保留历史记录。然后,在我们的应用程序中显示最新版本。考虑一个包含以下数据的表:

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

作为报告的一部分,我需要提取在给定日期之间发生变化的细节。例如,假设我想在添加新地址时提取当前address_line1和之前的address_line1以及更改日期(effective_start_date)。需要注意的是,如果其他列数据发生更改,这也将创建一个新行。例如,在上面的示例中,last_name在地址更改后发生了更改。

不幸的是,查询必须是通用的,以便它可以作为报告的一部分运行,即。无需明确指定有效的开始和结束日期。

希望一切都有道理。希望你们还和我在一起。因此,根据上面的数据集,我希望在我的报告中看到以下结果:

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

我的方法涉及使用pl / sql进行处理并循环显示相当多的记录,但我想知道是否可以在单个sql查询中完成此操作。

有没有人对是否只使用sql进行此操作有任何想法?

2 个答案:

答案 0 :(得分: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

要返回上一个有效值,请使用:

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

答案 1 :(得分:0)

不要完全理解你的问题。

  

作为报告的一部分,我需要提取   细节之间发生了变化   一组给定的日期。

Versus

  

不幸的是,查询必须是   通用的,以便它可以作为一部分运行   一份报告,即。不必指定   明确有效的开始和结束   日期。

1)您的报告是否提供开始日期和结束日期,并查找范围内的变化? 2)或者您是否正在寻找所有更改的一般报告而没有约束日期?

另外

假设在您运行报告的日期集中更改了姓氏。

1)在您的预期结果中,您完全忽略了Last_Name已更改的事实

2)或者这将是您报告中的另一行。

的类型

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

3)或者,您将捕获一行中的所有更改甚至更复杂。

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

对于显示每个更改的报告,使用以前的名称和地址值。试试这个。您可以通过外部选择选择最后一次更改并选择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