web-dev-qa-db-ja.com

常にnvarchar(MAX)を使用することに欠点はありますか?

SQL Server 2005では、長さを明示的に指定するのではなく、すべての文字フィールドをnvarchar(MAX)にすることには欠点がありますか? nvarchar(255)? (データベースレベルでフィールドの長さを制限できないことは明らかです)

322
stucampbell

MSDNフォーラムでも同じ質問がされました。

元の投稿から(さらに詳しい情報があります):

データをVARCHAR(N)列に格納する場合、値は物理的に同じ方法で格納されます。ただし、VARCHAR(MAX)列に保存すると、画面の背後でデータがTEXT値として処理されます。そのため、VARCHAR(MAX)値を処理する場合、追加の処理が必要になります。 (サイズが8000を超える場合のみ)

VARCHAR(MAX)またはNVARCHAR(MAX)は、「大きな値のタイプ」と見なされます。通常、大きな値の型は「行外」に格納されます。これは、データ行に「大きな値」が保存されている別の場所へのポインタがあることを意味します...

140
David Kreps

それは公平な質問であり、彼は明白なこととは別に述べました...

欠点には次のものが含まれます。

パフォーマンスへの影響クエリオプティマイザーは、フィールドサイズを使用して最も効率的な実行プランを決定します

「1.データベースの拡張およびページのスペース割り当ては柔軟です。更新を使用してフィールドに情報を追加する場合、新しいデータが以前に挿入されたものより長い場合、データベースはポインターを作成する必要があります。断片化=インデックスから削除、更新、挿入まで、ほとんどすべてのパフォーマンスが低下します。 " http://sqlblogcasts.com/blogs/simons/archive/2006/02/28/Why-use-anything-but -varchar_2800_max_2900_.aspx

統合への影響-他のシステムがデータベースと統合する方法を知るのは難しいデータの予測不可能な成長考えられるセキュリティ問題すべてのディスク領域を占有することでシステムをクラッシュさせる可能性があります

ここに良い記事があります: http://searchsqlserver.techtarget.com/tip/1,289483,sid87_gci1098157,00.html

47
alexmac

データ型に含まれるデータに何らかの意味を持たせたい場合があります。

たとえば、実際には20文字を超えてはならない列があるとします。その列をVARCHAR(MAX)として定義すると、悪意のあるアプリケーションが長い列を挿入する可能性がありますが、それを知ることはできませんし、それを防ぐ方法もありません。

次回、アプリケーションがその文字列を使用するとき、文字列の長さがそれが表すドメインにとって適度で合理的であるという仮定の下で、予測できない混乱する結果を経験します。

28
Bill Karwin

受け入れられた回答で提供されたリンクに基づいて、次のように見えます:

  1. nvarchar(MAX)フィールドに格納された100文字は、nvarchar(100)フィールドに100文字と同じように格納されます。データはインラインで格納され、データを「行外」で読み書きするオーバーヘッドはありません。心配ありません。

  2. サイズが4000を超える場合、データは自動的に「行外」に格納されます。これは必要なことです。心配もありません。

しかしながら...

  1. nvarchar(MAX)列にインデックスを作成することはできません。フルテキストインデックスを使用できますが、クエリのパフォーマンスを向上させるために列にインデックスを作成することはできません。私にとって、これは取引を封印します...常にnvarchar(MAX)を使用することは明確な欠点です。

結論:

データベース全体で一種の「ユニバーサル文字列長」が必要な場合は、インデックスを作成でき、スペースとアクセス時間を無駄にしないため、nvarchar(4000)を使用できます。

24
Tim Abell

いくつかの記事を確認し、これから有用なテストスクリプトを見つけました: http://www.sqlservercentral.com/Forums/Topic1480639-1292-1.aspx その後NVARCHAR(10)対NVARCHAR(4000)対NVARCHAR(MAX)を比較するように変更し、指定された数値を使用しているがMAXを使用している場合は速度の違いが見つかりません。自分でテストできます。この助けを願っています。

SET NOCOUNT ON;

--===== Test Variable Assignment 1,000,000 times using NVARCHAR(10)
DECLARE @SomeString NVARCHAR(10),
        @StartTime DATETIME;
--=====         
 SELECT @startTime = GETDATE();
 SELECT TOP 1000000
        @SomeString = 'ABC'
   FROM master.sys.all_columns ac1,
        master.sys.all_columns ac2;
 SELECT testTime='10', Duration = DATEDIFF(ms,@StartTime,GETDATE());
GO
--===== Test Variable Assignment 1,000,000 times using NVARCHAR(4000)
DECLARE @SomeString NVARCHAR(4000),
        @StartTime DATETIME;
 SELECT @startTime = GETDATE();
 SELECT TOP 1000000
        @SomeString = 'ABC'
   FROM master.sys.all_columns ac1,
        master.sys.all_columns ac2;
 SELECT testTime='4000', Duration = DATEDIFF(ms,@StartTime,GETDATE());
