Как создать автоматически увеличивающийся номер ревизии, уникальный для ключа в PGSQL? - PullRequest
2 голосов
/ 26 мая 2009

Если у меня есть следующие таблицы.

PARENT: PARENT_ID serial, DESCRIPTION character varying(50)

CHILD: PARENT_ID integer, CHILD_ID integer, DESCRIPTION character varying(50)

То, что я хотел бы видеть, - это каждая строка в CHILD, имеющая CHILD_ID, который начинается с 1 и увеличивается на 1, уникально для PARENT_ID. Это было бы похоже на номер ревизии. Например ..

PARENT_ID 1, CHILD_ID 1
PARENT_ID 1, CHILD_ID 2
PARENT_ID 1, CHILD_ID 3
PARENT_ID 2, CHILD_ID 1
PARENT_ID 3, CHILD_ID 1
PARENT_ID 3, CHILD_ID 2

Есть ли способ автоматически назначить значение CHILD_ID, например последовательность или ограничение, только с возможностью повторного использования CHILD_ID, который был удален? Единственный способ, которым я могу понять, - это что-то вроде этого SQL.

INSERT INTO child SELECT parent_id, MAX(child_id)+1, 'description' FROM child WHERE parent_id = :PARENT_ID GROUP BY parent_id

Это что-то вроде хака. Я понимаю, что нормализация базы данных предполагает, что у вас не должно быть одного ключа, связанного с другим, но у меня нет этого варианта по некоторым другим причинам. Есть идеи?

РЕДАКТИРОВАТЬ: Название ужасно. Если кто-нибудь из вас с высокими показателями может подумать о том, кто более точен, пожалуйста, не стесняйтесь его менять.

Ответы [ 3 ]

3 голосов
/ 27 мая 2009

Я бы предложил использовать:

CHILD: PARENT_ID integer, CHILD_ID serial, DESCRIPTION character varying(50)

Когда вам нужно получить желаемый результат:

  • Вы можете считать строки на стороне клиента.

  • При выборе строк, где PARENT_ID =? Вы можете использовать временную последовательность.

  • В скором выпуске Postgresql 8.4 вы можете использовать такие функции управления окнами, как это:

    $ create table child (parent_id integer, child_id serial);
    NOTICE:  CREATE TABLE will create implicit sequence "child_child_id_seq" for serial column "child.child_id"
    CREATE TABLE
    
    $ insert into child (parent_id) values (1), (1), (1), (2), (3), (3);
    
    $ select * from child;
     parent_id | child_id 
    -----------+----------
             1 |        1
             1 |        2
             1 |        3
             2 |        4
             3 |        5
             3 |        6
    (6 rows)
    
    $ select parent_id, row_number() over (partition by parent_id order by child_id) from child;
     parent_id | row_number 
    -----------+------
             1 |          1
             1 |          2
             1 |          3
             2 |          1
             3 |          1
             3 |          2
    (6 rows)
    

Он очень быстрый, простой в реализации и очень хорошо масштабируется, так как не будет проблем с параллелизмом.

0 голосов
/ 27 мая 2009

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

BEGIN
-- Get and hold onto parent_id and version values.
SELECT PARENT_ID, VERSION FROM PARENT WHERE PARENT_ID = :PARENT_ID;
-- Use the values to insert into the child table
INSERT INTO CHILD (PARENT_ID, CHILD_ID) VALUES (:PARENT_ID, :VERSION);
-- Update the version using an optimistic lock.
UPDATE PARENT SET VERSION = VERSION + 1 WHERE PARENT_ID = :PARENT_ID AND 
                                              VERSION = :VERSION_ID
-- If no rows are updated rollback the transaction and try again.
END

Это обеспечит строго возрастание дочерних идентификаторов, но не будет повторно использовать значения идентификаторов после удаления. Если вы сможете избежать ограничения на повторное использование старых идентификаторов, это упростит ваше решение (и решение будет более эффективным). Если вам нужно повторно использовать идентификаторы, то у вас есть 2 варианта, во-первых, решение, которое вы указали выше, но при удалении перенумерация всех значений, которые появляются после того, который вы удалили. Другой вариант - иметь какую-то функцию, которая сканирует дочерние идентификаторы по порядку и сравнивает их с набором последовательных чисел и возвращает значение, когда первое не найдено. Оба эти решения являются более сложными и будут медленными, так как вам потребуется снять блокировку строки, чтобы предотвратить одновременные обновления, и либо вставки, либо вставки и удаления повлекут за собой штраф O (n).

0 голосов
/ 26 мая 2009

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

Мое предложение будет выводить это значение по мере необходимости. От чего зависит порядок номера? Если это дата, введенная в систему, то добавьте эту дату в свою таблицу и поместите свой PK вместо parent_id и этой даты, тогда вы можете довольно легко придумать число через SQL или во внешнем интерфейсе, как вам нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...