Sử dụng varchar (5000) sẽ không tốt so với varchar (255)?


27

varchardù sao phân bổ không gian linh hoạt, câu hỏi của tôi là liệu sử dụng varchar(255)có hiệu quả hơn hay tiết kiệm nhiều không gian hơn so với sử dụng varchar(5000). Nếu đúng thì tại sao?


Bạn có cần một cột rộng 5000 ký tự không? Nếu vậy TẠI SAO? Một cột varchar (MAX) sẽ làm việc tốt hơn cho bạn ở đây?
Richard L. Dawson

Câu trả lời:


51

Có, varchar(5000)có thể tồi tệ hơn varchar(255)nếu tất cả các giá trị sẽ phù hợp với sau này. Lý do là SQL Server sẽ ước tính kích thước dữ liệu và đến lượt nó, cấp bộ nhớ dựa trên kích thước khai báo (không thực tế ) của các cột trong bảng. Khi bạn có varchar(5000), nó sẽ giả sử rằng mọi giá trị dài 2.500 ký tự và bộ nhớ dự trữ dựa trên đó.

Đây là bản demo từ bài thuyết trình GroupBy gần đây của tôi về những thói quen xấu giúp bạn dễ dàng chứng minh (yêu cầu SQL Server 2016 cho một số sys.dm_exec_query_statscột đầu ra, nhưng vẫn có thể chứng minh được bằng SET STATISTICS TIME ONhoặc các công cụ khác trên các phiên bản trước); nó hiển thị bộ nhớ lớn hơn và thời gian chạy dài hơn cho cùng một truy vấn đối với cùng một dữ liệu - sự khác biệt duy nhất là kích thước khai báo của các cột:

-- 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];

Vì vậy, vâng, đúng kích thước cột của bạn , xin vui lòng.

Ngoài ra, tôi chạy lại các thử nghiệm với varchar (32), varchar (255), varchar (5000), varchar (8000), và varchar (max). Kết quả tương tự ( nhấp để phóng to ), mặc dù sự khác biệt giữa 32 và 255 và từ 5.000 đến 8.000, là không đáng kể:

nhập mô tả hình ảnh ở đây

Đây là một thử nghiệm khác với sự TOP (5000)thay đổi cho thử nghiệm có thể tái tạo hoàn toàn hơn mà tôi đã liên tục gặp khó khăn ( bấm vào để phóng to ):

nhập mô tả hình ảnh ở đây

Vì vậy, ngay cả với 5.000 hàng thay vì 10.000 hàng (và có hơn 5.000 hàng trong sys.all_column ít nhất là từ SQL Server 2008 R2), một tiến trình tương đối tuyến tính được quan sát - ngay cả với cùng một dữ liệu, kích thước được xác định càng lớn của cột, càng cần nhiều bộ nhớ và thời gian để đáp ứng chính xác cùng một truy vấn (ngay cả khi nó không có ý nghĩa DISTINCT).


Điều này thực sự đáng ngạc nhiên. Sự khác biệt giữa varchar(450)varchar(255)sẽ giống nhau? (Hoặc bất cứ điều gì dưới 4000?)
a_horse_with_no_name

@a_horse_with_no_name Tôi chưa kiểm tra tất cả các hoán vị của hiệu năng thời gian chạy, nhưng cấp bộ nhớ sẽ là một sự tiến triển tuyến tính - nó chỉ đơn giản là một chức năng của rowcount*(column_size/2).
Aaron Bertrand

Điều đó thật đáng thất vọng. Tôi mặc dù các phiên bản hiện đại của SQL Server không bị như vậy (miễn là độ dài được xác định nhỏ hơn 8000 hoặc có thể 4000).
a_horse_with_no_name

1
@a_horse_with_no_name Vâng, nó phải đoán xem dữ liệu rộng đến mức nào để tránh sự cố tràn. Làm thế nào khác nó nên đoán? Nó không thể quét và đọc toàn bộ bảng để xác định độ dài avg / max của tất cả các cột có chiều rộng thay đổi làm tiền thân để tạo kế hoạch thực hiện (và thậm chí nếu có thể, nó chỉ có thể thực hiện điều đó trong quá trình biên dịch lại).
Aaron Bertrand

2
Oracle giữ số liệu thống kê về ví dụ: độ dài hàng trung bình, giá trị tối thiểu và tối đa cho mỗi cột cũng như biểu đồ. Postgres giữ số liệu thống kê rất giống nhau (mặc dù nó không ghi tối thiểu / tối đa nhưng tần số). Đối với cả hai không có sự khác biệt nào giữa nvarchar (150), nvarchar (2000) hoặc varchar (400) trong hiệu suất.
a_horse_with_no_name
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.