GO
--===== Test Variable Assignment 1,000,000 times using NVARCHAR(MAX)
DECLARE @SomeString NVARCHAR(MAX),
        @StartTime DATETIME;
 SELECT @startTime = GETDATE();
 SELECT TOP 1000000
        @SomeString = 'ABC'
   FROM master.sys.all_columns ac1,
        master.sys.all_columns ac2;
 SELECT testTime='MAX', Duration = DATEDIFF(ms,@StartTime,GETDATE());
GO
19
QMaster

単なる別の安全レベルと考えてください。完全に有効な外部キー関係なしでテーブルを設計し、完全にビジネスレイヤーに関連するエンティティが存在することを確認できます。ただし、外部キーは、ビジネス層で何かが台無しになった場合に別の制約レベルを追加するため、優れた設計プラクティスと見なされます。フィールドサイズの制限についても同様で、varchar MAXを使用しません。

13
Alex

最大またはテキストフィールドを使用しない理由は、SQL Server Enterprise Editionを使用していても オンラインインデックスの再構築 を実行できないためです。

8
Nick Kavadias

データベースの仕事は、企業が使用できるようにデータを保存することです。そのデータを有用にすることの一部は、それが意味があることを保証することです。誰かが名前に無制限の文字を入力できるようにすることは、意味のあるデータを保証するものではありません。

これらの制約をビジネスレイヤーに組み込むことは良い考えですが、それでもデータベースが完全に維持されることを保証しません。データルールに違反していないことを保証する唯一の方法は、データベース内で可能な限り低いレベルでルールを施行することです。

4
Tom H

フィールドの設定範囲が5〜10文字であることがわかっている場合の悪い考えです。私は、長さがどうなるかわからない場合にのみmaxを使用すると思います。たとえば、電話番号が特定の文字数を超えることはありません。

テーブル内のすべてのフィールドのおおよその長さの要件について、あなたが不確かだと正直に言えますか?

ただし、varchar(max)の使用を検討したいフィールドがいくつかあります。

興味深いことに、 MSDN docs はかなりうまくまとめています。

列データエントリのサイズが大幅に異なる場合は、varcharを使用します。列データエントリのサイズが大きく異なり、サイズが8,000バイトを超える可能性がある場合は、varchar(max)を使用します。

この問題についての興味深い議論 があります。

4
RichardOD

私が見つけた唯一の問題は、SQL Server 2005でアプリケーションを開発することでした。ある場合、SQL Server 2000をサポートする必要がありました。hardwaySQL Server 2000はvarcharまたはnvarcharのMAXオプションを好みません。

4
mattruma

1つの問題は、SQL Serverの複数のバージョンを使用する必要がある場合、MAXが常に機能するとは限らないことです。したがって、レガシーDBまたは複数のバージョンが関係するその他の状況で作業している場合は、十分に注意する必要があります。

3
TheTXI

上記で指摘したように、これは主にストレージとパフォーマンスのトレードオフです。少なくともほとんどの場合。

ただし、n/varchar(n)よりもn/varchar(Max)を選択する場合は、考慮すべき他の要因が少なくとも1つあります。データはインデックス付けされますか(たとえば、姓)? MAX定義はLOBと見なされるため、MAXとして定義されたものはすべてインデックスに使用できません。インデックスがない場合、WHERE句の述語としてデータを使用するルックアップは強制的に全表スキャンになります。これは、データルックアップで得られる最悪のパフォーマンスです。

3
Harry Cooper

1)nvarchar(max)とnvarchar(n)を処理する場合、SQLサーバーはより多くのリソース(割り当てられたメモリとCPU時間)を使用する必要があります(nはフィールド固有の数値)。

2)パフォーマンスに関してこれはどういう意味ですか?

SQL Server 2005では、15個のnvarchar(max)列を持つテーブルから13,000行のデータを照会しました。クエリのタイミングを繰り返してから、列をnvarchar(255)以下に変更しました。

最適化前のクエリは平均2.0858秒でした。変更後のクエリは平均1.90秒で返されました。これは、基本的なselect *クエリの約184ミリ秒の改善でした。これは8.8%の改善です。

3)私の結果は、パフォーマンスの違いがあることを示した他のいくつかの記事と一致しています。データベースとクエリに応じて、改善の割合は異なります。同時ユーザーがあまり多くない場合やレコードが非常に多い場合は、パフォーマンスの違いは問題になりません。ただし、レコード数と同時ユーザーが増えると、パフォーマンスの差は大きくなります。

2
WWC

行内のすべてのデータ(すべての列)が8000文字以下を合理的に使用できない場合、データレイヤーのデザインはこれを強制する必要があります。

データベースエンジンは、すべてをBLOBストレージから遠ざけてより効率的です。行を制限できるほど小さくなります。ページに詰め込める行が多いほど良い。アクセスするページ数が少ない場合、データベースのパフォーマンスは向上します。

