Postgresql - thay đổi kích thước của cột varchar thành chiều dài thấp hơn


153

Tôi có một câu hỏi về ALTER TABLElệnh trên một bảng thực sự lớn (gần 30 triệu hàng). Một trong những cột của nó là a varchar(255)và tôi muốn thay đổi kích thước của nó thành a varchar(40). Về cơ bản, tôi muốn thay đổi cột của mình bằng cách chạy lệnh sau:

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

Tôi không có vấn đề gì nếu quá trình này rất dài nhưng có vẻ như bảng của tôi không thể đọc được nhiều hơn trong lệnh ALTER TABLE. Có cách nào thông minh hơn không? Có thể thêm một cột mới, sao chép các giá trị từ cột cũ, bỏ cột cũ và cuối cùng đổi tên cột mới?

Bất kỳ đầu mối sẽ được đánh giá rất cao! Cảm ơn trước,

Lưu ý: Tôi sử dụng PostgreSQL 9.0.


11
Chỉ cần rõ ràng: Bạn biết đấy, điều đó resizingsẽ không làm cho bảng chiếm ít không gian hơn?
AH

ngay cả trong trường hợp của tôi? Ý tôi là cột sẽ có kích thước tối đa 40 char (vì vậy octet) thay vì 255?
Mê cung

