Чи можна використовувати CURRENT_TIMESTAMP як ПЕРШИЙ КЛЮЧ?


10

Можна CURRENT_TIMESTAMPвикористовувати як PRIMARY KEY?

Чи існує можливість двох або більше різних ВСТУП отримати однакові CURRENT_TIMESTAMP?


3
Я чув про додаток, кодоване з використанням часової позначки як ПК, ще в 1990-х. Через десять років комп'ютери стали швидшими, а часові позначки дублювалися. Це спричинило дуже серйозні проблеми, оскільки функціональність програми була дуже критичною. Також унікальність ПК не була належним чином застосована у всьому додатку.
Віктор Ді Лео

Чи є можливість, щоб два чи більше різних ВСТАВКИ отримали однакові CURRENT_TIMESTAMP? Досить одного запиту вставлено 2 записи для зіткнення. Тож відповідь на предметне питання - «НІ».
Акіна


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 Загалом, дуже хороша практика зробити ваш БД настільки детермінованим, наскільки це можливо. Тобто одна і та ж операція повинна створювати абсолютно однакові зміни в БД . Будь-яка ідентифікатор, що базується на часових позначках, не відповідає цій важливій функції. Що робити, якщо ви хочете щось налагодити чи імітувати? Ви відтворюєте операцію, і той самий об’єкт буде створений з іншим ідентифікатором ... насправді це не важко дотримуватися, і це витрачає багато робочих годин.

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


Навіть якщо ви не використовуєте транзакції, ви фактично використовуєте транзакції (оскільки Postgres не має режиму без транзакцій, він просто має автоматичну передачу). Тож якщо ви робите INSERTкілька рядків, всі вони виходять однаковими current_timestamp. І тоді у вас є тригери ...
Кевін

2
Я чув про додаток, який зламався через те, що 2 хлопці мали одне ім’я та народилися в той же день, а їхні імена матері були однакові. Ой. Якщо це МОЖЕ статися, це відбудеться, рано чи пізно.
Balazs Gunics

@BalazsGunics Helló :-) Це був лише приклад. Наприклад, у реальних сценаріях я вважаю, що ідентифікатор як адреса електронної пошти або вибране ім’я користувача (яке можна зареєструвати, лише якщо воно ще не існує) достатньо. Уряд, як правило, використовує особистий ідентифікаційний номер, наприклад 1 870728 0651. Важливим є те, що прив'язка ідентифікатора до часової позначки або випадкового значення - це погана практика, оскільки це робить БД менш детермінованою.
петерх

@BalazsGunics Окрім того, двоє людей з тим самим іменем матері + ім'ям_ імені + день народження, це призведе до детермінованої помилки. Зіткнення первинного ключа через те, що дві транзакції із вставками відбулися в одній мікросекунді, це все ще не детермінована і дуже важко відтворювана проблема.
peterh

10

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

Замість цього використовуйте UUID на основі часу: uuid_generate_v1mc () .


7

Строго кажучи: Ні. Тому що CURRENT_TIMESTAMPце функція, і лише одна або кілька стовпців таблиці можуть утворювати PRIMARY KEYобмеження.

Якщо ви маєте на увазі створити PRIMARY KEYобмеження на стовпчик зі значенням за замовчуванням CURRENT_TIMESTAMP, то відповідь така: Так, ви можете . Ніщо не заважає тобі це робити, як ніщо не заважає тобі відстрілювати яблука з голови сина. Питання все одно не має сенсу, поки ви не визначите мету. Які дані мають містити стовпці та таблиці? Які правила ви намагаєтесь виконати?

Зазвичай ідея обов'язково стикається з повторюваними помилками ключа, оскільки CURRENT_TIMESTAMPце STABLEфункція, що повертає однакове значення для тієї самої транзакції (час початку транзакції). Кілька INSERT в одній транзакції повинні зіткнутися, як і інші відповіді, які вже проілюстровані. Посібник:

Оскільки ці функції повертають час початку поточної транзакції, їх значення не змінюються під час транзакції. Це вважається особливістю: ціль полягає в тому, щоб дозволити одній транзакції мати послідовне уявлення про "поточний" час, так що кілька модифікацій в межах однієї транзакції мають однакову позначку часу.

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

Якщо ви будуєте таблицю, яка повинна займати не більше одного рядка за мікросекунди і ця умова не зміниться (що - то з ім'ям sensor_reading_per_microsecond), то це може мати сенс. Повторювані рядки повинні викликати помилку порушення дублюючого ключа. Але це екзотичний виняток. І тип даних timestamptz(не timestamp), ймовірно, буде кращим. Подивитися:

Я б все-таки скоріше скористався сурогатним серійним первинним ключем. І додайте UNIQUEобмеження у стовпчик часової позначки. Менше можливих ускладнень, не покладаючись на деталі впровадження RDBMS.


Навіть sensor_reading_per_microsecondможе зіткнутися, якщо ви не можете абсолютно гарантувати, що час кожного читання ідеально синхронізований щодо попереднього; субмікросекундне відхилення (що часто неможливо) порушує схему. Взагалі я все-таки повністю цього уникаю. (Зверніть увагу, як ви вказали, в такому випадку зіткнення в результаті може бути бажаним!)
Гонки легкості в орбіті

@Lightness: Я повністю згоден. Ваш приклад з ненавмисним зміщенням часу після закругленого крихітного відхилення ілюструє ще один застереження.
Erwin Brandstetter
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.