varchar(5000)の使用はvarchar(255)と比較して悪いでしょうか?


28

varcharとにかくスペースを動的に割り当てるので、私の質問は、を使用varchar(255)するよりも、使用する方が効率的か、スペースを節約するかvarchar(5000)です。はいの場合、なぜですか?


5000文字幅の列が必要ですか?もしそうなら、なぜ?ここでは、varchar(MAX)列がより適切に機能しますか?
リチャードL.ドーソン

回答:


52

はい、すべての値が後者に収まる場合varchar(5000)よりも悪化する可能性がありますvarchar(255)。その理由は、SQL Serverがテーブル内の列の宣言された実際ではない)サイズに基づいてデータサイズを推定し、メモリ許可を推定するためです。がある場合varchar(5000)、すべての値が2,500文字の長さであると想定し、それに基づいてメモリを予約します。

これは最近の悪い習慣に関するGroupByプレゼンテーションのデモで、自分で簡単に証明できます(sys.dm_exec_query_stats出力列の一部にはSQL Server 2016 が必要ですがSET STATISTICS TIME ON、以前のバージョンでは他のツールでも証明可能です)。同じデータに対する同じクエリのより大きなメモリと長い実行時間を示します -唯一の違いは、宣言された列のサイズです:

-- create three tables with different column sizes
CREATE TABLE dbo.t1(a nvarchar(32),   b nvarchar(32),   c nvarchar(32),   d nvarchar(32));
CREATE TABLE dbo.t2(a nvarchar(4000), b nvarchar(4000), c nvarchar(4000), d nvarchar(4000));
CREATE TABLE dbo.t3(a nvarchar(max),  b nvarchar(max),  c nvarchar(max),  d nvarchar(max));
GO -- that's important

-- Method of sample data pop : irrelevant and unimportant.
INSERT dbo.t1(a,b,c,d)
  SELECT TOP (5000) LEFT(name,1), RIGHT(name,1), ABS(column_id/10), ABS(column_id%10)
  FROM sys.all_columns ORDER BY object_id;
GO 100
INSERT dbo.t2(a,b,c,d) SELECT a,b,c,d FROM dbo.t1;
INSERT dbo.t3(a,b,c,d) SELECT a,b,c,d FROM dbo.t1;
GO

-- no "primed the cache in advance" tricks
DBCC FREEPROCCACHE WITH NO_INFOMSGS;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
GO

-- Redundancy in query doesn't matter! Just has to create need for sorts etc.
GO
SELECT DISTINCT a,b,c,d, DENSE_RANK() OVER (PARTITION BY b,c ORDER BY d DESC)
FROM dbo.t1 GROUP BY a,b,c,d ORDER BY c,a DESC;
GO
SELECT DISTINCT a,b,c,d, DENSE_RANK() OVER (PARTITION BY b,c ORDER BY d DESC)
FROM dbo.t2 GROUP BY a,b,c,d ORDER BY c,a DESC;
GO
SELECT DISTINCT a,b,c,d, DENSE_RANK() OVER (PARTITION BY b,c ORDER BY d DESC)
FROM dbo.t3 GROUP BY a,b,c,d ORDER BY c,a DESC;
GO

SELECT [table] = N'...' + SUBSTRING(t.[text], CHARINDEX(N'FROM ', t.[text]), 12) + N'...', 
s.last_dop, s.last_elapsed_time, s.last_grant_kb, s.max_ideal_grant_kb
FROM sys.dm_exec_query_stats AS s CROSS APPLY sys.dm_exec_sql_text(s.sql_handle) AS t
WHERE t.[text] LIKE N'%dbo.'+N't[1-3]%' ORDER BY t.[text];

そう、はい、あなたの列を正しいサイズにしてください。

また、varchar(32)、varchar(255)、varchar(5000)、varchar(8000)、varchar(max)を使用してテストを再実行しました。同様の結果(クリックして拡大)、ただし、32〜255、5,000〜8,000の違いは無視できます。

ここに画像の説明を入力してください

ここTOP (5000)に、私が絶え間なくバッジを付けていた、より完全に再現可能なテストのための変更を伴う別のテストがありますクリックして拡大):

ここに画像の説明を入力してください

したがって、10,000行ではなく5,000行(少なくともSQL Server 2008 R2までさかのぼってsys.all_columnsに5,000行以上ある)でも、同じデータであっても定義されたサイズが大きくなると、比較的直線的な進行が観察されます列の場合、まったく同じクエリを満たすために、より多くのメモリと時間が必要です(たとえそれが無意味であってもDISTINCT)。


これは本当に驚くべきことです。違いだろうvarchar(450)とはvarchar(255)同じになりますか?(または4000未満?)
a_horse_with_no_name

@a_horse_with_no_name実行時のパフォーマンスのすべての順列をテストしたわけではありませんが、メモリ許可は線形の進行になります-これは単にの関数ですrowcount*(column_size/2)
アーロンバートランド

それはかなり残念です。ただし、SQL Serverの最新バージョンは、(定義された長さが8000またはおそらく4000よりも短い限り)その影響を受けません。
a_horse_with_no_name

1
@a_horse_with_no_nameまあ、データがどれだけ幅があるかを推測して、流出を回避する必要があります。他にどのように推測すべきですか?実行計画を生成する前兆として、テーブル全体をスキャンして読み取り、すべての可変幅列の平均/最大長を決定することはできません(できたとしても、再コンパイル中にのみ可能です)。
アーロンバートランド

2
Oracleは、平均行長、各列の最小値と最大値、およびヒストグラムなどの統計を保持します。Postgresは非常によく似た統計を保持しています(ただし、頻度は記録しますが、最小/最大は記録しません)。どちらについても、nvarchar(150)、nvarchar(2000)、またはvarchar(400)のパフォーマンスに違いはありません。
a_horse_with_no_name
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.