Обновить последнюю строку с данными от второй до последней строки - PullRequest
3 голосов
/ 15 декабря 2009
CREATE TABLE Landmark
(
   Indexc NUMBER
   Birth DATE
   Address VARCHAR2(50 BYTE)
   City VARCHAR2(30 BYTE)
   State CHAR(2 BYTE)
   Zip VARCHAR2(15 BYTE)
   ...
   Other properties
)  

ALTER TABLE Landmark ADD (
   CONSTRAINT Landmark_PK PRIMARY KEY (Indexc, Birth));  

CREATE TABLE MapPoint
(
   Indexc NUMBER
   Longitude FLOAT(126)
   Latitude FLOAT(126)
   BuildDate DATE
)  

ALTER TABLE MapPoint ADD (
   CONSTRAINT MapPoint_FK FOREIGN KEY (Indexc) REFERENCES Landmark (Indexc));  

Таблица ориентиров содержит список ориентиров, которые могут время от времени перемещаться. Таблица MapPoint содержит долготу и широту для этих ориентиров. Каждый раз, когда изменяются свойства ориентира, в таблицу Ориентир для этого ориентира вставляется новая строка с новой датой рождения без адреса.

У меня есть скрипт, который установит для BuildDate значение null в таблице MapPoint для всех ориентиров, которые не были физически перемещены, и я пытаюсь написать оператор UDPATE, который будет использовать адрес из последней строки. Следующее близко к тому, что я хочу, но не работает, потому что самый внутренний подзапрос не будет коррелировать с оператором обновления. Я получаю ORA-00904: "lm2". "Index": неверный идентификатор.

UPDATE Landmark lm1
   SET (Address, City, State, Zip) = (
   SELECT Address, City, State, Zip
     FROM Landmark lm2
    WHERE lm2.Indexc = lm1.Indexc
      AND lm2.birth = (
      SELECT MIN(Birth)
        FROM (
         SELECT Birth
           FROM Landmark lm3
          WHERE lm3.Indexc = lm2.Indexc
          ORDER BY Birth DESC)
         AND ROWNUM < 3))
 WHERE Indexc IN (
   SELECT Indexc
     FROM MapPoint
    WHERE BuildDate IS NULL);

Образец до:

Indexc                    Birth        Address          City   State     Zip
------   ----------------------   ------------   -----------   -----   -----
    45    8/16/2009  4:46:21 AM    123 Main St   Springfield      PA   84679
    45    8/18/2009  7:20:27 PM    123 Main St   Springfield      PA   84679
    45    8/20/2009  9:01:44 PM   456 Smith Ln   Springfield      PA   84153
    45   10/31/2009 12:29:07 AM

Образец после:

Indexc                    Birth        Address          City   State     Zip
------   ----------------------   ------------   -----------   -----   -----
    45    8/16/2009  4:46:21 AM    123 Main St   Springfield      PA   84679
    45    8/18/2009  7:20:27 PM    123 Main St   Springfield      PA   84679
    45    8/20/2009  9:01:44 PM   456 Smith Ln   Springfield      PA   84153
    45   10/31/2009 12:29:07 AM   456 Smith Ln   Springfield      PA   84153

Ответы [ 3 ]

1 голос
/ 16 декабря 2009

Это работает отлично. Хотя он не очень расширяемый для n-го нового предмета.

UPDATE Landmark lm1
   SET (Address, City, State, Zip) = (
   SELECT Address, City, State, Zip
     FROM Landmark lm2
    WHERE lm2.Indexc = lm1.Indexc
      AND lm2.birth = (
      SELECT MAX(Birth)
        FROM Landmark lm3
       WHERE lm3.Indexc = lm2.Indexc
         AND lm3.birth < (
         SELECT MAX(Birth)
           FROM Landmark lm4
          WHERE lm4.Indexc = lm3.Indexc)))
 WHERE Indexc IN (
   SELECT Indexc
     FROM MapPoint
    WHERE BuildDate IS NULL)
   AND Birth = (
   SELECT MAX(Birth)
     FROM Landmark lm2
    WHERE lm2.Indexc = lm1.Indexc);
1 голос
/ 16 декабря 2009

В этом решении используется аналитическая функция. Код выглядит несколько более избыточным, но его стоимость ниже, по крайней мере, в моей тестовой базе данных, возможно, из-за того, что подзапросов меньше:

update landmark L1 set (address, city, state, zip) =  
    (select address, city, state, zip from 
       (select  last_value(address ignore nulls) over (partition by cindex order by birth) address, 
                last_value(city ignore nulls) over (partition by cindex order by birth) city, 
                last_value(state ignore nulls) over (partition by cindex order by birth) state, 
                last_value(zip ignore nulls) over (partition by cindex order by birth) zip, 
                cindex, birth
          from landmark) L2
      where l1.cindex = l2.cindex and l2.birth = l1.birth)       
where CIndex IN (
 SELECT CIndex
     FROM MapPoint
    WHERE BuildDate IS NULL)
    and address is null

По сути, это говорит о том, что нужно извлечь все строки из таблицы и для каждой строки выбрать значения адреса, если они не равны NULL, или последнее ненулевое значение, если они есть, а затем обновить эти значения для строк, которые необходимо быть обновленным.

0 голосов
/ 15 декабря 2009

Я заменил «index» на «indexc», потому что index не является допустимым именем столбца в oracle. Я думаю, что это делает то, что вы хотите, хотя:

UPDATE Landmark lm1
 SET (Address, City, State, Zip) = (
 SELECT Address, City, State, Zip
   FROM Landmark lm2
  WHERE lm2.Indexc = lm1.Indexc
    AND lm2.birth = (
    SELECT MAX(Birth)
      FROM Landmark lm3
        WHERE lm3.Indexc = lm2.Indexc
        and lm3.birth < lm2.birth))
 WHERE  (indexc,birth) in (
         select indexc, max(birth)
         from   Landmark
         where  Indexc IN (
           SELECT Indexc
           FROM   MapPoint
           WHERE  BuildDate IS NULL));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...