web-dev-qa-db-fra.com

PostgreSQL: Sous-sélection dans l'insert

J'ai une table appelée map_tags:

map_id | map_license | map_desc

Et une autre table (widgets) dont les enregistrements contiennent une référence de clé étrangère (1 à 1) à un enregistrement map_tags:

widget_id | map_id | widget_name

Etant donné la contrainte que tous les map_licenses sont uniques (cependant, ils ne sont pas configurés en tant que clés sur map_tags), alors si j'ai un map_license et un widget_name, j'aimerais effectuer une insertion sur widgets tout à l'intérieur de la même instruction SQL:

INSERT INTO
    widgets w
(
    map_id,
    widget_name
)
VALUES (
    (
        SELECT
            mt.map_id
        FROM
            map_tags mt
        WHERE
            // This should work and return a single record because map_license is unique
            mt.map_license = '12345'
    ),
    'Bupo'
)

Je crois je suis sur la bonne voie, mais je sais d’emblée que c’est un code SQL incorrect pour Postgres. Est-ce que quelqu'un sait la bonne façon de réaliser une telle requête?

20
Bantha Fodder
INSERT INTO widgets
(
    map_id,
    widget_name
)
SELECT
    mt.map_id, 'Bupo'
FROM
    map_tags mt
WHERE
    mt.map_license = '12345'
23
Clodoaldo Neto

Utilisez la variante INSERT INTO SELECT, en incluant les constantes dans l’instruction SELECT.

La syntaxe PostgreSQL INSERT est la suivante:

INSERT INTO table [ ( column [, ...] ) ]
 { DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) [, ...] | query }
 [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ]

Prenez note de l’option query à la fin de la deuxième ligne ci-dessus.

Voici un exemple pour vous.

INSERT INTO 
    widgets
    (
        map_id,
        widget_name
    )
SELECT 
   mt.map_id,
   'Bupo'
FROM
    map_tags mt
WHERE
    mt.map_license = '12345'
30
gahooa

Réponse rapide: Vous n'avez pas " un seul enregistrement " vous avez un " défini avec 1 enregistrement " Si cela était javascript: Vous avez un "tableau avec 1 valeur" et non "1 valeur".

Dans votre exemple , un enregistrement peut être renvoyé dans la sous-requête, , Mais vous essayez toujours de décompresser un "tableau" d'enregistrements dans des paramètres réels Distincts dans un place qui prend seulement 1 paramètre.

Il m'a fallu quelques heures pour comprendre le "pourquoi pas". Alors que j'essayais de faire quelque chose de très similaire:

Voici mes notes:

tb_table01: (no records)
+---+---+---+
| a | b | c | << column names
+---+---+---+

tb_table02:
+---+---+---+
| a | b | c | << column names
+---+---+---+
|'d'|'d'|'d'| << record #1
+---+---+---+
|'e'|'e'|'e'| << record #2
+---+---+---+
|'f'|'f'|'f'| << record #3
+---+---+---+

--This statement will fail:
INSERT into tb_table01
    ( a, b, c )
VALUES
    (  'record_1.a', 'record_1.b', 'record_1.c' ),
    (  'record_2.a', 'record_2.b', 'record_2.c' ),

    -- This sub query has multiple
    -- rows returned. And they are NOT
    -- automatically unpacked like in 
    -- javascript were you can send an
    -- array to a variadic function.
    (
        SELECT a,b,c from tb_table02
    ) 
    ;

Fondamentalement, ne fait pas pense à "VALEURS" comme une fonction variadic Qui peut décompresser un tableau d'enregistrements. Aucun argument n’a été décompressé ici comme dans une fonction javascript . Tel que:

function takeValues( ...values ){ 
    values.forEach((v)=>{ console.log( v ) });
};

var records = [ [1,2,3],[4,5,6],[7,8,9] ];
takeValues( records );

//:RESULT:
//: console.log #1 : [1,2,3]
//: console.log #2 : [4,5,7]
//: console.log #3 : [7,8,9]

Retour à votre question SQL:

La réalité de cette fonctionnalité inexistante ne change pas Simplement parce que votre sous-sélection ne contient qu'un seul résultat. C'est A " défini avec un enregistrement " pas " un seul enregistrement ".

0
J.M.I. MADISON