Можно ли использовать CURRENT_TIMESTAMP в качестве ПЕРВИЧНОГО КЛЮЧА?

10

Может CURRENT_TIMESTAMPбыть использован как PRIMARY KEY?

Есть ли вероятность, что два или более разных INSERT, получат одинаковые CURRENT_TIMESTAMP?

Джон Пушкин
источник
3
Я слышал о приложении, которое было закодировано с использованием временной метки в качестве ПК, еще в 1990-х годах. Десять лет спустя компьютеры стали быстрее, а временные метки были продублированы. Это вызвало очень серьезные проблемы, так как функциональность приложения была очень критичной. Кроме того, уникальность PK не была должным образом применена во всем приложении.
Виктор Ди Лев
Есть ли вероятность, что два или более разных INSERT получат одинаковый CURRENT_TIMESTAMP? Для одного запроса достаточно вставить 2 записи для коллизии. Таким образом, ответ на предметный вопрос - «НЕТ».
Акина
5
32 °
лжецов, которым
3
Мне интересно, почему ты этого хочешь?
Нанн
@Nanne Я подозреваю, что это: MySQL очень хорошо обрабатывает автоматически увеличивающиеся целочисленные идентификаторы (просто атрибут auto_increment для поля). PostgreSQL не имеет, он имеет серийный тип, который гораздо менее красив.
Петер - Восстановить Монику

Ответы:

18

Согласно документации , точность CURRENT_TIMESTAMPсоставляет микросекунды. Таким образом, вероятность столкновения низкая, но возможная.

Теперь представьте ошибку, которая случается очень редко и вызывает ошибки базы данных. Насколько сложно это отладить? Это гораздо хуже, чем ошибка, которая, по крайней мере, является детерминированной.

Более широкий контекст: вы, вероятно, хотите избежать этих маленьких нюансов с последовательностями, что особенно раздражает, если вы привыкли к MySQL.

Кроме того, если вы используете транзакции (большинство веб-платформ, особенно Java, делают!), То временные метки внутри транзакции будут одинаковыми! Демонстрация:

postgres=# begin;
BEGIN
postgres=# select current_timestamp;
       current_timestamp       
-------------------------------
 2018-08-06 02:41:42.472163+02
(1 Zeile)

postgres=# select current_timestamp;
       current_timestamp       
-------------------------------
 2018-08-06 02:41:42.472163+02
(1 Zeile)

Увидимся? Два выбора, точно такой же результат. Я не печатаю так быстро. ;-)

-

Если вы хотите легко идентификаторы, избегая использования последовательностей, то сгенерируйте некоторое значение хеш-функции из реальных идентификаторов записей. Например, если в вашей базе данных есть люди, и вы знаете, что их дата рождения, девичья фамилия и настоящее имя матери однозначно идентифицируют их, тогда используйте

md5(mother_name || '-' || given_name || '-' birthday);

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

Ps В общем, это очень хорошая практика, чтобы сделать вашу БД настолько детерминированной, насколько это возможно. Т.е. одна и та же операция должна создать точно такое же изменение в БД . Любой ID на основе отметки времени не выполняет эту важную функцию. Что если вы хотите что-то отладить или смоделировать? Вы воспроизводите операцию, и один и тот же объект будет создан с другим идентификатором ... за ним действительно нетрудно следить, и это экономит много рабочих часов.

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

Петер - Восстановить Монику
источник
Даже если вы не используете транзакции, вы на самом деле используете транзакции (потому что у Postgres нет режима без транзакций, он просто имеет автоматическую фиксацию). Так что если вы делаете INSERTнесколько строк, они все получают одинаково current_timestamp. И тогда у вас есть триггеры ...
Кевин
2
Я слышал о приложении, которое сломалось, потому что 2 парня имели одинаковое имя и родились в один и тот же день, а имена их матерей были идентичны. Уч. Если это МОЖЕТ произойти, это произойдет рано или поздно.
Balazs Gunics
@BalazsGunics Helló :-) Это был просто пример. Например, в реальных сценариях я думаю, что id в качестве адреса электронной почты или выбранного имени пользователя (который может быть зарегистрирован, только если он еще не существует) достаточно. Правительство стремится использовать какой-то личный идентификационный номер, например, 1 870728 0651. Важно то, что привязка идентификатора к временной метке или к случайному значению является плохой практикой, поскольку она делает БД менее детерминированной.
Петер - Восстановить Монику
@BalazsGunics Кроме того, два человека с одинаковым именем матери + имя_доставки + день рождения могут вызвать детерминированную ошибку. Столкновение первичного ключа из-за того, что две транзакции, имеющие вставки, произошло за одну микросекунду, это все еще недетерминированная и очень трудно воспроизводимая проблема.
Петер - Восстановить Монику
10

Не совсем, потому что CURRENT_TIMESTAMP может предоставить два одинаковых значения для двух последующих INSERT (или одного INSERT с несколькими строками).

Вместо этого используйте основанный на времени UUID: uuid_generate_v1mc () .

Линас
источник
7

Строго говоря: Нет. Потому CURRENT_TIMESTAMPчто это функция, и только один или несколько столбцов таблицы могут образовывать PRIMARY KEYограничение.

Если вы хотите создать PRIMARY KEYограничение для столбца со значением по умолчанию CURRENT_TIMESTAMP, то ответ: да, вы можете . Ничто не мешает вам делать это, как ничто не мешает вам стрелять яблоками из головы вашего сына. Вопрос все еще не будет иметь смысла, пока вы не определите его цель. Какие данные должны содержать столбец и таблица? Какие правила вы пытаетесь реализовать?

Как правило, эта идея связана нарваться дублирующие ключевыми ошибками , так как CURRENT_TIMESTAMPэто STABLEфункция , возвращающая то же значение для той же самой транзакции (время начала транзакции). Несколько INSERT в одной транзакции обязательно столкнутся - как и другие ответы, уже показанные. Руководство:

Поскольку эти функции возвращают время начала текущей транзакции, их значения не изменяются во время транзакции. Это считается особенностью: цель состоит в том, чтобы позволить одной транзакции иметь согласованное представление о «текущем» времени, так чтобы несколько модификаций в одной транзакции имели одинаковую отметку времени.

Метки времени Postgres реализованы в виде 8-байтовых целых чисел, представляющих до 6 дробных цифр (микросекундное разрешение).

Если вы создаете таблицу, которая должна содержать не более одной строки в микросекунду, и это условие не изменится (что-то с именем sensor_reading_per_microsecond), то это может иметь смысл. Дублирующиеся строки должны вызывать ошибку нарушения дублирующего ключа. Это экзотическое исключение. И тип данных timestamptz(не timestamp), вероятно, будет предпочтительнее. Увидеть:

Я все еще предпочел бы использовать суррогатный серийный первичный ключ вместо этого. И добавьте UNIQUEограничение на столбец отметки времени. Меньше возможных осложнений, не опираясь на детали реализации СУБД.

Эрвин Брандштеттер
источник
Даже sensor_reading_per_microsecondможет столкнуться, если вы не можете абсолютно гарантировать, что синхронизация каждого чтения идеально синхронизирована относительно предыдущего; отклонение на микросекунду (что часто не невозможно) нарушает схему. В общем, я бы до сих пор полностью этого избегал. (Имейте в виду, как вы указали, в таком случае результирующее столкновение может быть желательным!)
Гонки
@Lightness: я полностью согласен. Ваш пример с непреднамеренным временным сдвигом после округленного крошечного отклонения иллюстрирует еще одну оговорку.
Эрвин Брандштеттер,