SQLでVARCHARではなくCHARを選択する使用例は何ですか?


270

すべての値が固定幅の場合、CHARが推奨されることを理解しています。しかし、それで何ですか?安全のために、すべてのテキストフィールドにVARCHARを選択しないのはなぜですか。

回答:


386

すべての行がほぼ同じ長さになる場合は、通常、CHARを選択します。長さが大きく異なる場合は、VARCHARを選択してください。すべての行が同じ長さであるため、CHARは少し高速になる場合もあります。

これはDBの実装によって異なりますが、通常、VARCHARは実際のデータに加えて、1バイトまたは2バイトのストレージ(長さまたは終了用)を使用します。したがって、(1バイト文字セットを使用していると仮定して)単語「FooBar」を保存します

  • CHAR(6)= 6バイト(オーバーヘッドなし)
  • VARCHAR(10)= 8バイト(2バイトのオーバーヘッド)
  • CHAR(10)= 10バイト(4バイトのオーバーヘッド)

ボトムラインは、CHARをすることができより速く、よりスペース効率の良い(2文字の長さの差内で)比較的同じ長さのデータのために。

:Microsoft SQLでは、VARCHARに対して2バイトのオーバーヘッドがあります。これはDBによって異なりますが、通常、VARCHARの長さまたはEOLを示すために必要なオーバーヘッドが少なくとも1バイトあります。

コメントでGavenが指摘したように、UTF8のようなマルチバイトの可変長文字セットを使用している場合、CHARは文字数を格納するのに必要な最大バイト数を格納します。したがって、UTF8が文字を格納するために最大3バイトを必要とする場合、latin1文字のみを格納する場合でも、CHAR(6)は18バイトに固定されます。したがって、この場合、VARCHARの方がはるかに適切な選択になります。


20
別の理由は、ページ分割と断片化です。varchar列のページ分割のために、IDEN PKが99%断片化されたテーブルがありました。非常にアクティブなテーブルであり、アプリケーションの性質上、新しい行の空の行が作成され、データが設定されます。Charは断片化の問題を修正しました。
パパラッツォ

12
@ジム・マッキース-これらの計算は、latin1文字セットを使用している場合にのみ当てはまります。最近ではほとんどの人がutf8を使用しているはずなので、CHAR列は、ベースの多言語プレーンにほとんどの文字を格納するVARCHARとして、平均で3倍のスペースを使用します。
Gavin Towey 2014年

11
@JimMcKeethはい、それは正確です。CHARは固定長であるため、使用可能な最大可能なスペースに固定する必要があります。UTF8では、1文字あたり3バイトです。varcharの場合、必要に応じて1文字あたり1〜3バイトを自由に使用できます。これはMySQLマニュアルにあります:dev.mysql.com/doc/refman/5.0/en/charset-unicode-utf8.html
Gavin Towey

3
文字列FooBarとvarchar(100)とchar(100)の違いは何ですか?私は違いをよりよく示していると思います、そうですか?番号?
Nenotlep 2014年

4
@GavinTowey SQLSERVERは、NCHARおよびNVARCHARデータ型にUCS-2を使用します。常に1文字あたり2バイトです。
1010

69

私と一緒に作業していて、Oracleと一緒に作業している場合はvarchar、ほとんどすべての状況で使用することになるでしょう。現状charよりvarcharも少ない処理能力を使用するという仮定...今のところ...しかし、データベースエンジンは時間とともに改善され、この種の一般的なルールは将来の「神話」を作ることになります。

もう1つ:誰かがに行くことにしたので、パフォーマンスの問題を見たことがありませんvarchar。優れたコード(データベースへの呼び出しが少ない)と効率的なSQL(インデックスのしくみ、オプティマイザがどのように決定を下すか、なぜ通常existsよりも速いかin...)を書く時間をより有効に活用します。

最終的な考え:CHAR「」を検索する必要があるときに「」を探している人、または「FOO(ここにたくさんのスペース)」を探しているときに「FOO」を探している人の使用に関するあらゆる種類の問題を見てきました、または後続の空白を削除しない人、またはOracleプロシージャから返される値に最大2000個の空白を追加するPowerbuilderのバグ。


