Postgresql - ubah ukuran kolom varchar ke panjang yang lebih rendah

154

Saya punya pertanyaan tentang ALTER TABLEperintah di atas meja yang sangat besar (hampir 30 juta baris). Salah satu kolomnya adalah a varchar(255)dan saya ingin mengubah ukurannya menjadi a varchar(40). Pada dasarnya, saya ingin mengubah kolom saya dengan menjalankan perintah berikut:

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

Saya tidak punya masalah jika prosesnya sangat lama tetapi sepertinya meja saya tidak lagi dapat dibaca selama perintah ALTER TABLE. Apakah ada cara yang lebih pintar? Mungkin menambahkan kolom baru, menyalin nilai dari kolom lama, menjatuhkan kolom lama dan akhirnya mengganti nama yang baru?

Petunjuk apa pun akan sangat dihargai! Terima kasih sebelumnya,

Catatan: Saya menggunakan PostgreSQL 9.0.

Labynocle
sumber
11
Hanya untuk menjadi jelas: Anda tahu, itu resizingtidak akan membuat meja menempati ruang lebih sedikit?
AH
bahkan dalam kasus saya? Maksudku kolom akan memiliki ukuran maksimal 40 char (jadi oktet) bukan 255?
Labynocle
16
Jika Anda mengatakan varchar(255)kepada PostgreSQL maka itu tidak akan mengalokasikan 255 byte untuk nilai yang panjang sebenarnya adalah 40 byte. Ini akan mengalokasikan 40 byte (ditambah beberapa overhead internal). Satu-satunya hal yang akan be changed by the ALTER TABLE` adalah jumlah byte maksimum yang dapat Anda simpan di kolom itu tanpa mendapatkan kesalahan dari PG.
AH
Tentang overhead AH disebutkan: Apa overhead untuk varchar (n)?
Erwin Brandstetter
Lihatlah jawabannya di sini untuk pembaruan dba.stackexchange.com/questions/189890/…
Evan Carroll

Jawaban:

73

Ada deskripsi tentang bagaimana melakukan ini di Ubah ukuran kolom dalam tabel PostgreSQL tanpa mengubah data . Anda harus meretas data katalog basis data. Satu-satunya cara untuk melakukan ini secara resmi adalah dengan ALTER TABLE, dan seperti yang Anda perhatikan bahwa perubahan akan mengunci dan menulis ulang seluruh tabel saat sedang berjalan.

Pastikan Anda membaca bagian Jenis Karakter dari dokumen sebelum mengubah ini. Segala macam kasus aneh yang harus diperhatikan di sini. Pemeriksaan panjang dilakukan ketika nilai disimpan ke dalam baris. Jika Anda meretas batas bawah di sana, itu tidak akan mengurangi ukuran nilai yang ada sama sekali. Anda sebaiknya melakukan pemindaian pada seluruh tabel untuk mencari baris yang panjang bidangnya> 40 karakter setelah melakukan perubahan. Anda harus mencari cara untuk memotongnya secara manual - jadi Anda kembali beberapa kunci hanya pada yang kebesaran - karena jika seseorang mencoba memperbarui apa pun di baris itu akan menolaknya terlalu besar sekarang, pada titik ia pergi untuk menyimpan versi baris baru. Hilaritas terjadi untuk pengguna.

VARCHAR adalah tipe mengerikan yang ada di PostgreSQL hanya untuk memenuhi bagian mengerikan yang terkait dengan standar SQL. Jika Anda tidak peduli dengan kompatibilitas multi-basis data, pertimbangkan untuk menyimpan data Anda sebagai TEXT dan tambahkan batasan untuk membatasi panjangnya. Kendala yang bisa Anda ubah tanpa masalah penguncian / penulisan tabel ini, dan mereka bisa melakukan pemeriksaan integritas lebih dari sekadar pemeriksaan panjang yang lemah.

Greg Smith
sumber
Terima kasih atas jawabannya. Saya akan memeriksa tautan Anda. Saya tidak khawatir tentang pemeriksaan ukuran manual karena semua konten saya memiliki ukuran maksimal 40 karakter. Saya perlu membaca lebih lanjut tentang batasan pada TEXT karena saya percaya bahwa VARCHAR lebih baik untuk memeriksa lentgh :)
Labynocle
6
Ubah panjang varchar tidak menulis ulang tabel. Itu hanya memeriksa panjang kendala terhadap seluruh tabel persis seperti PERIKSA KONSTRAINT. Jika Anda menambah panjang tidak ada yang bisa dilakukan, masukkan saja berikutnya atau pembaruan akan menerima panjang lebih besar. Jika Anda mengurangi panjang dan semua baris melewati batasan baru yang lebih kecil, Pg tidak mengambil tindakan lebih lanjut selain untuk membiarkan sisipan atau pembaruan berikutnya hanya menulis panjang baru.
Maniero
3
@Bigown, hanya untuk memperjelas, pernyataan Anda hanya berlaku untuk PostgreSQL 9.2+ , bukan yang lama.
MatheusOl
12
Tautan sekarang mati.
raarts
Untuk informasi lebih lanjut tentang cara kerjanya, periksa dba.stackexchange.com/questions/189890/…
Evan Carroll
100

Di PostgreSQL 9.1 ada cara yang lebih mudah

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
sumber
6
Perhatikan bahwa ini hanya berfungsi karena Anda menentukan ukuran yang lebih besar (30> 10). Jika ukurannya lebih kecil, Anda akan mendapatkan kesalahan yang sama dari yang saya miliki .
Matthieu
2
Postgres tidak boleh melempar kesalahan jika Anda menurunkan ukuran varchar melalui kueri ALTER TABLE kecuali satu baris lagi berisi nilai yang melebihi ukuran baru.
Katakan
@Beritahu, menarik. Apakah itu berarti Postgres melakukan pemindaian penuh tabel, atau entah bagaimana menjaga ukuran maksimal dalam statistiknya?
Matthieu
47

Ok, saya mungkin terlambat ke pesta, TAPI ...

TIDAK PERLU MENGUBAH KOLOM DALAM KASUS ANDA!

Postgres, tidak seperti beberapa database lain, cukup pintar untuk hanya menggunakan ruang yang cukup agar sesuai dengan string (bahkan menggunakan kompresi untuk string yang lebih lama), jadi bahkan jika kolom Anda dinyatakan sebagai VARCHAR (255) - jika Anda menyimpan string 40-karakter dalam kolom, penggunaan ruang akan menjadi 40 byte + 1 byte overhead.

Persyaratan penyimpanan untuk string pendek (hingga 126 byte) adalah 1 byte plus string yang sebenarnya, yang mencakup ruang bantalan dalam kasus karakter. String yang lebih panjang memiliki 4 byte overhead daripada 1. String yang panjang dikompresi oleh sistem secara otomatis, sehingga persyaratan fisik pada disk mungkin kurang. Nilai yang sangat panjang juga disimpan dalam tabel latar belakang sehingga tidak mengganggu akses cepat ke nilai kolom yang lebih pendek.

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

Spesifikasi ukuran dalam VARCHAR hanya digunakan untuk memeriksa ukuran nilai yang dimasukkan, itu tidak mempengaruhi tata letak disk. Faktanya, bidang VARCHAR dan TEXT disimpan dengan cara yang sama di Postgres .

Sergey
sumber
8
Tidak ada kata terlambat untuk menambahkan lebih banyak info tentang "mengapa"! Terima kasih atas semua informasi ini
Labynocle
Terkadang Anda harus konsisten dalam struktur basis data Anda. Bahkan jika 2 kolom tidak memiliki relasi, mereka dapat memiliki relasi dalam sudut pandang konsep, misalnya checkout model EAV.
Alexandre
37

Saya menghadapi masalah yang sama mencoba untuk memotong VARCHAR dari 32 menjadi 8 dan mendapatkan ERROR: value too long for type character varying(8). Saya ingin tetap sedekat mungkin dengan SQL karena saya menggunakan struktur mirip JPA yang dibuat sendiri sehingga kita mungkin harus beralih ke DBMS yang berbeda sesuai dengan pilihan pelanggan (PostgreSQL menjadi yang default). Oleh karena itu, saya tidak ingin menggunakan trik mengubah tabel Sistem.

Saya berakhir menggunakan USINGpernyataan di ALTER TABLE:

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

Seperti dicatat oleh @raylu, ALTERmemperoleh kunci eksklusif di atas meja sehingga semua operasi lainnya akan tertunda hingga selesai.

Matthieu
sumber
2
mereka ALTERmemperoleh kunci eksklusif di atas meja dan mencegah semua operasi lainnya
raylu
8

Menambahkan kolom baru dan mengganti yang baru dengan yang lama berfungsi untuk saya, di redshift postgresql, lihat tautan ini untuk lebih jelasnya 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;
pertengkaran
sumber
7

Ini cache halaman yang dijelaskan oleh Greg Smith. Jika itu mati juga, pernyataan alter terlihat seperti ini:

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

Di mana tabel Anda adalah TABLE1, kolomnya adalah COL1 dan Anda ingin mengaturnya menjadi 35 karakter (+4 diperlukan untuk tujuan warisan sesuai dengan tautan, mungkin overhead yang dirujuk oleh AH dalam komentar).

Tom
sumber
7

jika Anda memasukkan alter ke dalam transaksi, tabel tidak boleh dikunci:

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

ini bekerja untuk saya dengan sangat cepat, beberapa detik di atas meja dengan lebih dari 400 ribu baris.

jacktrade
sumber
5
Mengapa Anda mengharapkan pembungkus transaksi eksplisit untuk mengubah perilaku penguncian ALTERpernyataan? Tidak.
Erwin Brandstetter
coba sendiri, dengan dan tanpa pembungkus transaksi, Anda akan melihat perbedaan besar.
jacktrade
2
Jawaban Anda salah pada prinsipal. Pernyataan DDL tanpa pembungkus transaksi eksplisit berjalan di dalam transaksi secara implisit. Satu-satunya efek yang mungkin dari transaksi eksplisit adalah bahwa kunci disimpan lebih lama - hingga eksplisit COMMIT. Pembungkus hanya masuk akal jika Anda ingin memasukkan lebih banyak perintah ke dalam transaksi yang sama.
Erwin Brandstetter
Anda sepenuhnya benar, tetapi saya bersikeras: cobalah sendiri, teruskan. dan kemudian tanyakan mengapa tidak bekerja dengan cara yang sama.
jacktrade
Tidak membantu Postgres 9.3.
Noumenon
1

Saya telah menemukan cara yang sangat mudah untuk mengubah ukuran yaitu penjelasan @ Ukuran (min = 1, maks = 50) yang merupakan bagian dari "import javax.validation.constraints" yaitu "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),

.....

)
Tito
sumber
Terima kasih untuk kiriman Anda! Tolong jangan gunakan tanda tangan / tagline dalam posting Anda. Kotak pengguna Anda dianggap sebagai tanda tangan Anda, dan Anda dapat menggunakan profil Anda untuk memposting informasi tentang diri Anda sendiri. FAQ tentang tanda tangan / tagline
Andrew Barber
0

Coba jalankan tabel ubah berikut:

ALTER TABLE public.users 
ALTER COLUMN "password" TYPE varchar(300) 
USING "password"::varchar;
Никита Верёвкин
sumber