Postgresql - зміна розміру стовпчика варшара на меншу довжину


153

У мене питання щодо 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це не змусить стіл займати менше місця?
AH

навіть у моєму випадку? Я маю на увазі, що стовпець матиме максимальний розмір 40 знаків (тобто октетів) замість 255?
Лабінокл

16
Якщо ви скажете varchar(255)на PostgreSQL, він не виділить 255 байт для значення, реальна довжина якого становить 40 байт. Він виділить 40 байт (плюс деякі внутрішні накладні витрати). Єдине, що буде be changed by the АЛЬТЕР ТАБЛИЦЯ - це максимальна кількість байтів, які ви можете зберігати в цьому стовпці, не отримуючи помилки від PG.
AH


Ознайомтеся з відповіддю на оновлення dba.stackexchange.com/questions/189890/…
Еван Керролл

Відповіді:


73

Опис того, як це зробити, можна змінити в розмірі стовпця в таблиці PostgreSQL без зміни даних . Ви повинні зламати дані каталогу баз даних. Єдиний спосіб зробити це офіційно - це ALTER TABLE, і, як ви зазначили, що зміна заблокує та перепише всю таблицю під час її запуску.

Переконайтесь, що ви прочитали розділ Типи символів у документах, перш ніж змінювати це. Тут слід пам’ятати про всілякі дивні випадки. Перевірка довжини проводиться, коли значення зберігаються у рядках. Якщо ви зламаєте нижню межу там, це зовсім не зменшить розмір існуючих значень. Було б розумно зробити сканування по всій таблиці, шукаючи рядки, де довжина поля> 40 символів після внесення змін. Вам потрібно буде з’ясувати, як обрізати їх вручну - щоб ви повернули деякі замки лише на великих розмірах - тому що, якщо хтось намагатиметься оновити що-небудь на цьому рядку, він зараз відкине його як занадто великий. він зберігається в новій версії рядка. Для користувача виникає веселість.

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


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

6
Зміна довжини вархара не переписує таблицю. Він просто перевіряє довжину обмеження щодо всієї таблиці точно так само, як ЗАВЕРШИТИ ЗБУД. Якщо ви збільшуєте довжину, нічого не робити, просто наступна вставка або оновлення приймуть більшу довжину. Якщо ви зменшите довжину, і всі рядки пройдуть нове менше обмеження, Pg не вживатиме жодних подальших дій, крім того, щоб дозволити наступним вставкам або оновленням писати лише нову довжину.
Маньєро

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

12
Посилання тепер мертве.
raarts

Для отримання додаткової інформації про те, як це працює, перегляньте 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, якщо один із кількох рядків не містить значення, яке перевищує новий розмір.
Повідомте

@Tell, цікаво. Чи означає це, що 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

Іноді вам потрібно бути послідовними у структурі вашої бази даних. Навіть якщо два стовпці не мають відношення, вони можуть мати відношення з точки зору концепції, наприклад замовити модель EAV.
Олександр

36

Я зіткнувся з тією самою проблемою, намагаючись урізати 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

Додаючи новий стовпець і замінюючи новий на старий, який працював для мене, на червоному зміні 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. Обгортка має сенс лише в тому випадку, якщо ви хочете ввести більше команд в одну транзакцію.
Ервін Брандштеттер

ви абсолютно праві, але я наполягаю: спробуйте себе, продовжуйте. а потім запитайте, чому не працює так само.
джек-трейд

Не допомогло на 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.