20
charは、オプティマイザ、将来のオプティマイザにも役立つヒントを提供する可能性があり、列の意図を伝えるのに役立つ可能性があるため、最初の段落には多少同意しません。しかし、3番目の段落では+1。余分なスペースはすべて嫌いです。フィールドには、[説明]パディングを一切行わずに、フィールドに何を入れてもかまいません。基本的に、すべてのデータの長さが正確に同じになる場合は、charを使用します。もちろん、これは非常にまれであり、通常はchar(1)です。
Jeffrey L Whitledge、2009

charは、アナリストや開発者にもヒントを提供します...これはxの文字数です...他の形式でシリアル化することを考えている場合、それが役立つかもしれません。(私は、uuidタイプを持たないmssqlのcharにmd5チェックサムを格納することを余儀なくされました...そして、32バイト未満は必要ありません...列にも制約を設定しました)。
joefromct 2017

31

パフォーマンス上の利点に加えてCHAR、すべての値同じ長さであることを示すために使用できます(たとえば、米国の州の略語の列)。


または国コード-2文字または3文字の国コード省略形の使用を区別するのに役立ちます
Dan Field

それが本当に固定長である場合、それを強制する制約があるはずです。ただし、を使用するCHAR場合は、制約がパディングを割引くことを確認する必要があります。
jpmc26

18

Charは少し高速であるため、特定の長さを知っている列がある場合は、charを使用します。たとえば、性別では(M)ale /(F)emale /(U)nknown、米国の州では2文字を格納します。


4
ENUMは通常、はるかに理にかなっているので、それが素晴らしい答えであることはわかりませんが、そのタイプが(MySQL以外で)広くサポートされているかどうかはわかりません。
ボビージャック

状態のセットは必ずしも不変ではないので、char(2)は列挙型よりもはるかに適切であるように思えます。
カーンズ、

1
@Bobby Jack-特定のSQL enum実装の詳細はわかりませんが、4バイト整数として格納されたenumには、char(1)またはchar(2)列よりも多くのスペースが必要になる場合があることに注意してください。同じデータ。enumは解釈の点でより論理的であり、説得力があるかもしれませんが、RDBMSシステム内のすべてはあるレベルで抽象的であり、テーブルに定義された述語に従います。
Jeffrey L Whitledge、2009

4
悪い例、その場合はENUMが最適です。より良い例は、3文字のIATA空港コードだろう
アンドリュー・G.ジョンソン

5
@Andrew、すべてのdbがENUMデータ型をサポートしているわけではありません。たとえば、MSSQLServerはそうしません。また、intとして格納されるENUMは4バイトを使用します。CHAR(1)は1バイトを使用し、NCHAR(1)は2バイトを使用します。
Jarrett Meyer

17

NCharまたはCharは、varの代替よりもパフォーマンスが優れていますか?

すばらしい質問です。簡単な答えは、特定の状況では「はい」です。これが説明できるかどうか見てみましょう。

明らかに、varchar(255)の列を持つテーブルを作成し(この列をmyColumnと呼ぶことにします)、100万行を挿入しますが、各列のmyColumnに数文字しか入力しない場合、テーブルははるかに小さくなります(全体として) myColumnをchar(255)として作成した場合よりも、ストレージエンジンに必要なデータページの数)。そのテーブルで操作(DML)を実行して多くの行を要求するときはいつでも、myColumnがvarchar であると、最後に「余分な」スペースすべてを移動する必要がないため、処理が速くなります。SQL Serverが個別操作またはユニオン操作中などの内部ソートを行うとき、またはクエリプランなどの間にマージを選択するときのように、移動します。

ただし、varcharの使用にはある程度のオーバーヘッドがあります。SQL Serverは、各行の2バイトインジケーター(オーバーヘッド)を使用して、その特定の行のmyColumnに含まれるバイト数を知る必要があります。問題を示すのは余分な2バイトではなく、すべての行のmyColumnにあるデータの長さを「デコード」する必要があります。

