Czy CURRENT_TIMESTAMP może być używany jako KLUCZ PODSTAWOWY?

10

Może CURRENT_TIMESTAMPbyć używany jako PRIMARY KEY?

Czy istnieje możliwość, że dwa lub więcej różnych WSTAWEK dostanie to samo CURRENT_TIMESTAMP?

John Puskin
źródło
3
Słyszałem o aplikacji, która została zakodowana przy użyciu znacznika czasu jako PK, w latach 90. Dziesięć lat później komputery PC stały się szybsze, a znaczniki czasu zostały zduplikowane. Powodowało to bardzo poważne problemy, ponieważ funkcjonalność aplikacji była bardzo krytyczna. Ponadto unikalność PK nie była poprawnie egzekwowana w całej aplikacji.
Victor Di Leo,
Czy istnieje możliwość, że dwa lub więcej różnych WSTAWEK otrzyma ten sam CURRENT_TIMESTAMP? Wystarczy jedno zapytanie wstawiło 2 rekordy do kolizji. Tak więc odpowiedź na pytanie tematyczne brzmi „NIE”.
Akina,
3
Jestem ciekawy, dlaczego tego chcesz?
Nanne
@Nanne Podejrzewam, że: MySQL ma bardzo dobrą obsługę automatycznie zwiększanych identyfikatorów liczb całkowitych (po prostu atrybut auto_increment pola). PostgreSQL nie ma, ma typ seryjny, który jest znacznie mniej piękny.
peterh - Przywróć Monikę

Odpowiedzi:

18

Zgodnie z dokumentacją dokładność CURRENT_TIMESTAMPwynosi mikrosekund. Zatem prawdopodobieństwo kolizji jest niskie, ale możliwe.

Teraz wyobraź sobie błąd, który zdarza się bardzo rzadko i powoduje błędy bazy danych. Jak trudno jest to debugować? Jest to znacznie gorszy błąd niż ten, który jest co najmniej deterministyczny.

Szerszy kontekst: prawdopodobnie chcesz uniknąć tych małych niuansów w sekwencjach, co jest szczególnie denerwujące, jeśli jesteś przyzwyczajony do MySQL.

Ponadto, jeśli używasz transakcji (większość frameworków internetowych, szczególnie Java, zrób!), Znaczniki czasu będą takie same w transakcji! Demonstracja:

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)

Do zobaczenia? Dwa selekcje, dokładnie taki sam wynik. Nie piszę tak szybko. ;-)

-

Jeśli chcesz łatwo identyfikować, unikając użycia sekwencji, wygeneruj pewną wartość skrótu na podstawie rzeczywistych identyfikatorów rekordów. Na przykład, jeśli w Twojej bazie danych znajdują się ludzie i wiesz, że ich data urodzenia, nazwisko panieńskie matki i prawdziwe nazwisko jednoznacznie ich identyfikują, użyj

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

jako identyfikator Poza tym możesz użyć CreationDatekolumny po tym, co indeksujesz tabelę, ale nie jest to klucz (który jest id).

Ps Ogólnie rzecz biorąc, bardzo dobrą praktyką jest, aby twoja DB była tak deterministyczna, jak to możliwe. To znaczy ta sama operacja powinna stworzyć dokładnie taką samą zmianę w DB . Identyfikator oparty na znacznikach czasu nie spełnia tej ważnej funkcji. Co jeśli chcesz coś debugować lub symulować? Ponownie odtworzysz operację, a ten sam obiekt zostanie utworzony z innym identyfikatorem ... naprawdę nie jest trudny do naśladowania i oszczędza wiele godzin pracy.

Ps2 Każdy, kto sprawdzi kod w przyszłości, nie będzie miał najlepszej opinii na podstawie identyfikatorów wygenerowanych znacznikami czasu z powyższych powodów.

