我遇到了一个问题,我花了很多时间试图解决,虽然我有一个解决方案,它很笨重,涉及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进行此操作有任何想法?
答案 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