Postgresql - zmień rozmiar kolumny varchar na mniejszą długość

153

Mam pytanie odnośnie ALTER TABLEpolecenia na naprawdę dużym stole (prawie 30 milionów wierszy). Jedna z jego kolumn to a varchar(255)i chciałbym zmienić jej rozmiar na a varchar(40). Zasadniczo chciałbym zmienić moją kolumnę, uruchamiając następujące polecenie:

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

Nie mam problemu, jeśli proces jest bardzo długi, ale wydaje się, że moja tabela nie jest bardziej czytelna podczas polecenia ALTER TABLE. Czy jest mądrzejszy sposób? Może dodać nową kolumnę, skopiować wartości ze starej kolumny, usunąć starą kolumnę i na koniec zmienić nazwę nowej?

Każda wskazówka będzie bardzo mile widziana! Z góry dziękuję,

Uwaga: używam PostgreSQL 9.0.

Labynocle
źródło
11
Żeby było jasne: wiesz, to resizingnie sprawi, że stół będzie zajmował mniej miejsca?
AH
nawet w moim przypadku? Mam na myśli, że kolumna będzie miała maksymalny rozmiar 40 znaków (więc oktetów) zamiast 255?
Labynocle
16
Jeśli powiesz, że varchar(255)PostgreSQL nie przydzieli 255 bajtów wartości, której rzeczywista długość wynosi 40 bajtów. Przydzieli 40 bajtów (plus trochę narzutu wewnętrznego). Jedyną rzeczą, która zmieni be changed by the opcję ALTER TABLE, jest maksymalna liczba bajtów, które możesz przechowywać w tej kolumnie bez otrzymywania błędu od PG.
AH
O narzutach wspomnianych AH: Jaki jest narzut dla varchar (n)?
Erwin Brandstetter
Sprawdź tutaj odpowiedź na aktualizację dba.stackexchange.com/questions/189890/…
Evan Carroll

Odpowiedzi:

73

Opis tego, jak to zrobić, znajduje się w sekcji Zmień rozmiar kolumny w tabeli PostgreSQL bez zmiany danych . Musisz włamać się do danych katalogu bazy danych. Jedynym sposobem, aby to zrobić oficjalnie, jest ALTER TABLE, a jak zauważyłeś, zmiana spowoduje zablokowanie i przepisanie całej tabeli podczas jej działania.

Upewnij się, że przeczytałeś sekcję Typy znaków w dokumentach przed zmianą tego. Różne dziwne przypadki, o których należy tutaj pamiętać. Kontrola długości jest wykonywana, gdy wartości są przechowywane w wierszach. Jeśli zhakujesz tam dolny limit, w ogóle nie zmniejszy to rozmiaru istniejących wartości. Po dokonaniu zmiany warto przeskanować całą tabelę, szukając wierszy, w których długość pola przekracza 40 znaków. Będziesz musiał wymyślić, jak skrócić je ręcznie - więc wróciłeś do niektórych blokad tylko tych dużych rozmiarów - ponieważ jeśli ktoś spróbuje zaktualizować cokolwiek w tym wierszu, odrzuci to teraz jako zbyt duże, w tym momencie przechowuje nową wersję wiersza. Dla użytkownika nastaje wesołość.

VARCHAR to okropny typ, który istnieje w PostgreSQL tylko po to, aby zachować zgodność z powiązaną z nim straszną częścią standardu SQL. Jeśli nie zależy Ci na kompatybilności z wieloma bazami danych, rozważ przechowywanie danych jako TEKST i dodaj ograniczenie, aby ograniczyć jego długość. Ograniczenia, które można zmieniać bez problemu z blokowaniem / przepisywaniem tabeli, i mogą wykonywać więcej sprawdzania integralności niż tylko sprawdzanie słabej długości.

Greg Smith
źródło
Dziękuję za Twoją odpowiedź. Sprawdzę twój link. Nie martwię się ręcznym sprawdzaniem rozmiaru, ponieważ cała moja zawartość ma maksymalny rozmiar 40 znaków. Muszę przeczytać więcej o ograniczeniach w TEXT, ponieważ uważałem, że VARCHAR lepiej sprawdzić długość :)
Labynocle
6
Zmiana długości varchar nie powoduje przepisania tabeli. Po prostu sprawdza długość ograniczenia względem całej tabeli dokładnie jako CHECK CONSTRAINT. Jeśli zwiększysz długość, nie ma nic do zrobienia, po prostu następne wstawienie lub aktualizacje zaakceptują większą długość. Jeśli zmniejszysz długość i wszystkie wiersze spełnią nowe, mniejsze ograniczenie, Pg nie podejmie żadnych dalszych działań poza zezwoleniem następnym wstawieniom lub aktualizacjom na zapisanie tylko nowej długości.
Maniero
3
@bigown, dla wyjaśnienia, twoje stwierdzenie jest prawdziwe tylko dla PostgreSQL 9.2+ , a nie dla starych.
MatheusOl
12
Link jest teraz martwy.
raarts
Aby uzyskać więcej informacji o tym, jak to działa, odwiedź dba.stackexchange.com/questions/189890/ ...
Evan Carroll
100

W PostgreSQL 9.1 jest prostszy sposób

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) |
sir_leslie
źródło
6
Zwróć uwagę, że działa tylko dlatego, że określasz większy rozmiar (30> 10). Jeśli rozmiar jest mniejszy, pojawi się ten sam błąd, co ja .
Matthieu
2
Postgres nie powinien generować błędu, jeśli zmniejszysz rozmiar varchar za pomocą zapytania ALTER TABLE, chyba że jeden lub więcej wierszy zawiera wartość, która przekracza nowy rozmiar.
Powiedz
@ Powiedz, interesujące. Czy to oznacza, że ​​Postgres wykonuje pełne skanowanie tabeli, czy w jakiś sposób zachowuje maksymalny rozmiar w swoich statystykach?
Matthieu,
47