私の経験では、クエリで結合される列でvarcharではなくcharを使用することが最も理にかなっています。たとえば、テーブルの主キー、またはインデックスが作成されるその他の列。人口統計表のCustomerNumber、デコード表のCodeID、または注文表のOrderNumber。charを使用すると、クエリエンジンは、ページを読み取るときにポインターを可変量のバイトを移動する必要がなく、ストレートポインター演算を(確定的に)実行できるため、より迅速に結合を実行できます。私はその最後の文であなたを失ったかもしれないことを知っています。SQL Serverの結合は、「述語」の考え方に基づいています。述語は条件です。たとえば、myColumn = 1、またはOrderNumber <500。

したがって、SQL ServerがDMLステートメントを実行していて、述語、つまり結合される「キー」が固定長(char)である場合、クエリエンジンは、1つのテーブルの行を別のテーブル。行内のデータの長さを確認し、文字列を下に移動して最後を見つける必要はありません。それには時間がかかります。

これは、簡単に実装できない可能性があることを覚えておいてください。オンラインシステムの主キーフィールドにcharが使用されるのを見ました。幅は小さく保つ必要があります。つまり、char(15)または妥当なものです。また、通常は少数の行を取得またはアップサートするだけなので、結果セットで取得する末尾のスペースを「rtrim」する必要があるため、オンラインシステムで最適に機能します。 1つのテーブルの行から別のテーブルの数百万行まで。

オンラインシステムでCHARがvarcharよりも意味があるもう1つの理由は、ページ分割が減少することです。charを使用することで、本質的にそのスペースを「予約」し(無駄に)するため、ユーザーが後で来てその列にデータを追加した場合、SQLはすでにそのスペースを割り当てており、そのスペースに入ります。

CHARを使用するもう1つの理由は、2番目の理由と同様です。プログラマーまたはユーザーが何百万行にも「バッチ」更新を行って、たとえばノートフィールドに文を追加した場合、真夜中にDBAからドライブがいっぱいであると不思議に思うような問い合わせはありません。つまり、データベースのサイズの予測可能な増大につながります。

したがって、これらはオンライン(OLTP)システムがvarcharよりcharの方がメリットがある3つの方法です。ウェアハウス/分析/ OLAPシナリオでcharを使用することはほとんどありません。通常、これらのchar列の合計が大量の無駄なスペースになるほどの大量のデータがあるためです。

charを使用するとデータベースがはるかに大きくなる可能性がありますが、ほとんどのバックアップツールにはデータ圧縮があるため、バックアップはvarcharを使用した場合とほぼ同じサイズになる傾向があります。たとえば、LiteSpeedまたはRedGate SQLバックアップ。

もう1つの用途は、固定幅ファイルにデータをエクスポートするために作成されたビューです。メインフレームで読み取るために、一部のデータをフラットファイルにエクスポートする必要があるとします。固定幅です(区切られていません)。私は「ステージング」テーブルにデータをvarcharとして保存し(したがって、データベースで消費するスペースが少ない)、ビューを使用して、charに相当するものすべてをキャストし、その長さをその列の固定幅の幅に対応させます。例えば:

create table tblStagingTable (
pkID BIGINT (IDENTITY,1,1),
CustomerFirstName varchar(30),
CustomerLastName varchar(30),
CustomerCityStateZip varchar(100),
CustomerCurrentBalance money )

insert into tblStagingTable
(CustomerFirstName,CustomerLastName, CustomerCityStateZip) ('Joe','Blow','123 Main St Washington, MD 12345', 123.45)

create view vwStagingTable AS
SELECT CustomerFirstName = CAST(CustomerFirstName as CHAR(30)),
CustomerLastName = CAST(CustomerLastName as CHAR(30)),
CustomerCityStateZip = CAST(CustomerCityStateZip as CHAR(100)),
CustomerCurrentBalance = CAST(CAST(CustomerCurrentBalance as NUMERIC(9,2)) AS CHAR(10))

SELECT * from vwStagingTable

