2009-04-16 4 views
2

У меня возникла проблема, с которой я потратил много времени, пытаясь решить проблему, и хотя у меня есть решение, оно неудобно и включает обработку pl/sql, и мне было интересно, что другие могут придумать.Получение текущего и последнего предыдущего значения (Oracle)

Я работаю с набором данных, который создает новую строку при каждом изменении записи, тем самым сохраняя историю. В нашем приложении отображается самая современная версия. Рассмотрим таблицу со следующими данными:

 
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?

ответ

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 
0

Не совсем понимаю ваш вопрос.

В рамках доклада, мне нужно извлечь детали, которые изменились между данным набором дат.

Versus

Unfortunatey, запрос должен быть родовым, так что он может работать как часть отчета, то есть. не обязательно указывать фактические начальные и конечные даты .

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