Ok, pewnie spóźniłem się na imprezę, ALE ...

NIE MA POTRZEBY ZMIENIANIA KOLUMNY W TWOIM PRZYPADKU!

Postgres, w przeciwieństwie do niektórych innych baz danych, jest na tyle inteligentny, że używa tylko wystarczającej ilości miejsca, aby dopasować ciąg (nawet używając kompresji dla dłuższych ciągów), więc nawet jeśli twoja kolumna jest zadeklarowana jako VARCHAR (255) - jeśli przechowujesz 40-znakowe ciągi w w kolumnie, użycie miejsca wyniesie 40 bajtów + 1 bajt narzutu.

Wymagane miejsce na przechowywanie krótkiego ciągu (do 126 bajtów) to 1 bajt plus rzeczywisty ciąg, który obejmuje wypełnienie spacjami w przypadku znaku. Dłuższe łańcuchy mają 4 bajty narzutu zamiast 1. Długie łańcuchy są kompresowane przez system automatycznie, więc fizyczne wymagania na dysku mogą być mniejsze. Bardzo długie wartości są również przechowywane w tabelach w tle, aby nie przeszkadzały w szybkim dostępie do krótszych wartości w kolumnach.

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

Specyfikacja rozmiaru w VARCHAR służy tylko do sprawdzania rozmiaru wstawianych wartości, nie wpływa na układ dysku. W rzeczywistości pola VARCHAR i TEXT są przechowywane w ten sam sposób w Postgres .

Siergiej
źródło
8
Nigdy nie jest za późno, aby dodać więcej informacji o „dlaczego”! Dziękuję za wszystkie te informacje
Labynocle
Czasami musisz zachować spójność w strukturze swojej bazy danych. Nawet jeśli 2 kolumny nie mają relacji, mogą mieć relację z punktu widzenia koncepcji, na przykład wyewidencjonuj model EAV.
Alexandre,
36

Miałem ten sam problem, próbując skrócić VARCHAR z 32 do 8 i uzyskać rozszerzenie ERROR: value too long for type character varying(8). Chcę pozostać jak najbliżej SQL, ponieważ używam samodzielnie wykonanej struktury podobnej do JPA, która może być konieczna do przełączenia się na inny DBMS zgodnie z wyborami klienta (domyślnie PostgreSQL). Dlatego nie chcę używać sztuczki polegającej na zmienianiu tabel systemowych.

Skończyłem używając USINGoświadczenia w ALTER TABLE:

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

Jak zauważył @raylu, ALTERuzyskuje wyłączną blokadę stołu, więc wszystkie inne operacje będą opóźnione do zakończenia.

Matthieu
źródło
2
ALTERnabywa wyłączną blokadę na stole i zapobiega wszystkie inne operacje
raylu
8

Dodając nową kolumnę i zastępując nową starą, pracowałem dla mnie, na redshift postgresql, zapoznaj się z tym linkiem, aby uzyskać więcej informacji 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;
spats
źródło
7

Oto pamięć podręczna strony opisanej przez Grega Smitha. W przypadku, gdy to również umrze, oświadczenie alter wygląda następująco:

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

Jeśli twoja tabela to TABELA1, kolumna to COL1 i chcesz ustawić ją na 35 znaków (+4 jest potrzebne do starszych celów zgodnie z linkiem, prawdopodobnie narzut, do którego odnosi się AH w komentarzach).

Tomek
źródło
7

jeśli umieścisz zmianę w transakcji, tabela nie powinna być blokowana:

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

to zadziałało dla mnie błyskawicznie, kilka sekund na stole z ponad 400 tysiącami wierszy.

jacktrade
źródło
5
Dlaczego miałbyś oczekiwać, że jawne opakowanie transakcji zmieni zachowanie blokowania ALTERinstrukcji? Tak nie jest.
Erwin Brandstetter
spróbuj sam, z opakowaniem transakcji i bez niego, zauważysz ogromną różnicę.
jacktrade
2
Twoja odpowiedź jest nieprawidłowa z zasady. Każda instrukcja DDL bez jawnego opakowania transakcji jest niejawnie uruchamiana wewnątrz transakcji. Jedynym możliwym skutkiem jawnej transakcji jest to, że blokady są utrzymywane dłużej - do momentu jawnego COMMIT. Opakowanie ma sens tylko wtedy, gdy chcesz umieścić więcej poleceń w tej samej transakcji.
Erwin Brandstetter
masz całkowitą rację, ale nalegam: spróbuj sam, kontynuuj. a następnie zapytaj, dlaczego nie działa w ten sam sposób.
jacktrade
Nie pomogło w Postgres 9.3.
Noumenon,
1

Znalazłem bardzo łatwy sposób na zmianę rozmiaru, tj. Adnotację @Size (min = 1, max = 50), która jest częścią "importu javax.validation.constraints" tj. "Importu 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),

.....

)
Tito
źródło
Dzięki za Twój post! Prosimy nie używać podpisów / sloganów w swoich postach. Twoja skrzynka użytkownika liczy się jako Twój podpis i możesz używać swojego profilu do publikowania dowolnych informacji o sobie. Często zadawane pytania dotyczące podpisów / sloganów
Andrew Barber,
0

Spróbuj uruchomić następującą tabelę zmian:

ALTER TABLE public.users 
ALTER COLUMN "password" TYPE varchar(300) 
USING "password"::varchar;
Никита Верёвкин
źródło