Question

Je suis en train de faire la mise à jour suivante dans Oracle 10gR2:

update
  (select voyage_port_id, voyage_id, arrival_date, port_seq,
    row_number() over (partition by voyage_id order by arrival_date) as new_seq
   from voyage_port) t
set t.port_seq = t.new_seq

Voyage_port_id est la clé primaire, voyage_id est une clé étrangère. J'essaie d'attribuer un numéro de séquence en fonction des dates de chaque voyage.

Cependant, l'échec ci-dessus avec ORA-01732: opération de manipulation de données non juridique sur ce point de vue

Quel est le problème et comment puis-je éviter?

Était-ce utile?

La solution

Puisque vous ne pouvez pas mettre à jour les sous-requêtes avec row_number, vous devrez calculer le numéro de ligne dans la partie set de la mise à jour. Au début, j'essayé ceci:

update voyage_port a
set a.port_seq = (
  select 
    row_number() over (partition by voyage_id order by arrival_date)
  from voyage_port b
  where b.voyage_port_id = a.voyage_port_id
)

Mais cela ne fonctionne pas, parce que le sous-requête sélectionne une seule ligne, puis le row_number() est toujours 1. En utilisant une autre sous-requête permet un résultat significatif:

update voyage_port a
set a.port_seq = (
  select c.rn
  from (
      select 
        voyage_port_id
      , row_number() over (partition by voyage_id 
            order by arrival_date) as rn
      from voyage_port b
   ) c
  where c.voyage_port_id = a.voyage_port_id
)

Il fonctionne, mais plus complexe que je pense à cette tâche.

Autres conseils

Vous pouvez mettre à jour des vues, mais il y a des restrictions et l'une est que la vue ne doit pas contenir des fonctions analytiques. Voir SQL Langage sur UPDATE et la recherche de la première apparition de « analytique ».

Cela fonctionne, à condition aucune visite au voyage plus d'un port le jour même (ou les dates comprennent un élément de temps qui les rend uniques):

update voyage_port vp
set vp.port_seq =
( select count(*)
  from voyage_port vp2
  where vp2.voyage_id = vp.voyage_id
  and vp2.arrival_date <= vp.arrival_date
)

Je pense que cela traite le cas où une visite de voyage à plus de 1 port par jour et il n'y a pas de composante de temps (bien que la séquence des ports visités sur le même jour est alors arbitraire):

update voyage_port vp
set vp.port_seq =
( select count(*)
  from voyage_port vp2
  where vp2.voyage_id = vp.voyage_id
  and (vp2.arrival_date <= vp.arrival_date)
  or (   vp2.arrival_date = vp.arrival_date 
     and vp2.voyage_port_id <= vp.voyage_port_id
     )
)

Ne pensez pas que vous pouvez mettre à jour une table dérivée, je réécris comme:

update voyage_port
set port_seq = t.new_seq
from
voyage_port p
inner join
  (select voyage_port_id, voyage_id, arrival_date, port_seq,
   row_number() over (partition by voyage_id order by arrival_date) as new_seq
   from voyage_port) t
on p.voyage_port_id = t.voyage_port_id

Le premier jeton après la mise à jour doit être le nom de la table à mettre à jour, vos colonnes à jour. Je ne sais pas ce que vous essayez d'atteindre avec l'instruction select où il est, mais vous pouvez » mise à jour le jeu de résultats de la sélection légalement.
Une version de SQL, deviner ce que vous avez à l'esprit, pourrait ressembler à ...

update voyage_port t
set t.port_seq = (<select statement that generates new value of port_seq>)

NOTE: pour utiliser une instruction select pour définir une valeur comme celui-ci, vous devez vous assurer que 1 ligne sera retournée à partir de la sélection

EDIT: déclaration modifiée ci-dessus pour refléter ce que je tentais d'expliquer. La question a été répondu très bien par Andomar ci-dessus

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top