1
Matt Spradley

興味深いリンク: TEXTを使用できるのにVARCHARを使用する理由

それはPostgreSQLとMySQLに関するものであるため、パフォーマンス分析は異なりますが、「明示性」のロジックは依然として保持されます。電子メールアドレスを変数に保存した場合、「80文字に制限された文字列」ではなく「文字列」を使用します。

1
orip

レガシーシステムのサポート。データを使用しているシステムがあり、特定の長さであると予想される場合、データベースは長さを強制するのに適した場所です。これは理想的ではありませんが、レガシーシステムは理想的ではない場合があります。 = P

1
Tony

私のテストでは、選択するときに違いがあることが示されました。

CREATE TABLE t4000 (a NVARCHAR(4000) NULL);

CREATE TABLE tmax (a NVARCHAR(MAX) NULL);

DECLARE @abc4 NVARCHAR(4000) = N'ABC';

INSERT INTO t4000
SELECT TOP 1000000 @abc4
    FROM
    master.sys.all_columns ac1,
    master.sys.all_columns ac2;

DECLARE @abc NVARCHAR(MAX) = N'ABC';

INSERT INTO tmax
SELECT TOP 1000000 @abc
    FROM
    master.sys.all_columns ac1,
    master.sys.all_columns ac2;

SET STATISTICS TIME ON;
SET STATISTICS IO ON;

SELECT * FROM dbo.t4000;
SELECT * FROM dbo.tmax;
1
Kvasi

文字列を埋め込み、出力をvarchar(max)に入れるudfがありました。調整対象の列に適切なサイズにキャストバックする代わりにこれを直接使用した場合、パフォーマンスは非常に低下しました。私は、udfのすべての呼び出し元に依存して文字列をより小さなサイズに再キャストする代わりに、udfを大きな音で任意の長さにしました。

1
Cade Roux

欠点の1つは、予測できない変数を中心に設計することであり、行、ページ、エクステントで徐々に構成される内部SQL Serverデータ構造を利用する代わりに、おそらく無視することです。

これにより、Cで data structure alignment について考えるようになります。また、アライメントを認識することは、一般的に良いこと(TM)と見なされます。同様のアイデア、異なるコンテキスト。

ページとエクステント のMSDNページ

Row-Overflow Data のMSDNページ

0
tsundoku

私が見ることができる主な欠点は、あなたがこれを持っているとしましょう:

UIに必要なデータに関する情報を最も多く提供しているのはどれですか?

この

            CREATE TABLE [dbo].[BusData](
                [ID] [int] IDENTITY(1,1) NOT NULL,
                [RecordId] [nvarchar](MAX) NULL,
                [CompanyName] [nvarchar](MAX) NOT NULL,
                [FirstName] [nvarchar](MAX) NOT NULL,
                [LastName] [nvarchar](MAX) NOT NULL,
                [ADDRESS] [nvarchar](MAX) NOT NULL,
                [CITY] [nvarchar](MAX) NOT NULL,
                [County] [nvarchar](MAX) NOT NULL,
                [STATE] [nvarchar](MAX) NOT NULL,
                [Zip] [nvarchar](MAX) NOT NULL,
                [PHONE] [nvarchar](MAX) NOT NULL,
                [COUNTRY] [nvarchar](MAX) NOT NULL,
                [NPA] [nvarchar](MAX) NULL,
                [NXX] [nvarchar](MAX) NULL,
                [XXXX] [nvarchar](MAX) NULL,
                [CurrentRecord] [nvarchar](MAX) NULL,
                [TotalCount] [nvarchar](MAX) NULL,
                [Status] [int] NOT NULL,
                [ChangeDate] [datetime] NOT NULL
            ) ON [PRIMARY]

それともこれ?

            CREATE TABLE [dbo].[BusData](
                [ID] [int] IDENTITY(1,1) NOT NULL,
                [RecordId] [nvarchar](50) NULL,
                [CompanyName] [nvarchar](50) NOT NULL,
                [FirstName] [nvarchar](50) NOT NULL,
                [LastName] [nvarchar](50) NOT NULL,
                [ADDRESS] [nvarchar](50) NOT NULL,
                [CITY] [nvarchar](50) NOT NULL,
                [County] [nvarchar](50) NOT NULL,
                [STATE] [nvarchar](2) NOT NULL,
                [Zip] [nvarchar](16) NOT NULL,
                [PHONE] [nvarchar](18) NOT NULL,
                [COUNTRY] [nvarchar](50) NOT NULL,
                [NPA] [nvarchar](3) NULL,
                [NXX] [nvarchar](3) NULL,
                [XXXX] [nvarchar](4) NULL,
                [CurrentRecord] [nvarchar](50) NULL,
                [TotalCount] [nvarchar](50) NULL,
                [Status] [int] NOT NULL,
                [ChangeDate] [datetime] NOT NULL
            ) ON [PRIMARY]
0
carlos martini