16
Nếu bạn nói varchar(255)với PostgreSQL thì nó sẽ không phân bổ 255 byte cho một giá trị có độ dài thực là 40 byte. Nó sẽ phân bổ 40 byte (cộng với một số chi phí nội bộ). Điều duy nhất sẽ be changed by the THAY ĐỔI TABLE` là số byte tối đa bạn có thể lưu trữ trong cột đó mà không gặp lỗi từ PG.
AH


Kiểm tra câu trả lời ở đây để cập nhật dba.stackexchange.com/questions/189890/iêu
Evan Carroll

Câu trả lời:


73

Có một mô tả về cách thực hiện việc này tại Thay đổi kích thước cột trong bảng PostgreSQL mà không thay đổi dữ liệu . Bạn phải hack dữ liệu danh mục cơ sở dữ liệu. Cách duy nhất để thực hiện điều này một cách chính thức là với ALTER TABLE, và như bạn đã lưu ý rằng thay đổi sẽ khóa và viết lại toàn bộ bảng trong khi nó đang chạy.

Hãy chắc chắn rằng bạn đã đọc phần Kiểu ký tự của các tài liệu trước khi thay đổi điều này. Tất cả các loại trường hợp kỳ lạ để nhận thức được ở đây. Việc kiểm tra độ dài được thực hiện khi các giá trị được lưu trữ vào các hàng. Nếu bạn hack một giới hạn thấp hơn trong đó, điều đó sẽ không làm giảm kích thước của các giá trị hiện có. Bạn sẽ khôn ngoan khi quét toàn bộ bảng để tìm các hàng trong đó độ dài của trường> 40 ký tự sau khi thực hiện thay đổi. Bạn sẽ cần tìm ra cách cắt xén những cái đó một cách thủ công - vì vậy, bạn sẽ quay lại một số khóa chỉ trên những cái quá khổ - bởi vì nếu ai đó cố gắng cập nhật bất cứ thứ gì trên hàng đó, thì bây giờ sẽ từ chối nó quá lớn nó đi để lưu trữ phiên bản mới của hàng. Vui nhộn xảy ra sau đó cho người dùng.

VARCHAR là một loại khủng khiếp tồn tại trong PostgreSQL chỉ để tuân thủ phần khủng khiếp liên quan đến tiêu chuẩn SQL. Nếu bạn không quan tâm đến khả năng tương thích đa cơ sở dữ liệu, hãy xem xét việc lưu trữ dữ liệu của bạn dưới dạng văn bản và thêm một ràng buộc để giới hạn độ dài của nó. Các ràng buộc bạn có thể thay đổi xung quanh mà không có vấn đề khóa / ghi lại bảng này và chúng có thể thực hiện kiểm tra tính toàn vẹn nhiều hơn là chỉ kiểm tra độ dài yếu.


Cảm ơn bạn đã trả lời. Tôi sẽ kiểm tra liên kết của bạn. Tôi không lo lắng về việc kiểm tra kích thước thủ công vì tất cả nội dung của tôi có kích thước tối đa là 40 ký tự. Tôi cần đọc thêm về ràng buộc trên văn bản vì tôi tin rằng VARCHAR tốt hơn để kiểm tra lentgh :)
Labynocle

6
Thay đổi độ dài varchar không viết lại bảng. Nó chỉ kiểm tra độ dài ràng buộc đối với toàn bộ bảng chính xác như KIỂM TRA CONSTRAINT. Nếu bạn tăng chiều dài thì không có gì để làm, chỉ cần chèn hoặc cập nhật tiếp theo sẽ chấp nhận độ dài lớn hơn. Nếu bạn giảm độ dài và tất cả các hàng vượt qua ràng buộc nhỏ hơn mới, PG sẽ không thực hiện thêm bất kỳ hành động nào bên cạnh việc cho phép các lần chèn hoặc cập nhật tiếp theo chỉ viết độ dài mới.
Maniero

3
@bigown, chỉ cần làm rõ, tuyên bố của bạn chỉ đúng với PostgreQuery 9.2+ , không phải là những câu hỏi cũ.
MatheusOl

12
Liên kết bây giờ đã chết.
raarts

Để biết thêm thông tin về cách thức hoạt động này, hãy xem dba.stackexchange.com/questions/189890/iêu
Evan Carroll

100

Trong PostgreSQL 9.1 có một cách dễ dàng hơn

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
Lưu ý rằng nó chỉ hoạt động vì bạn chỉ định kích thước lớn hơn (30> 10). Nếu kích thước nhỏ hơn, bạn sẽ gặp lỗi tương tự như tôi đã có .
Matthieu

2
Postgres không được đưa ra lỗi nếu bạn hạ thấp kích thước varchar thông qua truy vấn ALTER TABLE trừ khi một trong nhiều hàng chứa giá trị vượt quá kích thước mới.
Nói với

@Tell, thú vị. Điều đó có nghĩa là Postgres thực hiện quét toàn bộ bảng hoặc bằng cách nào đó giữ kích thước tối đa trong thống kê của nó?
Matthieu

47

Ok, có lẽ tôi đến bữa tiệc muộn, NHƯNG ...

KHÔNG CẦN PHẢI GIẢI QUYẾT MÀU SẮC TRONG TRƯỜNG HỢP CỦA BẠN!

Postgres, không giống như một số cơ sở dữ liệu khác, đủ thông minh để chỉ sử dụng đủ không gian để vừa với chuỗi (thậm chí sử dụng nén cho chuỗi dài hơn), vì vậy ngay cả khi cột của bạn được khai báo là VARCHAR (255) - nếu bạn lưu trữ chuỗi 40 ký tự cột, không gian sử dụng sẽ là 40 byte + 1 byte trên không.

Yêu cầu lưu trữ cho một chuỗi ngắn (tối đa 126 byte) là 1 byte cộng với chuỗi thực tế, bao gồm phần đệm không gian trong trường hợp ký tự. Chuỗi dài hơn có 4 byte phí thay vì 1. Chuỗi dài được hệ thống nén tự động, do đó, yêu cầu vật lý trên đĩa có thể ít hơn. Các giá trị rất dài cũng được lưu trữ trong các bảng nền để chúng không cản trở việc truy cập nhanh vào các giá trị cột ngắn hơn.

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

Đặc tả kích thước trong VARCHAR chỉ được sử dụng để kiểm tra kích thước của các giá trị được chèn, nó không ảnh hưởng đến bố cục đĩa. Trong thực tế, các trường VARCHAR và TEXT được lưu trữ theo cùng một cách trong Postgres .


8
Không bao giờ quá muộn để thêm thông tin về "tại sao"! Cảm ơn bạn vì tất cả thông tin này
Labynocle

Đôi khi bạn cần nhất quán trong cấu trúc cơ sở dữ liệu của bạn. Ngay cả khi 2 cột không có mối quan hệ, chúng có thể có mối quan hệ theo quan điểm của khái niệm, ví dụ: kiểm tra EAV mô hình.
Alexandre

36

Tôi đã phải đối mặt với cùng một vấn đề khi cố gắng cắt bớt VARCHAR từ 32 xuống 8 và nhận được ERROR: value too long for type character varying(8). Tôi muốn ở gần SQL nhất có thể vì tôi đang sử dụng cấu trúc giống như JPA tự tạo mà chúng tôi có thể phải chuyển sang các DBMS khác nhau theo lựa chọn của khách hàng (PostgreQuery là cấu trúc mặc định). Do đó, tôi không muốn sử dụng thủ thuật thay đổi bảng Hệ thống.

Tôi đã kết thúc bằng cách sử dụng USINGcâu lệnh trong ALTER TABLE:

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

Như @raylu đã lưu ý, ALTERcó được một khóa độc quyền trên bàn để tất cả các hoạt động khác sẽ bị trì hoãn cho đến khi hoàn thành.


2
việc ALTERmua một khóa độc quyền trên bàn và ngăn chặn tất cả các hoạt động khác
raylu

8

Thêm cột mới và thay thế cột mới bằng cũ đã giúp tôi, trên redgift postgresql, hãy tham khảo liên kết này để biết thêm chi tiết 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

Đây là bộ nhớ cache của trang được mô tả bởi Greg Smith. Trong trường hợp cũng chết, câu lệnh thay đổi trông như thế này:

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

Trong đó bảng của bạn là TABLE1, cột là COL1 và bạn muốn đặt nó thành 35 ký tự (+4 là cần thiết cho mục đích kế thừa theo liên kết, có thể là chi phí được đề cập bởi AH trong các bình luận).


7

nếu bạn đặt các thay đổi vào một giao dịch, bảng sẽ không bị khóa:

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

điều này làm việc cho tôi rất nhanh, vài giây trên một cái bàn với hơn 400 nghìn hàng.


5
Tại sao bạn lại mong đợi trình bao bọc giao dịch rõ ràng thay đổi hành vi khóa của ALTERcâu lệnh? Nó không.
Erwin Brandstetter

Hãy thử, với và không có trình bao bọc giao dịch, bạn sẽ nhận thấy một sự khác biệt rất lớn.
jacktrade

2
Câu trả lời của bạn không chính xác về hiệu trưởng. Bất kỳ câu lệnh DDL nào không có trình bao bọc giao dịch rõ ràng đều chạy bên trong một giao dịch ngầm. Tác động duy nhất có thể có của giao dịch rõ ràng là các khóa được giữ lâu hơn - cho đến khi rõ ràng COMMIT. Trình bao bọc chỉ có ý nghĩa nếu bạn muốn đặt nhiều lệnh hơn vào cùng một giao dịch.
Erwin Brandstetter

bạn hoàn toàn đúng, nhưng tôi khẳng định: hãy cố gắng, tiếp tục. và sau đó hỏi tại sao không hoạt động theo cùng một cách.
jacktrade

Không giúp đỡ trên Postgres 9.3.
Noumenon

1

Tôi đã tìm thấy một cách rất dễ dàng để thay đổi kích thước, ví dụ: chú thích @Size (min = 1, max = 50) là một phần của "nhập javax.validation.constraint" tức là "nhập javax.validation.constraint.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),

.....

)

Cảm ơn bài viết của bạn! Vui lòng không sử dụng chữ ký / khẩu hiệu trong bài viết của bạn. Hộp người dùng của bạn được tính là chữ ký của bạn và bạn có thể sử dụng hồ sơ của mình để đăng bất kỳ thông tin nào về bản thân bạn muốn. Câu hỏi thường gặp về chữ ký / khẩu hiệu
Andrew Barber

0

Hãy thử chạy bảng thay đổi sau:

ALTER TABLE public.users 
ALTER COLUMN "password" TYPE varchar(300) 
USING "password"::varchar;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.