Postgresql - изменить размер столбца varchar на меньшую длину


154

У меня вопрос по поводу ALTER TABLEкоманды для действительно большой таблицы (почти 30 миллионов строк). Один из столбцов - это a, varchar(255)и я хотел бы изменить его размер на a varchar(40). По сути, я хотел бы изменить свой столбец, выполнив следующую команду:

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

У меня нет проблем, если процесс очень длинный, но кажется, что моя таблица больше не читается во время команды ALTER TABLE. Есть ли умнее? Может быть, добавить новый столбец, скопировать значения из старого столбца, удалить старый столбец и, наконец, переименовать новый?

Любая подсказка будет с благодарностью! Заранее спасибо,

Примечание: я использую PostgreSQL 9.0.


11
Просто чтобы прояснить: вы знаете, что resizingне заставит столик занимать меньше места?
АХ

даже в моем случае? Я имею в виду столбец будет иметь максимальный размер 40 символов (так октетов) вместо 255?
Labynocle

16
Если вы скажете varchar(255)PostgreSQL, то он не будет выделять 255 байтов для значения, реальная длина которого составляет 40 байтов. Он выделит 40 байтов (плюс некоторые внутренние издержки). Единственное, что be changed by the ALTER TABLE`, - это максимальное количество байтов, которое вы можете сохранить в этом столбце, не получая ошибки от PG.
AH


Проверьте ответ здесь для обновления dba.stackexchange.com/questions/189890/…
Эван Кэрролл

Ответы:


73

Описание того, как это сделать, приведено в разделе Изменение размера столбца в таблице PostgreSQL без изменения данных . Вы должны взломать данные каталога базы данных. Единственный способ сделать это официально - с помощью ALTER TABLE, и, как вы заметили, изменение заблокирует и перезапишет всю таблицу во время ее работы.

Обязательно ознакомьтесь с разделом « Типы символов » в документации, прежде чем изменять это. Всевозможные странные случаи, о которых нужно знать здесь. Проверка длины выполняется, когда значения сохраняются в строках. Если вы взломаете нижний предел, это вообще не уменьшит размер существующих значений. Было бы целесообразно выполнить сканирование всей таблицы в поисках строк, длина поля которых превышает 40 символов после внесения изменений. Вам нужно будет выяснить, как их урезать вручную - чтобы вы вернули некоторые блокировки только для слишком больших - потому что если кто-то попытается обновить что-либо в этом ряду, он отклонит его как слишком большое сейчас, в тот момент, когда это идет, чтобы сохранить новую версию ряда. Веселье наступает для пользователя.

VARCHAR - это ужасный тип, который существует в PostgreSQL только для соответствия связанной с ним ужасной части стандарта SQL. Если вас не волнует совместимость с несколькими базами данных, рассмотрите возможность хранения ваших данных в формате TEXT и добавьте ограничение, ограничивающее их длину. Ограничения, которые вы можете изменить без этой проблемы блокировки / перезаписи таблицы, и они могут выполнять больше проверки целостности, чем просто проверка слабой длины.


Спасибо за ответ. Я проверю вашу ссылку. Я не беспокоюсь о ручной проверке размера, потому что весь мой контент имеет максимальный размер 40 символов. Мне нужно больше узнать об ограничении на TEXT, потому что я полагал, что VARCHAR лучше проверить на длину :)
Labynocle

6
Изменение длины varchar не переписывает таблицу. Он просто проверяет длину ограничения для всей таблицы точно так же, как CHECK CONSTRAINT. Если вы увеличиваете длину, делать нечего, просто следующая вставка или обновления примут большую длину. Если вы уменьшаете длину, и все строки проходят новое меньшее ограничение, Pg не предпринимает никаких дальнейших действий, кроме того, чтобы позволить следующим вставкам или обновлениям записывать только новую длину.
Maniero

3
@bigown, просто чтобы уточнить, ваше утверждение верно только для PostgreSQL 9.2+ , а не для старых.
MatheusOl

12
Ссылка сейчас мертва.
17

Для получения дополнительной информации о том, как это работает, посетите dba.stackexchange.com/questions/189890/…
Эван Кэрролл,

100

В PostgreSQL 9.1 есть более простой способ

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
Обратите внимание, что это работает только потому, что вы указываете больший размер (30> 10). Если размер меньше, вы получите ту же ошибку, что и я .
Матье

2
Postgres не должен выдавать ошибку, если вы уменьшаете размер varchar с помощью запроса ALTER TABLE, если только одна из нескольких строк не содержит значение, которое превышает новый размер.
Скажи

@ Расскажи, интересно. Означает ли это, что Postgres выполняет полное сканирование таблицы или сохраняет максимальный размер в своей статистике?
Матье

47

Хорошо, я, наверное, опаздываю на вечеринку, НО ...

НЕ НУЖНО ИЗМЕНИТЬ КОЛОННУ В ВАШЕМ ДЕЛЕ!

Postgres, в отличие от некоторых других баз данных, достаточно умен, чтобы использовать только достаточно места для размещения строки (даже используя сжатие для более длинных строк), поэтому даже если ваш столбец объявлен как VARCHAR (255) - если вы храните строки из 40 символов в столбец, использование пространства будет 40 байтов + 1 байт служебных данных.

Требование к памяти для короткой строки (до 126 байт) составляет 1 байт плюс фактическая строка, которая включает пробел в случае символа. Более длинные строки имеют 4 байта служебной информации вместо 1. Длинные строки сжимаются системой автоматически, поэтому физические требования к диску могут быть меньше. Очень длинные значения также хранятся в фоновых таблицах, чтобы они не мешали быстрому доступу к более коротким значениям столбцов.

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

Спецификация размера в VARCHAR используется только для проверки размера вставляемых значений, она не влияет на структуру диска. Фактически, поля VARCHAR и TEXT хранятся в Postgres одинаково .


8
Никогда не поздно добавить больше информации о «почему»! Спасибо за всю эту информацию
Labynocle

Иногда вам нужно быть последовательным в структуре вашей базы данных. Даже если 2 столбца не имеют отношения, они могут иметь отношение с точки зрения концепции, например, проверить модель EAV.
Александр

37

Я столкнулся с той же проблемой, пытаясь обрезать VARCHAR с 32 до 8 и получить ERROR: value too long for type character varying(8). Я хочу оставаться как можно ближе к SQL, потому что я использую самодельную JPA-подобную структуру, которую нам, возможно, придется переключаться на разные СУБД в соответствии с выбором клиента (PostgreSQL является стандартной). Следовательно, я не хочу использовать хитрость изменения системных таблиц.

Я закончил, используя USINGутверждение в ALTER TABLE:

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

Как отметил @raylu, ALTERполучает эксклюзивную блокировку таблицы, поэтому все другие операции будут отложены до его завершения.


2
ALTERприобретает исключительную блокировку на столе и предотвращает все остальные операции
Raylu

8

Добавление нового столбца и замена нового на старый работал для меня, на redshift postgresql, обратитесь к этой ссылке для более подробной информации 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

Вот кеш страницы, описанной Грегом Смитом. В случае, если это также умирает, оператор alter выглядит так:

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

Если ваша таблица - TABLE1, столбец - COL1, и вы хотите установить для него 35 символов (+4 требуется для унаследованных целей в соответствии со ссылкой, возможно, это служебная информация, указанная AH в комментариях).


7

если вы введете alter в транзакцию, таблица не должна быть заблокирована:

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

это сработало у меня быстро, несколько секунд на столе с более чем 400 000 строк.


5
Почему вы ожидаете, что явная оболочка транзакции изменит поведение блокировки ALTERоператора? Это не так.
Эрвин Брандштеттер,

Попробуйте сами, с и без оболочки транзакций, вы заметите огромную разницу.
Джектрейд

2
Ваш ответ неверен по принципу. Любой оператор DDL без явной оболочки транзакций выполняется внутри транзакции неявно. Единственный возможный эффект явной транзакции состоит в том, что блокировки сохраняются дольше - до явной COMMIT. Оболочка имеет смысл, только если вы хотите поместить больше команд в одну транзакцию.
Эрвин Брандштеттер

Вы совершенно правы, но я настаиваю: попробуй, продолжай. а затем спросите, почему не работает таким же образом.
Jacktrade

Не помогло на Postgres 9.3.
Нумен

1

Я нашел очень простой способ изменить размер, то есть аннотацию @Size (min = 1, max = 50), которая является частью «import javax.validation.constraints», то есть «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),

.....

)

Спасибо за ваш пост! Пожалуйста, не используйте подписи / слоганы в своих сообщениях. Ваш ящик пользователя считается вашей подписью, и вы можете использовать свой профиль, чтобы публиковать любую информацию о себе, которая вам нравится. FAQ по подписям / слоганам
Эндрю Барбер

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.