内部的にはvarcharを使用しているため、データが占めるスペースが少ないため、これはすばらしいことです。しかし、DTSまたはSSISを使用するか、SSMSからメモ帳にカットアンドペーストするだけでも、ビューを使用して適切な数の末尾のスペースを取得できます。DTSでは以前、「サジェストカラム」などと呼ばれていた機能を忘れていました。SSISではこれを行うことができなくなり、フラットファイル接続マネージャーを退屈に定義する必要があります。しかし、ビューの設定があるので、SSISは各列の幅を知ることができ、データフロータスクを構築するときに多くの時間を節約できます。

つまり、最終的にはvarcharを使用します。charを使用する理由は非常に少なく、パフォーマンス上の理由のみです。数百万行のシステムが存在する場合、述語が確定的(char)である場合に顕著な違いが見られますが、charを使用するほとんどのシステムでは、単にスペースを浪費しています。

お役に立てば幸いです。ジェフ


固定チャットは、保存時だけでなく、転送または「移動」したときにも、より多くのスペースを必要とするのですか?たとえば、DBサーバーからクライアントへ?これらのnullバイトはいつ失われるのですか?
Red Pea

9

パフォーマンス上の利点はありますが、ここでは言及されていない1つは行の移行です。charを使用すると、スペース全体を事前に予約するため、char(1000)があり、10文字を格納すると、1000文字すべてのスペースを使い果たすことになります。varchar2(1000)では、10文字のみを使用します。データを変更するときに問題が発生します。今、900文字を含むように列を更新するとします。varcharを拡張するためのスペースが現在のブロックで使用できない可能性があります。その場合、DBエンジンは行を別のブロックに移行し、元のブロックのポインターを新しいブロックの新しい行に移動する必要があります。このデータを読み取るために、DBエンジンは2つのブロックを読み取る必要があります。
varcharやcharの方が優れているとはっきりと言う人は誰もいません。時間のトレードオフのためのスペースがあり、データが更新されるかどうか、特にデータが大きくなる可能性が高い場合は考慮されます。


私はあなたの投稿にタイプミスがあると思います-varchar2(1000)はCHAR(1000)であるべきではありませんか?
Matt Rogish

8

初期のパフォーマンスの最適化とベストプラクティスタイプのルールの使用には違いがあります。常に固定長フィールドを持つ新しいテーブルを作成する場合、CHARを使用することは理にかなっています。その場合はCHARを使用する必要があります。これは初期の最適化ではなく、経験則(またはベストプラクティス)の実装です。

つまり、2文字の州フィールドがある場合は、CHAR(2)を使用します。実際の州名のフィールドがある場合は、VARCHARを使用します。


8

列に米国の州コードなどの固定値が格納されていない限り、varcharを選択します-常に2文字で、有効な米国の州コードのリストは頻繁に変更されません:)。

それ以外の場合は、ハッシュされたパスワード(固定長)を格納する場合と同様に、varcharを選択します。

理由-char型の列は常にスペースで満たされるため、列my_columnは、比較の中で値 'ABC'を持つchar(5)として定義されます。

my_column = 'ABC' -- my_column stores 'ABC  ' value which is different then 'ABC'

偽。

この機能は、開発中に多くの苛立たしいバグにつながる可能性があり、テストを困難にします。


1
少なくともMSSQLサーバーでは、 'abc' = 'abc'です。Iその機能など、または憎む場合、私はかなり....考え出したことがありません
マーク・ブラケット・


6

そのフィールドのすべてのデータ値が同じ長さである場合、CHARはVARCHARよりも少ないストレージ領域を使用します。おそらく2009年には、800 GBのデータベースは、VARCHARをCHARに変換した場合、すべての目的と目的で810GBと同じですが、短い文字列(1または2文字)の場合、CHARは業界の「ベストプラクティス」だと思います。

ここで、ほとんどのデータベースが整数のみ(ビット、小さな、整数、大きな整数)に対しても提供するさまざまなデータ型を見ると、どちらか一方を選択する理由があります。毎回単純にbigintを選択することは、実際にはフィールドの目的と使用法について少し無知です。フィールドが単に人の年齢を年で表す場合、bigintはやりすぎです。これは必ずしも「間違っている」とは限りませんが、効率的ではありません。

