существует ли способ постараться не называть nextval (), если вставка перестала работать в PostgreSQL?

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

  CREATE TABLE users (
    id      INTEGER PRIMARY KEY DEFAULT nextval('groups_id_seq'::regclass),
    name    VARCHAR(255) UNIQUE NOT NULL
  );

  INSERT users (name) VALUES ('foo');
  INSERT users (name) VALUES ('foo');
  INSERT users (name) VALUES ('bar');

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

Существует ли способ сказать PostgreSQL выбирать следующее значение, только если другие ограничения встречены, или я должен проверить сначала ВЫБОР использования, если имя не является дубликатом? Это все еще не гарантировало бы отсутствия разрывов, но по крайней мере оно сократит их количество к редким случаям, когда будет другой процесс, пытающийся вставлять то же имя одновременно

6
задан szabgab 10 January 2010 в 09:14
поделиться

3 ответа

Я так не думаю: базовая особенность последовательностей заключается в том, что возможны пробелы (подумайте о двух одновременных транзакциях, с одним выполнением отката). Вы должны игнорировать пробелы. Почему они проблема в вашем случае?

12
ответ дан 8 December 2019 в 05:21
поделиться

Если вам нужны безразрывные последовательности - есть способы сделать это, но это не тривиально, и определенно намного медленнее.

Также - если вас беспокоит "использование слишком большого количества id" - просто определите id как bigserial.

6
ответ дан 8 December 2019 в 05:21
поделиться

Рекурсивная функция имеет производительность O (n ^ 2), поскольку копирует оставшееся содержимое последовательности каждый раз при обнаружении совпадения. Это медленнее, чем итеративное решение O (n), и излишне.

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

def countSubStringMatchRecursive(target, key, start_index = 0):
    index = target.find(key, start_index)
    if index >= 0:
        return countSubStringMatchRecursive(target, key, index + len(key)) + 1
    return 0

target_string = 'an apple and a banana'
key = 'an'
count = countSubStringMatchRecursive(target_string,  key)
print "Number of instances of %r in %r is %d" % (key, target_string, count)

Output:

Number of instances of 'an' in 'an apple and a banana' is 4

Update: Если вы действительно хотите использовать функцию поиска модуля последовательности, вы можете сделать это, просто изменив одну строку:

index = find(target, key, start_index)
-121--2433159-

Как насчет этого?

def count_it(target, key):
    index = target.find(key)
    if index >= 0:
        return 1 + count_it(target[index+len(key):], key)
    else:
        return 0


print count_it("aaa bbb aaa ccc aaa", "aaa")

Вывод:

3
-121--2433165-

Возможно, хотя и громоздко, сделать это. Как говорится в bortzmeyer , опасно полагаться на значения из последовательностей, которые являются смежными, поэтому лучше всего оставить вещи такими, какие есть, если можно.

Если вы не можете:

Каждый доступ к таблице, который может привести к тому, что строка будет иметь определенное имя (то есть каждое INSERT к этой таблице, и если вы разрешаете это (хотя это плохо) каждое UPDATE , которое может изменить поле имя ), должны сделать это внутри транзакции, которая блокирует первым . Простейший и наименее эффективный вариант заключается в простой блокировке всей таблицы с помощью LOCK USERS IN EXCLUSIVE МЕТОД (добавление последних 3 слов позволяет осуществлять одновременное чтение другими процессами, что является безопасным).

Однако это очень грубая блокировка, которая замедлит производительность при наличии множества параллельных модификаций для пользователей ; лучшим вариантом является блокировка одной соответствующей строки в другой таблице, которая уже должна существовать. Эта строка может быть заблокирована с помощью команды SELECT... ДЛЯ ОБНОВЛЕНИЯ . Это имеет смысл только при работе с «дочерней» таблицей, которая имеет FK-зависимость от другой «родительской» таблицы.

Например, пока представьте, что мы действительно пытаемся безопасно создать новые заказы для клиента , и что эти заказы каким-то образом идентифицируют «имена». (Я знаю, плохой пример...) заказы зависят от клиентов . Чтобы предотвратить создание двух заказов с одинаковым именем для данного клиента, можно сделать следующее:

BEGIN;

-- Customer 'jbloggs' must exist for this to work.  
SELECT 1 FROM customers
WHERE id = 'jbloggs'
FOR UPDATE

-- Provided every attempt to create an order performs the above step first,
-- at this point, we will have exclusive access to all orders for jbloggs.
SELECT 1 FROM orders
WHERE id = 'jbloggs'
AND order_name = 'foo'

-- Determine if the preceding query returned a row or not.
-- If it did not:
INSERT orders (id, name) VALUES ('jbloggs', 'foo');

-- Regardless, end the transaction:
END;

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

Любая схема блокировки будет работать; важно то, что любой, кто пытается создать строку с тем же именем, должен попытаться заблокировать тот же объект .

5
ответ дан 8 December 2019 в 05:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: