すべての値が固定幅の場合、CHARが推奨されることを理解しています。しかし、それで何ですか?安全のために、すべてのテキストフィールドにVARCHARを選択しないのはなぜですか。
すべての値が固定幅の場合、CHARが推奨されることを理解しています。しかし、それで何ですか?安全のために、すべてのテキストフィールドにVARCHARを選択しないのはなぜですか。
回答:
すべての行がほぼ同じ長さになる場合は、通常、CHARを選択します。長さが大きく異なる場合は、VARCHARを選択してください。すべての行が同じ長さであるため、CHARは少し高速になる場合もあります。
これはDBの実装によって異なりますが、通常、VARCHARは実際のデータに加えて、1バイトまたは2バイトのストレージ(長さまたは終了用)を使用します。したがって、(1バイト文字セットを使用していると仮定して)単語「FooBar」を保存します
ボトムラインは、CHARをすることができより速く、よりスペース効率の良い(2文字の長さの差内で)比較的同じ長さのデータのために。
注:Microsoft SQLでは、VARCHARに対して2バイトのオーバーヘッドがあります。これはDBによって異なりますが、通常、VARCHARの長さまたはEOLを示すために必要なオーバーヘッドが少なくとも1バイトあります。
コメントでGavenが指摘したように、UTF8のようなマルチバイトの可変長文字セットを使用している場合、CHARは文字数を格納するのに必要な最大バイト数を格納します。したがって、UTF8が文字を格納するために最大3バイトを必要とする場合、latin1文字のみを格納する場合でも、CHAR(6)は18バイトに固定されます。したがって、この場合、VARCHARの方がはるかに適切な選択になります。
私と一緒に作業していて、Oracleと一緒に作業している場合はvarchar
、ほとんどすべての状況で使用することになるでしょう。現状char
よりvarchar
も少ない処理能力を使用するという仮定...今のところ...しかし、データベースエンジンは時間とともに改善され、この種の一般的なルールは将来の「神話」を作ることになります。
もう1つ:誰かがに行くことにしたので、パフォーマンスの問題を見たことがありませんvarchar
。優れたコード(データベースへの呼び出しが少ない)と効率的なSQL(インデックスのしくみ、オプティマイザがどのように決定を下すか、なぜ通常exists
よりも速いかin
...)を書く時間をより有効に活用します。
最終的な考え:CHAR
「」を検索する必要があるときに「」を探している人、または「FOO(ここにたくさんのスペース)」を探しているときに「FOO」を探している人の使用に関するあらゆる種類の問題を見てきました、または後続の空白を削除しない人、またはOracleプロシージャから返される値に最大2000個の空白を追加するPowerbuilderのバグ。
Charは少し高速であるため、特定の長さを知っている列がある場合は、charを使用します。たとえば、性別では(M)ale /(F)emale /(U)nknown、米国の州では2文字を格納します。
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を使用するほとんどのシステムでは、単にスペースを浪費しています。
お役に立てば幸いです。ジェフ
パフォーマンス上の利点はありますが、ここでは言及されていない1つは行の移行です。charを使用すると、スペース全体を事前に予約するため、char(1000)があり、10文字を格納すると、1000文字すべてのスペースを使い果たすことになります。varchar2(1000)では、10文字のみを使用します。データを変更するときに問題が発生します。今、900文字を含むように列を更新するとします。varcharを拡張するためのスペースが現在のブロックで使用できない可能性があります。その場合、DBエンジンは行を別のブロックに移行し、元のブロックのポインターを新しいブロックの新しい行に移動する必要があります。このデータを読み取るために、DBエンジンは2つのブロックを読み取る必要があります。
varcharやcharの方が優れているとはっきりと言う人は誰もいません。時間のトレードオフのためのスペースがあり、データが更新されるかどうか、特にデータが大きくなる可能性が高い場合は考慮されます。
初期のパフォーマンスの最適化とベストプラクティスタイプのルールの使用には違いがあります。常に固定長フィールドを持つ新しいテーブルを作成する場合、CHARを使用することは理にかなっています。その場合はCHARを使用する必要があります。これは初期の最適化ではなく、経験則(またはベストプラクティス)の実装です。
つまり、2文字の州フィールドがある場合は、CHAR(2)を使用します。実際の州名のフィールドがある場合は、VARCHARを使用します。
列に米国の州コードなどの固定値が格納されていない限り、varcharを選択します-常に2文字で、有効な米国の州コードのリストは頻繁に変更されません:)。
それ以外の場合は、ハッシュされたパスワード(固定長)を格納する場合と同様に、varcharを選択します。
理由-char型の列は常にスペースで満たされるため、列my_columnは、比較の中で値 'ABC'を持つchar(5)として定義されます。
my_column = 'ABC' -- my_column stores 'ABC ' value which is different then 'ABC'
偽。
この機能は、開発中に多くの苛立たしいバグにつながる可能性があり、テストを困難にします。
そのフィールドのすべてのデータ値が同じ長さである場合、CHARはVARCHARよりも少ないストレージ領域を使用します。おそらく2009年には、800 GBのデータベースは、VARCHARをCHARに変換した場合、すべての目的と目的で810GBと同じですが、短い文字列(1または2文字)の場合、CHARは業界の「ベストプラクティス」だと思います。
ここで、ほとんどのデータベースが整数のみ(ビット、小さな、整数、大きな整数)に対しても提供するさまざまなデータ型を見ると、どちらか一方を選択する理由があります。毎回単純にbigintを選択することは、実際にはフィールドの目的と使用法について少し無知です。フィールドが単に人の年齢を年で表す場合、bigintはやりすぎです。これは必ずしも「間違っている」とは限りませんが、効率的ではありません。
しかし、これは興味深い議論であり、データベースが時間とともに改善するにつれて、CHARとVARCHARの関連性は低くなると主張することができます。
ジム・マッキースのコメントに賛成です。
また、テーブルにCHAR列しかない場合は、インデックス作成とテーブル全体のスキャンが高速になります。基本的に、オプティマイザはCHAR列しかない場合、各レコードの大きさを予測できますが、すべてのVARCHAR列のサイズ値を確認する必要があります。
さらに、VARCHAR列を以前のコンテンツよりも大きいサイズに更新すると、データベースにインデックスを再構築させることができます(データベースにディスク上のレコードを物理的に移動させるため)。CHAR列を使用している間は、このようなことは起こりません。
ただし、テーブルが巨大でない限り、パフォーマンスへの影響は気にしないでしょう。
ジクストラの賢明な言葉を思い出してください。初期のパフォーマンス最適化は、すべての悪の根源です。
CHAR
列を更新するときは、インデックスも更新する必要があります。その点でVARCHAR列またはCHAR列の更新に違いはありません。に更新することFOO
を検討してくださいBAR
。
私はあなたの場合、おそらくVarcharを選ばない理由はないと思います。それはあなたに柔軟性を与え、多くの回答者によって言及されたように、非常に特定の状況を除いて私たちが(Google DBAのものとは対照的に)私たちを倒すことは違いに気付かないほどのパフォーマンスです。
DBタイプに関して注目に値する興味深いことは、sqlite(非常に印象的なパフォーマンスを持つ人気のミニデータベース)がすべてを文字列およびデータベースの型としてその場で配置することです。
私は常にVarCharを使用しており、通常、それを必要以上に大きくします。例えば。Firstnameは50です。安全を確保するためではありません。
私は文字を決して使用しません。私は多くの人々とこの討論をしてきました、そして彼らはいつも、チャーが速いという疲れた決まり文句を持ち出します。さて、私は言う、どのくらい速く?ここで何を話しているのか、ミリ秒、秒、もしそうなら何回?誰かが数ミリ秒速いと主張しているので、あなたは私に言っています、私たちはシステムにバグを修正するのが難しいトンを導入すべきですか?
だからここにあなたが遭遇するいくつかの問題があります:
すべてのフィールドが埋め込まれるので、どこにでもRTRIMSを持つコードが永久に残ります。これはまた、より長いフィールドにとっては巨大なディスク領域の浪費です。
ここで、1文字のcharフィールドの典型的な例がありますが、フィールドはオプションです。誰かがそのフィールドに空の文字列を渡すと、1つのスペースになります。したがって、別のアプリケーション/プロセスがクエリを実行すると、rtrimを使用しない場合、1つのスペースが取得されます。xmlドキュメント、ファイル、その他のプログラムがあり、オプションのフィールドにスペースを1つだけ表示して、問題を解決しました。
そのため、charフィールドに空の文字列ではなくnullを確実に渡す必要があります。しかし、それはnullの正しい使い方ではありません。これがnullの使用です。ベンダーからファイルを入手したとしましょう
名前|性別|市
ボブ||ロサンゼルス
ボブを入力するよりも性別が指定されていない場合は、空の文字列とロサンゼルスをテーブルに入力します。ここで、ファイルを取得し、その形式の変更と性別が含まれなくなったが、過去にあったとしましょう。
名前|市
ボブ|シアトル
さて今は性別が含まれていないので、nullを使用します。Varcharsはこれを問題なくサポートします。
一方、Charは異なります。常にnullを送信する必要があります。空の文字列を送信した場合、スペースが含まれるフィールドになります。
私はcharsから、そして約20年の開発で修正しなければならなかったすべてのバグを何度も続けることができました。
一部のSQLデータベースでは、オフセットを最適化するために、VARCHARが最大サイズに埋め込まれます。これは、テーブル全体のスキャンとインデックスを高速化するためです。
このため、CHAR(200)と比較してVARCHAR(200)を使用してもスペースを節約できません。
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に変更すると、問題が解決します。