2011-10-11 43 views
88

Ho una domanda sul comando ALTER TABLE su una tabella molto grande (quasi 30 milioni di righe). Una delle sue colonne è un varchar(255) e vorrei ridimensionarlo a un varchar(40). In sostanza, vorrei cambiare la mia colonna eseguendo il comando seguente:Postgresql - modifica la dimensione di una colonna varchar

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

non ho alcun problema se il processo è molto lungo, ma a quanto pare il mio tavolo non è più leggibile durante il comando ALTER TABLE. C'è un modo più intelligente? Forse aggiungere una nuova colonna, copiare i valori dalla vecchia colonna, rilasciare la vecchia colonna e rinominare infine quella nuova?

Qualsiasi indizio sarà molto apprezzato! Grazie in anticipo,

Nota: utilizzo PostgreSQL 9.0.

+8

Giusto per essere chiari: sapete, che il 'ridimensionamento' non renderà il tavolo occupare meno spazio? –

+0

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

+10

Se si dice 'varchar (255)' a PostgreSQL, allora _non_ alloca 255 byte per un valore la cui lunghezza reale è 40 byte. Assegnerà 40 byte (più un overhead interno). L'unica cosa che sarà 'cambiata da' ALTER TABLE' è il numero massimo di byte che puoi memorizzare in quella colonna senza ottenere un errore da PG. –

risposta

58

C'è una descrizione di come fare questo a Resize a column in a PostgreSQL table without changing data. 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'intera tabella mentre è in esecuzione.

Assicurati di leggere la sezione Character Types della documentazione prima di cambiarla. Tutti i tipi di casi strani di cui essere a conoscenza qui. Il controllo della lunghezza viene eseguito quando i valori vengono memorizzati nelle righe. Se si ha un limite inferiore lì, ciò non ridurrà affatto la dimensione dei valori esistenti. Sarebbe saggio fare una scansione sull'intera tabella cercando le righe in cui la lunghezza del campo è> 40 caratteri dopo aver apportato la modifica. Avrai bisogno di capire come troncare quelli manualmente - quindi sei tornato alcuni blocchi solo su quelli sovradimensionati - perché se qualcuno cerca di 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 soddisfare la sua terribile parte associata dello standard SQL. Se non ti interessa la compatibilità multi-database, considera di memorizzare i tuoi dati come TEXT e aggiungi un vincolo per limitarne la lunghezza. Vincoli che è possibile modificare senza questo problema di blocco/riscrittura della tabella e possono eseguire un controllo di integrità maggiore rispetto al controllo della lunghezza insufficiente.

+0

Grazie per la risposta. Controllerò il tuo link Non sono preoccupato per il controllo della dimensione manuale perché tutto il mio contenuto ha una dimensione massima di 40 caratteri.Ho bisogno di leggere di più sul vincolo su TESTO perché credevo che VARCHAR fosse meglio controllare lentgh :) – Labynocle

+0

Consigli pericolosi che non stanno andando risolvere il problema dell'OP ... – Sergey

+3

La modifica della lunghezza varchar non riscrive la tabella. Controlla semplicemente la lunghezza del vincolo sull'intera tabella esattamente come CONSEGNA DI CONTROLLO. Se aumenti la lunghezza non c'è niente da fare, solo il prossimo inserimento o gli aggiornamenti accetteranno una lunghezza maggiore. Se si riduce la lunghezza e tutte le righe passano il nuovo vincolo più piccolo, Pg non intraprende ulteriori azioni oltre a consentire a inserti o aggiornamenti successivi di scrivere solo la nuova lunghezza. – Maniero

7

Ecco il numero the cache della pagina descritta da Greg Smith. Nel caso in cui muore pure, l'istruzione ALTER assomiglia a questo:

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

Dove il tavolo è TABLE1, la colonna è COL1 e si desidera impostarla a 35 caratteri (la 4 è necessario per scopi legacy secondo al link, possibilmente il sovraccarico indicato da AH nei commenti).

19

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 auto-costruita simile a JPA 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 utilizzando l'istruzione USING nella ALTER TABLE:

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

non ho davvero considerato le implicazioni se la tabella si accede da un altro programma o se la tabella è indicizzato. Presumo che Postgres faccia questo tipo di automaticamente?

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 .Dimensione;"

@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), 

..... 

) 
+0

Grazie per il tuo post! Per favore non usare firme/tagline nei tuoi post. Il tuo box utente conta come la tua firma, e puoi usare il tuo profilo per pubblicare qualsiasi informazione che ti riguarda. [FAQ su firme/taglines] (http://stackoverflow.com/faq#signatures) –

40

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

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

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

La memorizzazione necessario per una breve stringa (fino a 126 byte) è 1 byte più la stringa effettiva, che comprende l'imbottitura spazio nel caso di carattere. Stringhe più lunghe hanno 4 byte di overhead anziché 1. Le stringhe lunghe vengono compresse automaticamente dal sistema, pertanto il requisito fisico su disco potrebbe essere inferiore. I valori molto lunghi sono anche memorizzati nelle tabelle di sfondo in modo che non interferiscano con l'accesso rapido ai valori di colonna più brevi.

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

La specifica dimensione in VARCHAR viene utilizzato solo per controllare la dimensione dei valori che vengono inseriti, non influenza il layout del disco. Infatti, VARCHAR and TEXT fields are stored in the same way in Postgres.

+6

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

+0

A volte devi essere coerente nella struttura del tuo database. Anche se 2 colonne non hanno una relazione, possono avere una relazione nel punto di vista del concetto, ad esempio il checkout del modello EAV. – Alexandre

4

Aggiunta nuova colonna e la sostituzione di uno nuovo con il vecchio lavorato per me, su PostgreSQL redshift, consultare questo link per maggiori informazioni 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; 
49

In PostgreSQL 9.1 c'è un modo più semplice

http://www.postgresql.org/message-id/[email protected]

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) | 
+4

Nota che funziona solo perché stai specificando una dimensione ** più grande ** (30> 10). Se la dimensione è minore, otterrai [lo stesso errore di quello che avevo] (http://stackoverflow.com/a/10991954/1098603). – Matthieu

3

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 sfolgorante velocemente, pochi secondi su un tavolo con più di 400k righe.

+0

Perché dovresti aspettarti che il wrapper esplicito delle transazioni modifichi il comportamento di blocco dell'istruzione 'ALTER'? Non è così. –

+0

prova te stesso, con e senza il wrapper della transazione, noterai un'enorme differenza. – jipipayo

+0

La tua risposta non è corretta sul principale. Qualsiasi istruzione DDL senza wrapper esplicito delle transazioni viene eseguita in modo implicito all'interno di una transazione. L'unico effetto possibile della transazione esplicita è che i blocchi siano mantenuti * più lunghi * - fino all'esplicita 'COMMIT'. Il wrapper ha senso solo se vuoi inserire più comandi nella stessa transazione. –

Problemi correlati