Postgresql - เปลี่ยนขนาดของคอลัมน์ varchar ให้มีความยาวต่ำกว่า


154

ฉันมีคำถามเกี่ยวกับALTER TABLEคำสั่งบนตารางที่มีขนาดใหญ่มาก (เกือบ 30 ล้านแถว) หนึ่งในคอลัมน์ที่เป็นและฉันต้องการที่จะปรับขนาดไปvarchar(255) varchar(40)โดยทั่วไปฉันต้องการเปลี่ยนคอลัมน์ของฉันโดยเรียกใช้คำสั่งต่อไปนี้:

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

ฉันไม่มีปัญหาหากกระบวนการมีความยาวมาก แต่ดูเหมือนว่าตารางของฉันจะไม่สามารถอ่านได้มากขึ้นในระหว่างคำสั่ง ALTER TABLE มีวิธีที่ฉลาดกว่านี้ไหม? อาจเพิ่มคอลัมน์ใหม่คัดลอกค่าจากคอลัมน์เก่าวางคอลัมน์เก่าแล้วเปลี่ยนชื่อใหม่ในที่สุด?

เบาะแสใด ๆ จะได้รับการชื่นชมอย่างมาก! ขอบคุณล่วงหน้า,

หมายเหตุ: ฉันใช้ PostgreSQL 9.0


11
เพื่อให้ชัดเจน: คุณรู้ไหมว่าresizingจะไม่ทำให้ตารางใช้พื้นที่น้อยลง?
AH

แม้ในกรณีของฉันฉันหมายถึงคอลัมน์จะมีขนาดสูงสุด 40 อักขระ (ดังนั้น octets) แทนที่จะเป็น 255
Labynocle

