Postgresql-varcharカラムのサイズをより短い長さに変更します


153

ALTER TABLE非常に大きなテーブル(約3,000万行)に対するコマンドについて質問があります。その列の1つはです。varchar(255)これをにサイズ変更したいと考えていvarchar(40)ます。基本的に、次のコマンドを実行して列を変更します。

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

プロセスが非常に長くても問題はありませんが、ALTER TABLEコマンドの実行中にテーブルが読みにくくなったようです。もっと賢い方法はありますか?たぶん、新しい列を追加し、古い列から値をコピーし、古い列をドロップして、最後に新しい列の名前を変更しますか?

手がかりは大歓迎です!前もって感謝します、

注:PostgreSQL 9.0を使用しています。


11
ただ明確にするために、あなたは知っています、それresizingはテーブルがより少ないスペースを占有することをしないでしょうか?
AH

私の場合でも、列の最大サイズが255ではなく40文字(つまりオクテット)になるということですか?
Labynocle

16
varchar(255)PostgreSQLに言うと、実際の長さが40バイトの値に255バイト割り当てられません。40バイトを割り当てます(さらに、内部オーバーヘッドも含まれます)。唯一のことだろうbe changed by the ALTER TABLE`あなたはPGからエラーを取得せずに、その列に格納できる最大バイト数です。
AH

AHが言及したオーバーヘッドについて:varchar(n)のオーバーヘッドは何ですか?
Erwin Brandstetter 2016年

ここで答えをチェックして、アップデートdba.stackexchange.com/questions/189890/…
Evan Carroll

回答:


73

データを変更せずにPostgreSQLテーブルの列のサイズを変更するでこれを行う方法の説明があります。データベースカタログデータをハックする必要があります。これを公式に行う唯一の方法は、ALTER TABLEを使用することです。前述のとおり、変更すると、実行中にテーブル全体がロックされ、テーブル全体が書き換えられます。

これを変更する前に、ドキュメントの「文字タイプ」セクションを必ずお読みください。ここで知っておくべきあらゆる種類の奇妙なケース。長さチェックは、値が行に格納されるときに行われます。そこで下限をハッキングしても、既存の値のサイズはまったく減少しません。変更を加えた後、フィールドの長さが40文字を超える行を探すためにテーブル全体をスキャンすることをお勧めします。それらを手動で切り捨てる方法を理解する必要があります-そのため、サイズが大きいものにロックを戻します-誰かがその行で何かを更新しようとすると、現時点では大きすぎるため拒否されます行の新しいバージョンを格納します。陽気さがユーザーに起こります。

VARCHARは、SQL標準の関連するひどい部分に準拠するためだけにPostgreSQLに存在するひどい型です。マルチデータベースの互換性を気にしない場合は、データをTEXTとして保存し、長さを制限する制約を追加することを検討してください。このテーブルロック/書き換えの問題がなくても変更できる制約は、弱い長さのチェックよりも多くの整合性チェックを実行できます。


答えてくれてありがとう。リンクを確認します。すべてのコンテンツの最大サイズが40文字であるため、手動でサイズを確認する必要はありません。VARCHARがlentghをチェックする方が良いと思ったので、TEXTの制約についてもっと読む必要があります:)
Labynocle

6
varcharの長さを変更しても、テーブルは書き換えられません。CHECK CONSTRAINTとまったく同じように、テーブル全体に対して制約の長さをチェックするだけです。長さを増やす場合、何もする必要はありません。次の挿入または更新だけで、より長い長さが受け入れられます。長さを減らし、すべての行が新しい小さな制約を通過した場合、Pgは次の挿入または更新で新しい長さのみを書き込むことを許可する以外に、それ以上のアクションを実行しません。
Maniero 2013

3
@bigown、明確にするために、ステートメントはPostgreSQL 9.2+にのみ当てはまり、古いステートメントには当てはまりません。
MatheusOl 2013年

12
リンクは現在無効です。
raarts 2017年

これがどのように機能するかについての詳細は、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 2015年

2
1つ以上の行に新しいサイズを超える値が含まれていない限り、ALTER TABLEクエリを介してvarcharサイズを下げても、Postgresはエラーをスローしませ
伝える

@Tell、面白い。それは、Postgresがテーブルのフルスキャンを実行することを意味しますか、それとも、統計に最大サイズを保持しますか?
Matthieu

47

さて、私はおそらくパーティーに遅れます、しかし...

ケースの列のサイズを変更する必要はありません!

他の一部のデータベースとは異なり、Postgresは文字列に収まるだけの十分なスペースを使用するのに十分スマートです(長い文字列の圧縮を使用していても)。したがって、列がVARCHAR(255)として宣言されている場合でも-40文字の文字列を列では、スペース使用量は40バイト+ 1バイトのオーバーヘッドになります。

短い文字列(最大126バイト)のストレージ要件は、1バイトに実際の文字列を加えたもので、文字の場合はスペースのパディングが含まれます。長い文字列は1ではなく4バイトのオーバーヘッドを持ちます。長い文字列はシステムによって自動的に圧縮されるため、ディスク上の物理的な要件は少なくなる可能性があります。非常に長い値もバックグラウンドテーブルに格納されるため、短い列値への迅速なアクセスを妨げることはありません。

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

VARCHARのサイズ指定は、挿入される値のサイズを確認するためにのみ使用され、ディスクレイアウトには影響しません。実際、VARCHARおよびTEXTフィールドはPostgresと同じ方法で格納されます。


8
「なぜ」に関する情報を追加するのに遅すぎることはありません。このすべての情報をありがとう
Labynocle 2013

データベースの構造を一貫させる必要がある場合があります。2つの列に関係がなくても、モデルEAVのチェックアウトなど、概念の観点から関係を持たせることができます。
アレクサンドル

36

VARCHARを32から8に切り詰めようとして、を取得しようとして同じ問題に直面していましたERROR: value too long for type character varying(8)。私は顧客の選択に応じて異なるDBMSに切り替える必要がある可能性のある自作のJPAのような構造を使用しているため、SQLにできるだけ近づけたいです(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

新しい列を追加して新しい列を古い列に置き換えると、うまくいきました。redshiftpostgresqlで、詳細についてはこのリンクを参照してください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;

これは、40万行を超えるテーブルで数秒高速で動作しました。


5
明示的なトランザクションラッパーがALTERステートメントのロック動作を変更することを期待するのはなぜですか?そうではありません。
Erwin Brandstetter 2017年

トランザクションラッパーの有無にかかわらず、自分で試してみると、大きな違いに気付くでしょう。
jacktrade 2017年

2
君の答えは原則的に正しくない。明示的なトランザクションラッパーのないDDLステートメントは、トランザクション内で暗黙的に実行されます。明示的なトランザクションの唯一可能な影響は、明示的なトランザクションになるまで、ロックがより長く保持されることCOMMITです。ラッパーは、同じトランザクションにさらにコマンドを入れたい場合にのみ意味があります。
Erwin Brandstetter 2017年

あなたは完全に正しいですが、私は主張します:自分で試して、続けてください。次に、なぜ同じように機能しないのかを尋ねます。
jacktrade 2017年

Postgres 9.3では役に立たなかった。
Noumenon

1

サイズを変更する非常に簡単な方法、つまり「import javax.validation.constraints」、つまり「import javax.validation.constraints.Size;」の一部であるアノテーション@Size(min = 1、max = 50)を見つけました。

@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
Andrew Barber

0

次の変更テーブルを実行してみてください。

ALTER TABLE public.users 
ALTER COLUMN "password" TYPE varchar(300) 
USING "password"::varchar;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.