Postgres 9.2 PL/pgSQL простое обновление в цикле

У меня есть следующая таблица:

+----+----------+----------+
| id | trail_id | position |
+----+----------+----------+
| 11 | 16       | NULL     |
| 12 | 121      | NULL     |
| 15 | 121      | NULL     |
| 19 | 42       | NULL     |
| 20 | 42       | NULL     |
| 21 | 42       | NULL     |
+----+----------+----------+

И я ищу простой способ обновления position с добавочными целыми числами (для каждого родителя). Итак, после того, как я закончил, таблица должна выглядеть так:

+----+-----------+----------+
| id | trail_id  | position |
+----+-----------+----------+
| 11 | 16        | 1        |
| 12 | 121       | 1        |
| 15 | 121       | 2        |
| 19 | 42        | 1        |
| 20 | 42        | 2        |
| 21 | 42        | 3        |
+----+-----------+----------+

То, что мне кажется нужным, это функция, которая перебирает все строки для данного trail, имеет простой инкрементирующий индекс и обновляет столбец position. Однако я новичок pgSQL, поэтому буду рад услышать, что есть более простые способы сделать это.

Решение, которое я пытаюсь сделать прямо сейчас, выглядит следующим образом:

CREATE FUNCTION fill_positions(tid integer) RETURNS integer AS $$
DECLARE
    pht RECORD;
    i INTEGER := 0;
BEGIN
    FOR pht IN SELECT * FROM photos WHERE photos.trail_id = tid LOOP
        i := i + 1;
        UPDATE photos SET position = i WHERE id = pht.id;
    END LOOP;
    RETURN i;
END;
$$ LANGUAGE plpgsql;

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

Ответ 1

Для этого вам не нужна сохраненная функция. Вы можете сделать это с помощью одного оператора.

with new_numbers as (
   select id,  
          trail_id,
          row_number() over (partition by trail_id order by id) as position
   from photos
)
update photos
  set position = nn.position
from new_numbers nn
where nn.id = photos.id;