16
ถ้าคุณพูดvarchar(255)กับ PostgreSQL มันจะไม่จัดสรร 255 ไบต์สำหรับค่าที่ความยาวจริงคือ 40 ไบต์ จะจัดสรร 40 ไบต์ (บวกค่าใช้จ่ายภายในบางส่วน) สิ่งเดียวที่จะbe changed by the แก้ไข TABLE` คือจำนวนไบต์สูงสุดที่คุณสามารถเก็บไว้ในคอลัมน์นั้นโดยไม่ได้รับข้อผิดพลาดจาก PG
AH

เกี่ยวกับค่าใช้จ่าย AH กล่าวถึง: ค่าใช้จ่ายสำหรับ varchar (n) คืออะไร?
Erwin Brandstetter

ตรวจสอบคำตอบได้ที่นี่สำหรับการอัพเดทdba.stackexchange.com/questions/189890/…
Evan Carroll

คำตอบ:


73

มีรายละเอียดของวิธีการทำเช่นนี้เป็นที่ปรับขนาดคอลัมน์ในตาราง PostgreSQL โดยไม่ต้องเปลี่ยนข้อมูล คุณต้องแฮ็คข้อมูลแคตาล็อกฐานข้อมูล วิธีเดียวที่จะทำเช่นนี้อย่างเป็นทางการคือใช้ ALTER TABLE และดังที่คุณได้บันทึกไว้ว่าการเปลี่ยนแปลงจะล็อคและเขียนทั้งตารางใหม่ในขณะที่ทำงานอยู่

ตรวจสอบให้แน่ใจว่าคุณอ่านส่วนประเภทอักขระของเอกสารก่อนเปลี่ยน ทุกกรณีแปลก ๆ ที่ต้องระวังที่นี่ การตรวจสอบความยาวจะกระทำเมื่อเก็บค่าไว้ในแถว หากคุณแฮ็กขีด จำกัด ที่ต่ำกว่าในนั้นนั่นจะไม่ลดขนาดของค่าที่มีอยู่เลย คุณควรทำการสแกนทั่วทั้งตารางเพื่อหาแถวที่ความยาวของฟิลด์คือ> 40 ตัวอักษรหลังจากทำการเปลี่ยนแปลง คุณจะต้องหาวิธีตัดทอนเหล่านั้นด้วยตัวเองดังนั้นคุณจะต้องล็อคบางส่วนไว้บนตัวที่มีขนาดใหญ่เกินไปเพราะถ้ามีคนพยายามอัพเดทอะไรในแถวนั้นมันจะปฏิเสธมันใหญ่เกินไปตอนนี้ มันจะไปเก็บรุ่นใหม่ของแถว Hilarity เป็นตัวเลือกสำหรับผู้ใช้

VARCHAR เป็นประเภทที่แย่มากที่มีอยู่ใน PostgreSQL เพื่อให้สอดคล้องกับส่วนที่แย่มากของมาตรฐาน SQL หากคุณไม่สนใจความเข้ากันได้ของหลายฐานข้อมูลให้พิจารณาจัดเก็บข้อมูลของคุณเป็น TEXT และเพิ่มข้อ จำกัด เพื่อจำกัดความยาวของข้อมูล ข้อ จำกัด ที่คุณสามารถเปลี่ยนแปลงได้โดยไม่มีปัญหาการล็อค / เขียนใหม่ของตารางนี้และพวกเขาสามารถทำการตรวจสอบความสมบูรณ์มากกว่าการตรวจสอบความยาวที่อ่อนแอ


ขอบคุณสำหรับคำตอบ ฉันจะตรวจสอบลิงก์ของคุณ ฉันไม่กังวลเกี่ยวกับการตรวจสอบขนาดด้วยตนเองเนื่องจากเนื้อหาทั้งหมดของฉันมีขนาดสูงสุด 40 ตัวอักษร ฉันจำเป็นต้องอ่านรายละเอียดเพิ่มเติมเกี่ยวกับข้อ จำกัด เกี่ยวกับข้อความเพราะผมเชื่อว่า VARCHAR เป็นเรื่องที่ดีที่จะตรวจสอบ lentgh :)
Labynocle

6
เปลี่ยนความยาว varchar ไม่ได้เขียนตาราง เพียงแค่ตรวจสอบความยาวข้อ จำกัด เทียบกับตารางทั้งหมดเหมือนกับ CHECK CONSTRAINT หากคุณเพิ่มความยาวไม่ต้องทำอะไรแทรกเพียงครั้งต่อไปหรือการปรับปรุงจะยอมรับความยาวที่ใหญ่กว่า หากคุณลดความยาวและแถวทั้งหมดผ่านข้อ จำกัด เล็ก ๆ ใหม่ Pg จะไม่ดำเนินการใด ๆ เพิ่มเติมนอกเหนือจากที่จะอนุญาตให้ส่วนแทรกหรืออัพเดตถัดไปเขียนเฉพาะความยาวใหม่
Maniero

3
@bigown เพียงชี้แจงเท่านั้นแถลงการณ์ของคุณเป็นจริงสำหรับ PostgreSQL 9.2+เท่านั้นไม่ใช่แบบเก่า
MatheusOl

12
ลิงค์นี้ตายแล้ว
raarts

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการทำงานตรวจสอบdba.stackexchange.com/questions/189890/…
Evan Carroll

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) ถ้ามีขนาดที่เล็กกว่าคุณจะได้รับข้อผิดพลาดเดียวกันกว่าผม
Matthieu

2
Postgres ไม่ควรทิ้งข้อผิดพลาดหากคุณลดขนาด varchar ผ่านทางแบบสอบถาม ALTER TABLE เว้นแต่ว่าแถวใดแถวหนึ่งมีค่าที่เกินขนาดใหม่
บอก

@ บอกว่าน่าสนใจ นั่นหมายความว่า Postgres ทำการสแกนแบบเต็มตารางหรือจะเก็บขนาดสูงสุดไว้ในสถิติหรือไม่?
Matthieu

47

ตกลงฉันอาจมาสายไปงานเลี้ยง แต่ ...

ไม่มีความจำเป็นต้องปรับขนาดคอลัมน์ในกระเป๋าของคุณ!

Postgres ซึ่งแตกต่างจากฐานข้อมูลอื่นคือฉลาดพอที่จะใช้พื้นที่เพียงพอเพื่อให้พอดีกับสตริงเท่านั้น (แม้ใช้การบีบอัดสำหรับสตริงที่ยาวกว่า) ดังนั้นแม้ว่าคอลัมน์ของคุณจะประกาศเป็น VARCHAR (255) - ถ้าคุณเก็บสตริง 40 อักขระใน คอลัมน์การใช้พื้นที่จะเป็น 40 ไบต์ + 1 ไบต์ของค่าใช้จ่าย

ความต้องการพื้นที่เก็บข้อมูลสำหรับสตริงสั้น (สูงสุด 126 ไบต์) คือ 1 ไบต์บวกกับสตริงจริงซึ่งรวมถึงช่องว่างภายในในกรณีของอักขระ สตริงที่ยาวกว่ามีค่าใช้จ่าย 4 ไบต์แทนที่จะเป็น 1 สตริงที่ยาวจะถูกบีบอัดโดยระบบโดยอัตโนมัติดังนั้นความต้องการทางกายภาพบนดิสก์อาจน้อยกว่า ค่าที่ยาวมากจะถูกเก็บไว้ในตารางพื้นหลังเพื่อไม่ให้รบกวนการเข้าถึงค่าคอลัมน์ที่สั้นกว่าอย่างรวดเร็ว

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

ข้อมูลจำเพาะขนาดใน VARCHAR ใช้เพื่อตรวจสอบขนาดของค่าที่ใส่เข้าไปเท่านั้นซึ่งจะไม่มีผลกับเค้าโครงของดิสก์ ในความเป็นจริงVARCHAR และข้อความเขตข้อมูลจะถูกเก็บไว้ในลักษณะเดียวกันใน Postgres


8
ไม่สายเกินไปที่จะเพิ่มข้อมูลเพิ่มเติมเกี่ยวกับ "ทำไม"! ขอบคุณสำหรับข้อมูลทั้งหมดนี้
Labynocle

บางครั้งคุณต้องมีความสอดคล้องในโครงสร้างของฐานข้อมูลของคุณ แม้ว่าคอลัมน์ 2 คอลัมน์ไม่มีความสัมพันธ์กันพวกเขาสามารถมีความสัมพันธ์ในมุมมองแนวคิดเช่นเช็คเอาต์รุ่น EAV
Alexandre

37

ผมก็ประสบปัญหาเดียวกันพยายามที่จะตัดทอน VARCHAR 32-8 ERROR: value too long for type character varying(8)และได้รับ ฉันต้องการอยู่ใกล้กับ SQL ให้มากที่สุดเพราะฉันใช้โครงสร้างแบบ JPA ที่สร้างขึ้นเองซึ่งเราอาจต้องเปลี่ยนไปใช้ DBMS ที่แตกต่างกันตามตัวเลือกของลูกค้า (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

นี่คือแคชของหน้าที่อธิบายโดย Greg Smith ในกรณีที่ตายเช่นกันคำสั่ง alter จะมีลักษณะดังนี้:

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

ที่ตารางของคุณคือ TABLE1 คอลัมน์คือ COL1 และคุณต้องการตั้งค่าเป็น 35 ตัวอักษร (จำเป็นต้องมี +4 เพื่อวัตถุประสงค์ดั้งเดิมตามลิงก์อาจเป็นค่าใช้จ่ายที่ AH อ้างอิงในความคิดเห็น)


7

หากคุณนำการเปลี่ยนแปลงไปใช้ในการทำธุรกรรมตารางไม่ควรถูกล็อก

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

สิ่งนี้ได้ผลสำหรับฉันที่เห็นได้อย่างรวดเร็วไม่กี่วินาทีบนโต๊ะที่มีแถวมากกว่า 400k


5
ทำไมคุณถึงคาดหวังว่าผู้ห่อธุรกรรมที่ชัดเจนจะเปลี่ยนพฤติกรรมการล็อคของALTERคำสั่ง? มันไม่ได้
Erwin Brandstetter

ลองด้วยตัวคุณเองโดยมีและไม่มี wrapper การทำธุรกรรมคุณจะสังเกตเห็นความแตกต่างอย่างมาก
jacktrade

2
คำตอบของคุณไม่ถูกต้องในหลักการ คำสั่ง DDL ใด ๆ ที่ไม่มี wrapper การทำธุรกรรมที่ชัดเจนจะทำงานภายในการทำธุรกรรมโดยปริยาย ผลกระทบที่เป็นไปได้เพียงหนึ่งเดียวของการทำธุรกรรมอย่างชัดเจนก็คือว่าล็อคจะถูกเก็บไว้อีกต่อไป - COMMITจนกว่าชัดเจน wrapper นั้นเหมาะสมถ้าคุณต้องการเพิ่มคำสั่งลงในรายการเดียวกัน
Erwin Brandstetter

คุณพูดถูก แต่ฉันขอยืนยัน: ลองด้วยตัวคุณเองต่อไป แล้วถามว่าทำไมไม่ทำงานในวิธีเดียวกัน
jacktrade

ไม่ได้ช่วย Postgres 9.3
Noumenon

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

.....

)

ขอบคุณสำหรับการโพสต์ของคุณ! กรุณาอย่าใช้ลายเซ็น / แท็กไลน์ในโพสต์ของคุณ กล่องผู้ใช้ของคุณนับเป็นลายเซ็นของคุณและคุณสามารถใช้โปรไฟล์ของคุณเพื่อโพสต์ข้อมูลใด ๆ เกี่ยวกับตัวคุณที่คุณชอบ คำถามที่พบบ่อยเกี่ยวกับลายเซ็น / แท็กไลน์
Andrew Barber เมื่อ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.