しかし、これは興味深い議論であり、データベースが時間とともに改善するにつれて、CHARとVARCHARの関連性は低くなると主張することができます。


4

ジム・マッキースのコメントに賛成です。

また、テーブルにCHAR列しかない場合は、インデックス作成とテーブル全体のスキャンが高速になります。基本的に、オプティマイザはCHAR列しかない場合、各レコードの大きさを予測できますが、すべてのVARCHAR列のサイズ値を確認する必要があります。

さらに、VARCHAR列を以前のコンテンツよりも大きいサイズに更新すると、データベースにインデックスを再構築させることができます(データベースにディスク上のレコードを物理的に移動させるため)。CHAR列を使用している間は、このようなことは起こりません。

ただし、テーブルが巨大でない限り、パフォーマンスへの影響は気にしないでしょう。

ジクストラの賢明な言葉を思い出してください。初期のパフォーマンス最適化は、すべての悪の根源です。


4
コメントにはある程度の憶測があります。私は何度も何度もこれらのような仮定がテストされ、正反対が真であることがわかりました。問題は、多くのエンジニアがこのような情報を福音として受け取ることです。皆さん、実際の状況を反映したテストケースを作成してください。
イーサンポスト

イーサンは完全に正しいです。これは、実際の(Product、Version)への参照なしでは、使用している実装に完全に依存しないためです。
デビッドシュミット

CHAR列を更新するときは、インデックスも更新する必要があります。その点でVARCHAR列またはCHAR列の更新に違いはありません。に更新することFOOを検討してくださいBAR
a_horse_with_no_name 2014年

4

多くの人が、CHARを使用して値の正確な長さを知っていれば、いくつかの利点があることを指摘しています。しかし、米国の州をCHAR(2)として保存するのは素晴らしいことですが、「オーストラリアへの最初の販売を行ったところです」という販売のメッセージを受け取ったとき、あなたは苦痛の世界にいます。私は常に、将来のイベントをカバーするために「正確な」推測を行うのではなく、フィールドがどれくらいの期間必要になると思うかを過大評価するために送信します。VARCHARを使用すると、この領域の柔軟性が向上します。


3

私はあなたの場合、おそらくVarcharを選ばない理由はないと思います。それはあなたに柔軟性を与え、多くの回答者によって言及されたように、非常に特定の状況を除いて私たちが(Google DBAのものとは対照的に)私たちを倒すことは違いに気付かないほどのパフォーマンスです。

DBタイプに関して注目に値する興味深いことは、sqlite(非常に印象的なパフォーマンスを持つ人気のミニデータベース)がすべてを文字列およびデータベースの型としてその場で配置することです。

私は常にVarCharを使用しており、通常、それを必要以上に大きくします。例えば。Firstnameは50です。安全を確保するためではありません。


3

私は文字を決して使用しません。私は多くの人々とこの討論をしてきました、そして彼らはいつも、チャーが速いという疲れた決まり文句を持ち出します。さて、私は言う、どのくらい速く?ここで何を話しているのか、ミリ秒、秒、もしそうなら何回?誰かが数ミリ秒速いと主張しているので、あなたは私に言っています、私たちはシステムにバグを修正するのが難しいトンを導入すべきですか?

だからここにあなたが遭遇するいくつかの問題があります:

すべてのフィールドが埋め込まれるので、どこにでもRTRIMSを持つコードが永久に残ります。これはまた、より長いフィールドにとっては巨大なディスク領域の浪費です。

ここで、1文字のcharフィールドの典型的な例がありますが、フィールドはオプションです。誰かがそのフィールドに空の文字列を渡すと、1つのスペースになります。したがって、別のアプリケーション/プロセスがクエリを実行すると、rtrimを使用しない場合、1つのスペースが取得されます。xmlドキュメント、ファイル、その他のプログラムがあり、オプションのフィールドにスペースを1つだけ表示して、問題を解決しました。