peterh - Przywróć Monikę
źródło
Nawet jeśli nie korzystasz z transakcji, w rzeczywistości korzystasz z transakcji (ponieważ Postgres nie ma trybu braku transakcji, po prostu ma automatyczne zatwierdzanie). Więc jeśli wykonasz INSERTwiele wierszy, wszystkie otrzymają to samo current_timestamp. A potem masz wyzwalacze ...
Kevin
2
Słyszałem o aplikacji, która zepsuła się, ponieważ dwóch facetów miało to samo imię i urodziło się tego samego dnia, a ich matki były identyczne. Auć. Jeśli to się stanie, stanie się to wcześniej czy później.
Balazs Gunics,
@BalazsGunics Helló :-) To był tylko przykład. Na przykład w prawdziwych scenariuszach uważam, że identyfikator jako adres e-mail lub nazwa użytkownika (którą można zarejestrować tylko wtedy, gdy jeszcze nie istnieje) jest wystarczająca. Rząd zwykle używa jakiegoś osobistego numeru identyfikacyjnego, takiego jak 1 870728 0651. Ważne jest, aby przypisanie identyfikatora znacznikowi czasu lub wartości losowej było złą praktyką, ponieważ powoduje, że DB jest mniej deterministyczny.
peterh - Przywróć Monikę
@BalazsGunics Poza tym dwie osoby o tej samej nazwie matki + podanej nazwie + urodzinach spowodowałyby nadal błąd deterministyczny. Kolizja klucza podstawowego z powodu tego, że dwie transakcje mające wstawki miały miejsce w tej samej mikrosekundie, jest to nadal problem niedeterministyczny i bardzo trudny do odtworzenia.
peterh - Przywróć Monikę
10

Nie do końca, ponieważ CURRENT_TIMESTAMP może podać dwie identyczne wartości dla dwóch kolejnych WSTAWEK (lub jednego WSTAWU z wieloma wierszami).

Zamiast tego użyj UUID opartego na czasie: uuid_generate_v1mc () .

Linas
źródło
7

Ściśle mówiąc: Nie. Ponieważ CURRENT_TIMESTAMPjest funkcją i tylko jedna lub więcej kolumn tabeli może tworzyć PRIMARY KEYograniczenie.

Jeśli masz na myśli, aby stworzyć PRIMARY KEYpresję na kolumnie z wartości domyślnej CURRENT_TIMESTAMP, to odpowiedź brzmi: Tak, to możliwe . Nic nie powstrzymuje cię przed zrobieniem tego, tak jak nic nie powstrzymuje cię przed wystrzeleniem jabłek z głowy syna. Pytanie wciąż nie miałoby sensu, dopóki nie zdefiniujesz jego celu. Jakie dane mają przechowywać kolumna i tabela? Jakie zasady próbujesz wdrożyć?

Zazwyczaj pomysł jest związany napotkasz duplikatów kluczowych błędów, ponieważ CURRENT_TIMESTAMPjest to STABLEfunkcja powrocie taką samą wartość dla tej samej transakcji (czas rozpoczęcia transakcji). Wiele WSTAWEK w tej samej transakcji musi się zderzyć - podobnie jak inne zilustrowane już odpowiedzi. Instrukcja:

Ponieważ funkcje te zwracają czas rozpoczęcia bieżącej transakcji, ich wartości nie zmieniają się podczas transakcji. Jest to uważane za cechę: celem jest umożliwienie jednej transakcji spójnego pojęcia „bieżącego” czasu, tak aby wielokrotne modyfikacje w ramach tej samej transakcji były opatrzone tym samym znacznikiem czasu.

Znaczniki czasu Postgres są implementowane jako 8-bajtowe liczby całkowite reprezentujące do 6 cyfr ułamkowych (rozdzielczość mikrosekundowa).

Jeśli budujesz tabelę, która ma pomieścić nie więcej niż jeden wiersz na mikrosekundy, a warunek ten nie ulegnie zmianie (coś o nazwie sensor_reading_per_microsecond), wówczas może to mieć sens. Zduplikowane wiersze powinny wywoływać błąd naruszenia klucza duplikatu. To jednak egzotyczny wyjątek. I typ danych timestamptz(nie timestamp) byłby prawdopodobnie preferowany. Widzieć:

Wolałbym zamiast tego użyć zastępczego klucza podstawowego szeregowego. I dodaj UNIQUEograniczenie do kolumny znacznika czasu. Mniej możliwych komplikacji, nie polegających na szczegółach implementacji RDBMS.

Erwin Brandstetter
źródło
sensor_reading_per_microsecondMoże nawet kolidować, jeśli nie można absolutnie zagwarantować, że czas każdego odczytu jest idealnie zsynchronizowany w stosunku do poprzedniego; odchylenie poniżej mikrosekundy (co często nie jest niemożliwe) psuje schemat. Generalnie nadal całkowicie tego unikam. (Pamiętaj, jak wskazałeś w takim przypadku, pożądana kolizja może być pożądana!)
Lekkość ściga się na orbicie w dniu
@Lightness: Zgadzam się. Twój przykład z niezamierzoną zmianą czasu po zaokrągleniu małego odchylenia ilustruje kolejne zastrzeżenie.
Erwin Brandstetter,