Postgresql: modifica la dimensione di una colonna varchar in lunghezza inferiore


154

Ho una domanda sul ALTER TABLEcomando su una tabella molto grande (quasi 30 milioni di righe). Una delle sue colonne è a varchar(255)e vorrei ridimensionarla a varchar(40). Fondamentalmente, vorrei cambiare la mia colonna eseguendo il seguente comando:

ALTER TABLE mytable ALTER COLUMN mycolumn TYPE varchar(40);

Non ho alcun problema se il processo è molto lungo ma sembra che la mia tabella non sia più leggibile durante il comando ALTER TABLE. C'è un modo più intelligente? Forse aggiungi una nuova colonna, copia i valori dalla vecchia colonna, elimina la vecchia colonna e infine rinomina quella nuova?

Qualsiasi indizio sarà molto apprezzato! Grazie in anticipo,

Nota: utilizzo PostgreSQL 9.0.


11
Giusto per essere chiari: sapete, questo resizingnon farà occupare meno spazio al tavolo?
AH,

anche nel mio caso? Voglio dire che la colonna avrà una dimensione massima di 40 caratteri (quindi ottetti) anziché 255?
Labynocle,

16
Se dici varchar(255)a PostgreSQL, non assegnerà 255 byte per un valore la cui lunghezza reale è 40 byte. Assegnerà 40 byte (più un sovraccarico interno). L'unica cosa che be changed by the ALTER TABLE` è il numero massimo di byte che è possibile memorizzare in quella colonna senza ricevere un errore da PG.
AH,

Informazioni sull'overhead AH menzionato: Qual è l'overhead per varchar (n)?
Erwin Brandstetter,

Controlla la risposta qui per un aggiornamento dba.stackexchange.com/questions/189890/…
Evan Carroll

Risposte:


73

C'è una descrizione di come eseguire questa operazione nel ridimensionare una colonna in una tabella PostgreSQL senza modificare i dati . Devi hackerare i dati del catalogo del database. L'unico modo per farlo ufficialmente è con ALTER TABLE e, come hai notato, il cambiamento bloccherà e riscriverà l'intero tavolo mentre è in esecuzione.

Assicurati di leggere la sezione Tipi di carattere dei documenti prima di modificarlo. Tutti i tipi di casi strani di cui essere consapevoli qui. Il controllo della lunghezza viene eseguito quando i valori vengono archiviati nelle righe. Se hackeri un limite inferiore, ciò non ridurrà affatto la dimensione dei valori esistenti. Sarebbe saggio eseguire una scansione su tutta la tabella alla ricerca di righe in cui la lunghezza del campo è> 40 caratteri dopo aver apportato la modifica. Dovrai capire come troncare quelli manualmente - quindi sei tornato ad alcuni blocchi solo su quelli sovradimensionati - perché se qualcuno prova ad aggiornare qualcosa su quella riga lo rifiuterà come troppo grande ora, al punto va a memorizzare la nuova versione della riga. L'ilarità segue per l'utente.

VARCHAR è un tipo terribile che esiste in PostgreSQL solo per conformarsi alla parte terribile associata dello standard SQL. Se non ti interessa la compatibilità multi-database, considera di archiviare i tuoi dati come TESTO e aggiungi un vincolo per limitarne la lunghezza. Vincoli che è possibile modificare senza questo problema di blocco / riscrittura della tabella e possono eseguire più controlli di integrità rispetto al solo controllo di lunghezza debole.


Grazie per la risposta. Controllerò il tuo link. Non mi preoccupo del controllo manuale delle dimensioni perché tutti i miei contenuti hanno una dimensione massima di 40 caratteri. Devo leggere di più sul vincolo di TEXT perché credevo che VARCHAR fosse meglio controllare la
quaresima

6
La modifica della lunghezza del varchar non riscrive la tabella. Controlla semplicemente la lunghezza del vincolo sull'intera tabella esattamente come CONTROLLA VINCERE. Se aumenti la lunghezza non c'è nulla da fare, solo l'inserimento o gli aggiornamenti successivi accettano una lunghezza maggiore. Se riduci la lunghezza e tutte le righe superano il nuovo vincolo più piccolo, Pg non intraprende ulteriori azioni oltre a consentire a inserimenti o aggiornamenti successivi di scrivere solo la nuova lunghezza.
Maniero,

3
@bigown, solo per chiarire, la tua affermazione è vera solo per PostgreSQL 9.2+ , non quelli vecchi.
Matheus,

12
Il link ora è morto.
raarts

Per ulteriori informazioni su come funziona, consulta dba.stackexchange.com/questions/189890/…
Evan Carroll,

100

In PostgreSQL 9.1 c'è un modo più semplice

http://www.postgresql.org/message-id/162867790801110710g3c686010qcdd852e721e7a559@mail.gmail.com

CREATE TABLE foog(a varchar(10));

ALTER TABLE foog ALTER COLUMN a TYPE varchar(30);

postgres=# \d foog

 Table "public.foog"
 Column |         Type          | Modifiers
--------+-----------------------+-----------
 a      | character varying(30) |

6
Nota che funziona solo perché stai specificando una dimensione più grande (30> 10). Se la dimensione è inferiore, otterrai lo stesso errore che avevo .
Matthieu,

2
Postgres non dovrebbe generare un errore se si riduce la dimensione del varchar tramite una query ALTER TABLE a meno che una o più righe non contengano un valore superiore alla nuova dimensione.
Racconta il

@Tello, interessante. Significa che Postgres esegue una scansione completa della tabella o mantiene in qualche modo la dimensione massima nelle sue statistiche?
Matthieu,

47

Ok, probabilmente sono in ritardo alla festa, MA ...

