PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Sekwencje bez przerw w PostgreSQL

Sekwencje mają luki umożliwiające jednoczesne wstawianie. Próba uniknięcia luk lub ponownego wykorzystania usuniętych identyfikatorów powoduje straszne problemy z wydajnością. Zobacz FAQ wiki PostgreSQL.

PostgreSQL SEQUENCE s służą do przydzielania identyfikatorów. Te tylko stale rosną i są zwolnione ze zwykłych reguł wycofywania transakcji, aby umożliwić wielu transakcjom pobieranie nowych identyfikatorów w tym samym czasie. Oznacza to, że jeśli transakcja zostanie wycofana, te identyfikatory zostaną „wyrzucone”; nie jest przechowywana lista „darmowych” identyfikatorów, tylko bieżący licznik identyfikatorów. Sekwencje są również zwykle zwiększane, jeśli baza danych jest nieczysto zamykana.

Klucze syntetyczne (identyfikatory) są bez znaczenia W każdym razie. Ich kolejność nie jest znacząca, jedyną ich cechą znaczenia jest wyjątkowość. Nie możesz w znaczący sposób zmierzyć, jak „daleko od siebie” znajdują się dwa identyfikatory, ani nie możesz sensownie powiedzieć, czy jeden jest większy czy mniejszy od drugiego. Wszystko, co możesz zrobić, to powiedzieć „równe” lub „nie równe”. Wszystko inne jest niebezpieczne. Nie powinieneś przejmować się lukami.

Jeśli potrzebujesz sekwencji bez przerw, która ponownie wykorzystuje usunięte identyfikatory, możesz je mieć, po prostu musisz zrezygnować z ogromnej wydajności - w szczególności nie możesz mieć żadnej współbieżności na INSERT s w ogóle, ponieważ musisz przeskanować tabelę w poszukiwaniu najniższego wolnego identyfikatora, blokując tabelę do zapisu, aby żadna inna transakcja nie mogła zażądać tego samego identyfikatora. Spróbuj wyszukać „sekwencję bez przerw postgresql”.

Najprostszym podejściem jest użycie tabeli liczników i funkcji, która pobiera następny identyfikator. Oto uogólniona wersja, która używa tabeli liczników do generowania kolejnych identyfikatorów bez przerw; jednak nie wykorzystuje ponownie identyfikatorów.

CREATE TABLE thetable_id_counter ( last_id integer not null );
INSERT INTO thetable_id_counter VALUES (0);

CREATE OR REPLACE FUNCTION get_next_id(countertable regclass, countercolumn text) RETURNS integer AS $$
DECLARE
    next_value integer;
BEGIN
    EXECUTE format('UPDATE %s SET %I = %I + 1 RETURNING %I', countertable, countercolumn, countercolumn, countercolumn) INTO next_value;
    RETURN next_value;
END;
$$ LANGUAGE plpgsql;

COMMENT ON get_next_id(countername regclass) IS 'Increment and return value from integer column $2 in table $1';

Użycie:

INSERT INTO dummy(id, blah) 
VALUES ( get_next_id('thetable_id_counter','last_id'), 42 );

Zwróć uwagę, że gdy jedna otwarta transakcja uzyskała identyfikator, wszystkie inne transakcje, które próbują wywołać get_next_id zablokuje się do momentu zatwierdzenia lub wycofania pierwszej transakcji. Jest to nieuniknione i w przypadku identyfikatorów bez przerw i jest zgodne z projektem.

Jeśli chcesz przechowywać wiele liczników do różnych celów w tabeli, po prostu dodaj parametr do powyższej funkcji, dodaj kolumnę do tabeli liczników i dodaj WHERE klauzula do UPDATE który dopasowuje parametr do dodanej kolumny. W ten sposób możesz mieć wiele niezależnie blokowanych rzędów liczników. Nie po prostu dodaj dodatkowe kolumny dla nowych liczników.

Ta funkcja nie wykorzystuje ponownie usuniętych identyfikatorów, po prostu zapobiega wprowadzaniu luk.

Aby ponownie użyć identyfikatorów, radzę... nie używać ponownie identyfikatorów.

Jeśli naprawdę musisz, możesz to zrobić, dodając ON INSERT OR UPDATE OR DELETE wyzwalacz w tabeli zainteresowania, który dodaje usunięte identyfikatory do tabeli pobocznej free-list i usuwa je z tabeli free-list, gdy są INSERT wyd. Traktuj UPDATE jako DELETE po którym następuje INSERT . Teraz zmodyfikuj powyższą funkcję generowania identyfikatorów, tak aby wykonywała SELECT free_id INTO next_value FROM free_ids FOR UPDATE LIMIT 1 a jeśli zostanie znaleziony, DELETE s ten wiersz. IF NOT FOUND pobiera nowy identyfikator z tabeli generatora w normalny sposób. Oto nieprzetestowane rozszerzenie poprzedniej funkcji do obsługi ponownego użycia:

CREATE OR REPLACE FUNCTION get_next_id_reuse(countertable regclass, countercolumn text, freelisttable regclass, freelistcolumn text) RETURNS integer AS $$
DECLARE
    next_value integer;
BEGIN
    EXECUTE format('SELECT %I FROM %s FOR UPDATE LIMIT 1', freelistcolumn, freelisttable) INTO next_value;
    IF next_value IS NOT NULL THEN
        EXECUTE format('DELETE FROM %s WHERE %I = %L', freelisttable, freelistcolumn, next_value);
    ELSE
        EXECUTE format('UPDATE %s SET %I = %I + 1 RETURNING %I', countertable, countercolumn, countercolumn, countercolumn) INTO next_value;
    END IF;
    RETURN next_value;
END;
$$ LANGUAGE plpgsql;



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Odejmij lata od daty w PostgreSQL

  2. Literały łańcuchowe i znaki ucieczki w postgresql

  3. Zrozumienie typów i funkcji dat PostgreSQL (przykłady)

  4. Jak zainstalować PgBackRest

  5. Jak używać operatora % z rozszerzenia pg_trgm?