そのため、charフィールドに空の文字列ではなくnullを確実に渡す必要があります。しかし、それはnullの正しい使い方ではありません。これがnullの使用です。ベンダーからファイルを入手したとしましょう

名前|性別|市

ボブ||ロサンゼルス

ボブを入力するよりも性別が指定されていない場合は、空の文字列とロサンゼルスをテーブルに入力します。ここで、ファイルを取得し、その形式の変更と性別が含まれなくなったが、過去にあったとしましょう。

名前|市

ボブ|シアトル

さて今は性別が含まれていないので、nullを使用します。Varcharsはこれを問題なくサポートします。

一方、Charは異なります。常にnullを送信する必要があります。空の文字列を送信した場合、スペースが含まれるフィールドになります。

私はcharsから、そして約20年の開発で修正しなければならなかったすべてのバグを何度も続けることができました。


2

列の値に実際に必要なサイズを計算し、Varcharにスペースを割り当てるには、多少の処理オーバーヘッドがあります。そのため、値が常にどのくらい長くなるかがはっきりしている場合は、Charを使用してヒットを回避することをお勧めします。


2

これは、従来のスペースとパフォーマンスのトレードオフです。

MS SQL 2005では、Varchar(または文字ごとに2バイトを必要とするローナグ、つまり中国語)のNVarcharは可変長です。ハードディスクに書き込まれた後に行に追加すると、元の行と隣接していない場所にデータが配置され、データファイルの断片化が発生します。これはパフォーマンスに影響します。

したがって、スペースが問題にならない場合は、Charの方がパフォーマンスに優れていますが、データベースのサイズを小さくしたい場合は、varcharが適しています。


2

断片化。Charはスペースを予約し、VarCharは予約しません。varcharの更新に対応するために、ページ分割が必要になる場合があります。


他の多くの要因により、CHAR列の更新時にページ分割が発生する可能性があります。
リックジェームズ

1

varchar値を使用する場合、SQL Serverはその列に関するいくつかの情報を格納するために行ごとに追加の2バイトを必要としますが、charを使用する場合は必要ありません。


0

一部のSQLデータベースでは、オフセットを最適化するために、VARCHARが最大サイズに埋め込まれます。これは、テーブル全体のスキャンとインデックスを高速化するためです。

このため、CHAR(200)と比較してVARCHAR(200)を使用してもスペースを節約できません。


3
その方法でVARCHARを実装しているデータベースはどれですか?
Troels Arvin

5
真剣に、どのデータベースがそれをそのように実装していますか?通常、説明する内容はVARCHARではなくCHARに適用されます。
RichardSimões

同じテーブルにcharとvarcharがある場合、mysqlはvarcharをcharに変換します。
マルフィスト2009

MySQLのコメントの私の解釈は、これはプライマリテーブルストレージには適用されませんが、一時テーブルなどに関連する可能性があるということです。データのグループ化/ソート用。dev.mysql.com/doc/refman/8.0/en/char.html stackoverflow.com/questions/262238/...
トーマス・W

0

CHAR(NCHAR)とVARCHAR(NVARCHAR)を使用すると、データベースサーバーがデータを格納する方法に違いが生じます。最初の例では、末尾の空白を紹介しています。SQL SERVER関数でLIKE演算子と一緒に使用すると問題が発生しました。したがって、常にVARCHAR(NVARCHAR)を使用して安全にする必要があります。

たとえば、テーブルTEST(ID INT、Status CHAR(1))があり、次のような特定の値を持つすべてのレコードをリストする関数を記述したとします。

CREATE FUNCTION List(@Status AS CHAR(1) = '')
RETURNS TABLE
AS
RETURN
SELECT * FROM TEST
WHERE Status LIKE '%' + @Status '%'

この関数では、デフォルトのパラメーターを指定すると、関数がすべての行を返すことを期待していますが、実際にはそうではありません。@Statusデータ型をVARCHARに変更すると、問題が解決します。


これはansi_paddingでも変更できます。値の取得方法
Edward
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.