Korzystanie z PostgreSQL v9.1. Mam następujące tabele:
CREATE TABLE foo
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
type VARCHAR(60) NOT NULL UNIQUE
);
CREATE TABLE bar
(
id BIGSERIAL NOT NULL UNIQUE PRIMARY KEY,
description VARCHAR(40) NOT NULL UNIQUE,
foo_id BIGINT NOT NULL REFERENCES foo ON DELETE RESTRICT
);
Powiedzmy, że pierwszy stół foo
jest wypełniony w następujący sposób:
INSERT INTO foo (type) VALUES
( 'red' ),
( 'green' ),
( 'blue' );
Czy jest jakiś sposób na bar
łatwe wstawienie wierszy , odwołując się do foo
tabeli? A może muszę to zrobić w dwóch krokach, najpierw szukając foo
pożądanego typu, a następnie wstawiając nowy wiersz bar
?
Oto przykład pseudo-kodu pokazującego, co miałem nadzieję zrobić:
INSERT INTO bar (description, foo_id) VALUES
( 'testing', SELECT id from foo WHERE type='blue' ),
( 'another row', SELECT id from foo WHERE type='red' );
postgresql
foreign-key
postgresql-9.1
insert
Stéphane
źródło
źródło
Zwykły WSTAW
Zastosowanie
LEFT [OUTER] JOIN
zamiast[INNER] JOIN
oznacza, że wiersze zval
nie są usuwane, gdy nie znaleziono dopasowaniafoo
. Zamiast tegoNULL
wpisuje się dlafoo_id
.VALUES
Wyraz w podzapytaniu działa tak samo jak @ ypercube za CTE. Typowe wyrażenia tabel oferują dodatkowe funkcje i są łatwiejsze do odczytania w dużych zapytaniach, ale stanowią również barierę optymalizacji. Zatem podkwerendy są zwykle nieco szybsze, gdy żadne z powyższych nie jest potrzebne.id
ponieważ nazwa kolumny jest szeroko rozpowszechnionym anty-wzorcem. Powinno byćfoo_id
i /bar_id
lub cokolwiek opisowego. Dołączając do kilku tabel, otrzymujesz wiele kolumn o nazwachid
...Rozważ zwykły
text
lubvarchar
zamiastvarchar(n)
. Jeśli naprawdę musisz nałożyć ograniczenie długości, dodajCHECK
ograniczenie:Może być konieczne dodanie rzutowania typu jawnego. Ponieważ
VALUES
wyrażenie nie jest bezpośrednio dołączone do tabeli (jak wINSERT ... VALUES ...
), typów nie można wyprowadzić, a domyślne typy danych są używane bez wyraźnej deklaracji typu, co może nie działać we wszystkich przypadkach. Wystarczy zrobić to w pierwszym rzędzie, reszta będzie zgodna.WSTAW brakujące wiersze FK w tym samym czasie
Jeśli chcesz tworzyć nieistniejące wpisy w
foo
locie, w pojedynczej instrukcji SQL , CTE są instrumentalne:Zwróć uwagę na dwa nowe atrapy wierszy do wstawienia. Oba są fioletowe , które jeszcze nie istnieją
foo
. Dwa wiersze ilustrujące potrzebęDISTINCT
w pierwszymINSERT
stwierdzeniu.Wyjaśnienie krok po kroku
1. CTE
sel
zapewnia wiele wierszy danych wejściowych. Podzapytanieval
zVALUES
wyrażeniem można zastąpić tabelą lub podzapytaniem jako źródłem. Natychmiast,LEFT JOIN
abyfoo
dołączyćfoo_id
do wcześniej istniejącychtype
wierszy. Wszystkie inne wiersze dostają się wfoo_id IS NULL
ten sposób.2. CTE
ins
wstawienie różnych nowych rodzajów (foo_id IS NULL
) dofoo
i zwraca nowo wytworzonyfoo_id
- wraz ztype
dołączyć z powrotem wstawić wierszy.Ostateczny
INSERT
element zewnętrzny może teraz wstawić foo.id dla każdego wiersza: albo typ istniał wcześniej, albo został wstawiony w kroku 2.Ściśle mówiąc, obie wstawki występują „równolegle”, ale ponieważ jest to pojedyncze stwierdzenie, domyślne
FOREIGN KEY
ograniczenia nie będą narzekać. Integralność referencyjna jest domyślnie wymuszana na końcu instrukcji.Fiddle SQL dla Postgres 9.3. (Działa tak samo w 9.1.)
Jeśli uruchomisz jednocześnie wiele z tych zapytań, istnieje mały warunek wyścigu . Przeczytaj więcej w powiązanych pytaniach tutaj i tutaj i tutaj . Naprawdę dzieje się to tylko pod dużym obciążeniem równoległym, jeśli w ogóle. W porównaniu z rozwiązaniami buforującymi, takimi jak reklamowane w innej odpowiedzi, szansa jest bardzo mała .
Funkcja wielokrotnego użytku
Do wielokrotnego użytku stworzyłbym funkcję SQL, która przyjmuje tablicę rekordów jako parametr i używa
unnest(param)
zamiastVALUES
wyrażenia.Lub, jeśli składnia tablic rekordów jest dla Ciebie zbyt nieporządna, użyj parametru rozdzielanego przecinkami
_param
. Na przykład formularz:Następnie użyj tego, aby zastąpić
VALUES
wyrażenie w powyższej instrukcji:Funkcja z UPSERT w Postgres 9.5
Utwórz niestandardowy typ wiersza do przekazywania parametrów. Bez tego moglibyśmy się obejść, ale jest to prostsze:
Funkcjonować:
Połączenie:
Szybki i niezawodny w środowiskach z jednoczesnymi transakcjami.
Oprócz powyższych zapytań ten ...
... dotyczy
SELECT
lub jestINSERT
włączonefoo
: Wszelkie,type
które nie istnieją jeszcze w tabeli FK, są wstawione. Zakładając, że większość typów istnieje wcześniej. Aby mieć absolutną pewność i wykluczyć warunki wyścigu, istniejące wiersze, których potrzebujemy, są zablokowane (aby równoczesne transakcje nie mogły przeszkadzać). Jeśli jest to zbyt paranoiczne dla twojej sprawy, możesz wymienić:z
... dotyczy
INSERT
lubUPDATE
(prawda „UPSERT”)bar
: jeślidescription
już istnieje,type
jest aktualizowana:Ale tylko jeśli
type
faktycznie się zmieni:... przekazuje wartości wraz ze znanymi typami wierszy z
VARIADIC
parametrem. Uwaga domyślnie maksymalnie 100 parametrów! Porównać:Istnieje wiele innych sposobów przekazywania wielu wierszy ...
Związane z:
źródło
INSERT missing FK rows at the same time
przykładzie, czy umieszczenie tego w Transakcji zmniejszyłoby ryzyko warunków wyścigu w SQL Server?SELECT
wewnątrzWITH
klauzuli). Źródło: dokumentacja MS.INSERT ... RETURNING \gset
in,psql
a następnie użyć zwróconych wartości jako psql:'variables'
, ale działa to tylko dla wstawek jednorzędowych.Lookup Zasadniczo potrzebujesz identyfikatorów foo, aby wstawić je do paska.
Nie dotyczy postgresu, btw. (i nie otagowałeś go w ten sposób) - tak na ogół działa SQL. Brak skrótów tutaj.
Jeśli chodzi o aplikacje, możesz mieć pamięć podręczną elementów foo. Moje tabele często mają do 3 unikalnych pól:
Przykład:
Oczywiście, gdy chcesz powiązać coś z kontem - najpierw technicznie musisz uzyskać identyfikator - ale zarówno identyfikator, jak i kod nigdy się nie zmieniają, gdy już tam są, dodatnia pamięć podręczna w pamięci może zatrzymać większość wyszukiwań przed trafieniem do bazy danych.
źródło