Non c'è bisogno di ridimensionare la colonna nel tuo caso!

Postgres, a differenza di altri database, è abbastanza intelligente da usare solo lo spazio sufficiente per adattarsi alla stringa (anche usando la compressione per stringhe più lunghe), quindi anche se la tua colonna è dichiarata come VARCHAR (255) - se memorizzi stringhe di 40 caratteri in nella colonna, l'utilizzo dello spazio sarà di 40 byte + 1 byte di sovraccarico.

Il requisito di archiviazione per una stringa breve (fino a 126 byte) è 1 byte più la stringa effettiva, che include il riempimento dello spazio in caso di carattere. Le stringhe più lunghe hanno 4 byte di sovraccarico invece di 1. Le stringhe lunghe vengono compresse automaticamente dal sistema, quindi i requisiti fisici sul disco potrebbero essere inferiori. I valori molto lunghi vengono anche memorizzati in tabelle di sfondo in modo che non interferiscano con un accesso rapido a valori di colonna più brevi.

( http://www.postgresql.org/docs/9.0/interactive/datatype-character.html )

La specifica della dimensione in VARCHAR viene utilizzata solo per verificare la dimensione dei valori inseriti, non influisce sul layout del disco. In effetti, i campi VARCHAR e TEXT sono memorizzati allo stesso modo in Postgres .


8
Non è mai troppo tardi per aggiungere ulteriori informazioni sul "perché"! Grazie per tutte queste informazioni
Labynocle

A volte è necessario essere coerenti nella struttura del database. Anche se 2 colonne non hanno una relazione, possono avere una relazione dal punto di vista del concetto, ad esempio controllare il modello EAV.
Alexandre,

37

Stavo affrontando lo stesso problema cercando di troncare un VARCHAR da 32 a 8 e ottenere il ERROR: value too long for type character varying(8). Voglio stare il più vicino possibile a SQL perché sto usando una struttura simile a JPA fatta in casa che potremmo dover passare a diversi DBMS in base alle scelte del cliente (PostgreSQL è quello predefinito). Quindi, non voglio usare il trucco di alterare le tabelle di sistema.

Ho finito di usare la USINGdichiarazione nel ALTER TABLE:

ALTER TABLE "MY_TABLE" ALTER COLUMN "MyColumn" TYPE varchar(8)
USING substr("MyColumn", 1, 8)

Come notato da @raylu, ALTERacquisisce un blocco esclusivo sul tavolo in modo che tutte le altre operazioni vengano ritardate fino al completamento.


2
la ALTERacquisisce un blocco esclusivo sul tavolo e impedisce tutte le altre operazioni
raylu

8

Aggiungendo una nuova colonna e sostituendone una nuova con quella vecchia per me, su redgrift postgresql, fai riferimento a questo link per maggiori dettagli https://gist.github.com/mmasashi/7107430

BEGIN;
LOCK users;
ALTER TABLE users ADD COLUMN name_new varchar(512) DEFAULT NULL;
UPDATE users SET name_new = name;
ALTER TABLE users DROP name;
ALTER TABLE users RENAME name_new TO name;
END;

7

Ecco la cache della pagina descritta da Greg Smith. Nel caso in cui muoia anche, l'istruzione alter appare così:

UPDATE pg_attribute SET atttypmod = 35+4
WHERE attrelid = 'TABLE1'::regclass
AND attname = 'COL1';

Dove la tabella è TABELLA1, la colonna è COL1 e si desidera impostarla su 35 caratteri (il +4 è necessario per scopi legacy in base al collegamento, possibilmente il sovraccarico a cui fa riferimento AH nei commenti).


7

se si inserisce la modifica in una transazione, la tabella non deve essere bloccata:

BEGIN;
  ALTER TABLE "public"."mytable" ALTER COLUMN "mycolumn" TYPE varchar(40);
COMMIT;

questo ha funzionato per me velocissimo, pochi secondi su un tavolo con più di 400k righe.


5
Perché dovresti aspettarti che il wrapper di transazione esplicito cambi il comportamento di blocco dell'istruzione ALTER? Non
Erwin Brandstetter,

mettiti alla prova, con e senza il wrapper della transazione, noterai un'enorme differenza.
Jacktrade,

2
La tua risposta è errata sul principale. Qualsiasi istruzione DDL senza wrapper di transazione esplicito viene eseguita in modo implicito all'interno di una transazione. L'unico effetto possibile della transazione esplicita è che i blocchi vengono mantenuti più a lungo , fino a quando non è esplicita COMMIT. Il wrapper ha senso solo se si desidera inserire più comandi nella stessa transazione.
Erwin Brandstetter,

hai perfettamente ragione, ma insisto: mettiti alla prova, continua. e poi chiedi perché non funziona allo stesso modo.
jacktrade,

Non ha aiutato Postgres 9.3.
Noumenon,

1

Ho trovato un modo molto semplice per modificare le dimensioni, ad esempio l'annotazione @Size (min = 1, max = 50) che fa parte di "import javax.validation.constraints" ovvero "import javax.validation.constraints.Size;"

@Size(min = 1, max = 50)
private String country;


when executing  this is hibernate you get in pgAdmin III 


CREATE TABLE address
(
.....
  country character varying(50),

.....

)

Grazie per il tuo post! Non utilizzare firme / slogan nei tuoi post. La tua casella utente conta come la tua firma e puoi usare il tuo profilo per pubblicare qualsiasi informazione su di te che ti piace. FAQ su firme / slogan
Andrew Barber

0

Prova a eseguire la seguente tabella alter:

ALTER TABLE public.users 
ALTER COLUMN "password" TYPE varchar(300) 
USING "password"::varchar;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.