Con un VALUES
autonomo espressione PostgreSQL non ha idea di quali dovrebbero essere i tipi di dati. Con semplici letterali numerici il sistema è felice di assumere tipi corrispondenti. Ma con altri input (come NULL
) dovresti eseguire il cast in modo esplicito, come hai già scoperto.
Puoi interrogare pg_catalog
(veloce, ma specifico per PostgreSQL) o lo information_schema
(SQL lento, ma standard) per scoprire e preparare la tua istruzione con i tipi appropriati.
Oppure puoi usare uno di questi semplici "trucchi" (ho salvato il meglio per ultimo ):
0. Seleziona la riga con LIMIT 0
, aggiungi le righe con UNION ALL VALUES
UPDATE foo f
SET x = t.x
, y = t.y
FROM (
(SELECT pkid, x, y FROM foo LIMIT 0) -- parenthesis needed with LIMIT
UNION ALL
VALUES
(1, 20, NULL) -- no type casts here
, (2, 50, NULL)
) t -- column names and types are already defined
WHERE f.pkid = t.pkid;
La prima sottoselezione della sottoquery:
(SELECT x, y, pkid FROM foo LIMIT 0)
ottiene nomi e tipi per le colonne, ma LIMIT 0
impedisce di aggiungere una riga effettiva. Le righe successive vengono forzate al tipo di riga ora ben definito e vengono immediatamente verificate se corrispondono al tipo. Dovrebbe essere un sottile miglioramento aggiuntivo rispetto al tuo modulo originale.
Pur fornendo valori per tutti colonne della tabella questa breve sintassi può essere utilizzata per la prima riga:
(TABLE foo LIMIT 0)
Limitazione maggiore :Postgres esegue il cast dei valori letterali di input dei VALUES
indipendenti immediatamente un'espressione a un tipo "best-effort". Quando in seguito tenta di eseguire il cast ai tipi specificati del primo SELECT
, potrebbe essere già troppo tardi per alcuni tipi se non è presente alcun cast di assegnazione registrato tra il tipo presunto e il tipo di destinazione. Esempi:text
-> timestamp
o text
-> json
.
Professionista:
- Spese generali minime.
- Leggibile, semplice e veloce.
- Devi solo conoscere i nomi delle colonne pertinenti della tabella.
Con:
- La risoluzione del tipo può non riuscire per alcuni tipi.
1. Seleziona la riga con LIMIT 0
, aggiungi le righe con UNION ALL SELECT
UPDATE foo f
SET x = t.x
, y = t.y
FROM (
(SELECT pkid, x, y FROM foo LIMIT 0) -- parenthesis needed with LIMIT
UNION ALL SELECT 1, 20, NULL
UNION ALL SELECT 2, 50, NULL
) t -- column names and types are already defined
WHERE f.pkid = t.pkid;
Professionista:
- Mi piace 0. , ma evita che la risoluzione dei tipi non vada a buon fine.
Con:
UNION ALL SELECT
è più lento diVALUES
espressione per lunghi elenchi di righe, come hai trovato nel test.- Sintassi dettagliata per riga.
2. VALUES
espressione con tipo per colonna
...
FROM (
VALUES
((SELECT pkid FROM foo LIMIT 0)
, (SELECT x FROM foo LIMIT 0)
, (SELECT y FROM foo LIMIT 0)) -- get type for each col individually
, (1, 20, NULL)
, (2, 50, NULL)
) t (pkid, x, y) -- columns names not defined yet, only types.
...
Contrariamente a 0. questo evita una risoluzione prematura del tipo.
La prima riga in VALUES
espressione è una riga di NULL
valori che definisce il tipo per tutte le righe successive. Questa riga di disturbo iniziale viene filtrata da WHERE f.pkid = t.pkid
più tardi, così non vede mai la luce del giorno. Per altri scopi puoi eliminare la prima riga aggiunta con OFFSET 1
in una sottoquery.
Professionista:
- In genere più veloce di 1. (o anche 0. )
- Sintassi breve per tabelle con molte colonne e solo poche sono rilevanti.
- Devi solo conoscere i nomi delle colonne pertinenti della tabella.
Con:
- Sintassi dettagliata solo per poche righe
- Meno leggibile (IMO).
3. VALUES
espressione con tipo riga
UPDATE foo f
SET x = (t.r).x -- parenthesis needed to make syntax unambiguous
, y = (t.r).y
FROM (
VALUES
('(1,20,)'::foo) -- columns need to be in default order of table
,('(2,50,)') -- nothing after the last comma for NULL
) t (r) -- column name for row type
WHERE f.pkid = (t.r).pkid;
Ovviamente conosci il nome del tavolo. Se conosci anche il numero di colonne e il loro ordine, puoi lavorare con questo.
Per ogni tabella in PostgreSQL viene registrato automaticamente un tipo di riga. Se abbini il numero di colonne nella tua espressione, puoi eseguire il cast al tipo di riga della tabella ('(1,50,)'::foo
) assegnando così i tipi di colonna in modo implicito. Non inserire nulla dietro una virgola per inserire un NULL
valore. Aggiungi una virgola per ogni colonna finale irrilevante.
Nel passaggio successivo puoi accedere alle singole colonne con la sintassi dimostrata. Ulteriori informazioni sulla Selezione del campo nel manuale.
Oppure potresti aggiungere una riga di valori NULL e usa una sintassi uniforme per i dati effettivi:
...
VALUES
((NULL::foo)) -- row of NULL values
, ('(1,20,)') -- uniform ROW value syntax for all
, ('(2,50,)')
...
Professionista:
- Il più veloce (almeno nei miei test con poche righe e colonne).
- Sintassi più breve per poche righe o tabelle in cui sono necessarie tutte le colonne.
- Non è necessario precisare le colonne della tabella:tutte le colonne hanno automaticamente il nome corrispondente.
Con:
- Sintassi non molto nota per la selezione dei campi da record/riga/tipo composito.
- Devi conoscere il numero e la posizione delle colonne pertinenti nell'ordine predefinito.
4. VALUES
espressione con scomposto tipo di riga
Come 3. , ma con righe scomposte nella sintassi standard:
UPDATE foo f
SET x = t.x
, y = t.y
FROM (
VALUES
(('(1,20,)'::foo).*) -- decomposed row of values
, (2, 50, NULL)
) t(pkid, x, y) -- arbitrary column names (I made them match)
WHERE f.pkid = t.pkid; -- eliminates 1st row with NULL values
Oppure, di nuovo con una riga iniziale di valori NULL:
...
VALUES
((NULL::foo).*) -- row of NULL values
, (1, 20, NULL) -- uniform syntax for all
, (2, 50, NULL)
...
Pro e contro come 3. , ma con una sintassi più comunemente nota.
E devi scrivere i nomi delle colonne (se ne hai bisogno).
5. VALUES
espressione con tipi recuperati dal tipo di riga
Come ha commentato Unril, possiamo combinare le virtù di 2. e 4. per fornire solo un sottoinsieme di colonne:
UPDATE foo f
SET ( x, y)
= (t.x, t.y) -- short notation, see below
FROM (
VALUES
((NULL::foo).pkid, (NULL::foo).x, (NULL::foo).y) -- subset of columns
, (1, 20, NULL)
, (2, 50, NULL)
) t(pkid, x, y) -- arbitrary column names (I made them match)
WHERE f.pkid = t.pkid;
Pro e contro come 4. , ma possiamo lavorare con qualsiasi sottoinsieme di colonne e non è necessario conoscere l'elenco completo.
Visualizza anche una breve sintassi per UPDATE
stesso che è conveniente per i casi con molte colonne. Correlati:
- Aggiornamento collettivo di tutte le colonne
4. e 5. sono i miei preferiti.
db<>gioca qui - dimostrando tutto