Utilisation de PostgreSQL v9.1. J'ai les tables suivantes:
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
);
Dites que la première table foo
est peuplée comme ceci:
INSERT INTO foo (type) VALUES
( 'red' ),
( 'green' ),
( 'blue' );
Existe-t-il un moyen d'insérer bar
facilement des lignes en référençant le foo
tableau? Ou dois-je le faire en deux étapes, d'abord en recherchant le foo
type que je veux, puis en insérant une nouvelle ligne bar
?
Voici un exemple de pseudo-code montrant ce que j'espérais pouvoir faire:
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
la source
la source
INSERT simple
L'utilisation de a
LEFT [OUTER] JOIN
au lieu de[INNER] JOIN
signifie que les lignes deval
ne sont pas supprimées lorsqu'aucune correspondance n'est trouvée dansfoo
. Au lieu de cela,NULL
est entré pourfoo_id
.L'
VALUES
expression dans la sous-requête fait la même chose que le CTE de @ ypercube . Les expressions de table communes offrent des fonctionnalités supplémentaires et sont plus faciles à lire dans les grandes requêtes, mais elles constituent également un obstacle à l'optimisation. Ainsi, les sous-requêtes sont généralement un peu plus rapides lorsqu'aucun des éléments ci-dessus n'est nécessaire.id
comme nom de colonne est un anti-motif largement répandu. Devrait êtrefoo_id
etbar_id
ou quoi que ce soit descriptif. En rejoignant un tas de tables, vous vous retrouvez avec plusieurs colonnes toutes nomméesid
...Considérez simple
text
ouvarchar
au lieu devarchar(n)
. Si vous avez vraiment besoin d'imposer une restriction de longueur, ajoutez uneCHECK
contrainte:Vous devrez peut-être ajouter des conversions de type explicites. Comme l'
VALUES
expression n'est pas directement attachée à une table (comme dansINSERT ... VALUES ...
), les types ne peuvent pas être dérivés et les types de données par défaut sont utilisés sans déclaration de type explicite, ce qui peut ne pas fonctionner dans tous les cas. Il suffit de le faire dans la première rangée, le reste va faire la queue.INSERER les lignes FK manquantes en même temps
Si vous voulez créer des entrées inexistantes à
foo
la volée, dans une seule instruction SQL , les CTE sont essentiels:Notez les deux nouvelles lignes factices à insérer. Les deux sont violets , ce qui n'existe pas
foo
encore. Deux lignes pour illustrer la nécessité deDISTINCT
la premièreINSERT
déclaration.Explication pas à pas
Le 1er CTE
sel
fournit plusieurs lignes de données d'entrée. La sous-requêteval
avec l'VALUES
expression peut être remplacée par une table ou une sous-requête en tant que source. ImmédiatementLEFT JOIN
àfoo
ajouter lefoo_id
pour lestype
lignes préexistantes . Toutes les autres lignes sontfoo_id IS NULL
ainsi.Le second CTE
ins
insère différents nouveaux types (foo_id IS NULL
) dansfoo
, et renvoie le nouveau généréfoo_id
- ainsi que letype
pour rejoindre pour insérer des lignes.Le dernier extérieur
INSERT
peut maintenant insérer un foo.id pour chaque ligne: le type préexistait ou il avait été inséré à l'étape 2.À proprement parler, les deux insertions se produisent "en parallèle", mais comme il s'agit d'une seule instruction, les
FOREIGN KEY
contraintes par défaut ne se plaindront pas. L'intégrité référentielle est appliquée à la fin de l'instruction par défaut.Fiddle SQL pour Postgres 9.3. (Fonctionne de la même manière en 9.1.)
Il y a une petite condition de concurrence si vous exécutez plusieurs de ces requêtes simultanément. Lire la suite sous des questions connexes ici et ici et ici . Cela ne se produit vraiment que sous une charge simultanée importante, si jamais. En comparaison avec les solutions de mise en cache comme celle annoncée dans une autre réponse, les chances sont minimes .
Fonction à usage répété
Pour une utilisation répétée, je créerais une fonction SQL prenant un tableau d'enregistrements en paramètre et l'utilisant
unnest(param)
à la place de l'VALUES
expression.Ou, si la syntaxe des tableaux d'enregistrements est trop compliquée, utilisez une chaîne séparée par des virgules en tant que paramètre
_param
. Par exemple de la forme:Ensuite, utilisez ceci pour remplacer l'
VALUES
expression dans l'instruction ci-dessus:Fonction avec UPSERT dans Postgres 9.5
Créez un type de ligne personnalisé pour le passage de paramètres. On pourrait s'en passer, mais c'est plus simple:
Une fonction:
Appel:
Rapide et solide pour les environnements avec des transactions simultanées.
En plus des requêtes ci-dessus, cette ...
... s'applique
SELECT
ouINSERT
surfoo
: Tout élémenttype
qui n'existe pas encore dans la table FK est inséré. En supposant que la plupart des types préexistent. Pour être absolument sûr et exclure les conditions de concurrence, les lignes existantes dont nous avons besoin sont verrouillées (afin que les transactions simultanées ne puissent pas interférer). Si c'est trop paranoïaque pour votre cas, vous pouvez remplacer:avec
... s'applique
INSERT
ouUPDATE
((vrai "UPSERT")) surbar
: s'ildescription
existe déjà, iltype
est mis à jour:Mais seulement si cela
type
change réellement:... transmet des valeurs aux types de lignes bien connus avec un
VARIADIC
paramètre. Notez le maximum par défaut de 100 paramètres! Comparer:Il y a beaucoup d'autres façons de passer plusieurs lignes ...
Apparenté, relié, connexe:
la source
INSERT missing FK rows at the same time
exemple, le fait de placer cela dans une transaction réduirait-il le risque de concurrence dans SQL Server?SELECT
dans uneWITH
clause). Source: documentation MS.INSERT ... RETURNING \gset
inpsql
puis utiliser les valeurs renvoyées en tant que psql:'variables'
, mais cela ne fonctionne que pour les insertions à une seule ligne.Chercher. Vous avez essentiellement besoin des identifiants de foo pour les insérer dans le bar.
Pas de postgres spécifique, d'ailleurs. (et vous ne l'avez pas étiqueté comme ça) - c'est généralement ainsi que fonctionne SQL. Pas de raccourcis ici.
En ce qui concerne les applications, vous pouvez cependant avoir un cache d’articles foo en mémoire. Mes tables ont souvent jusqu'à 3 champs uniques:
Exemple:
Évidemment, lorsque vous voulez lier quelque chose à un compte - vous devez d'abord obtenir techniquement l'identifiant - mais étant donné que l'identifiant et le code ne changent jamais une fois qu'ils sont là, un cache positif en mémoire peut empêcher la plupart des recherches d'accéder à la base de données.
la source