为您打算支持的数据使用适当的长度。由于您使用的是 SQL Server,因此您应该使用 nvarchar(260) 作为存储路径名的上限,因为 that is the specification limit 用于典型的 Windows 机器。在某些情况下,您可以创建比这更长的路径,但是 Windows 资源管理器在处理它们时往往会出现问题。 SQL Server 无法处理超过 260 个字符的文件名。这包括 Linux 上的 SQL Server。
我可以证明SQL Server 在内部使用nvarchar(260) 列来存储SQL Server 数据库文件名,包括路径。检查sys.master_files视图的定义,我们看到如下T-SQL:
CREATE VIEW sys.master_files AS
SELECT
database_id = f.dbid,
file_id = f.fileid,
file_guid = f.fileguid,
type = f.filetype,
type_desc = ft.name,
data_space_id = f.grpid,
name = f.lname,
physical_name = f.pname,
state = convert(tinyint, case f.filestate -- Map enum EMDFileState to AvailablityStates
when 0 then 0 when 10 then 0 -- ONLINE
when 4 then 7 -- DEFUNCT
when 5 then 3 when 9 then 3 -- RECOVERY_PENDING
when 7 then 1 when 8 then 1 when 11 then 1 -- RESTORING
when 12 then 4 -- SUSPECT
else 6 end), -- OFFLINE
state_desc = st.name,
f.size,
max_size = f.maxsize,
f.growth,
is_media_read_only = sysconv(bit, f.status & 8), -- FIL_READONLY_MEDIA
is_read_only = sysconv(bit, f.status & 16), -- FIL_READONLY
is_sparse = sysconv(bit, f.status & 256), -- FIL_SPARSE_FILE
is_percent_growth = sysconv(bit, f.status & 32), -- FIL_PERCENT_GROWTH
is_name_reserved = sysconv(bit, case f.filestate when 3 then 1 else 0 end), -- x_efs_DroppedReusePending
create_lsn = GetNumericLsn(f.createlsn),
drop_lsn = GetNumericLsn(f.droplsn),
read_only_lsn = GetNumericLsn(f.readonlylsn),
read_write_lsn = GetNumericLsn(f.readwritelsn),
differential_base_lsn = GetNumericLsn(f.diffbaselsn),
differential_base_guid = f.diffbaseguid,
differential_base_time = nullif(f.diffbasetime, 0),
redo_start_lsn = GetNumericLsn(f.redostartlsn),
redo_start_fork_guid = f.redostartforkguid,
redo_target_lsn = GetNumericLsn(f.redotargetlsn),
redo_target_fork_guid = f.forkguid,
backup_lsn = GetNumericLsn(f.backuplsn),
credential_id = cr.credential_id
FROM sys.sysbrickfiles f
LEFT JOIN sys.syspalvalues st ON st.class = 'DBFS' AND st.value = f.filestate
LEFT JOIN sys.syspalvalues ft ON ft.class = 'DBFT' AND ft.value = f.filetype
LEFT JOIN sys.credentials cr ON f.pname LIKE cr.name + N'%' COLLATE database_default
WHERE f.dbid < 0x7fff -- consistent with sys.databases
AND f.pruid = 0
AND f.filestate NOT IN (1, 2) -- x_efs_Dummy, x_efs_Dropped
AND has_access('MF', 1) = 1
Microsoft Docs for sys.master_files 谈到了physical_name 专栏:
physical_name nvarchar(260) 操作系统文件名。
但我们不要相信这一点。我们看到物理文件名被引用为physical_name = f.pname。表别名“f”指向FROM sys.sysbrickfiles f。因此,SQL Server 将文件名存储在 sys.sysbrickfiles 中,这是一个内部表,只能从专用管理员连接或众所周知的 DAC 中看到。连接到 DAC 和 generating a temp table from the output 的 sys.sysbrickfiles,我们看到以下内容:
CREATE TABLE #sysbrickfiles
(
brickid int NOT NULL
, dbid int NOT NULL
, pruid int NOT NULL
, fileid int NOT NULL
, grpid int NOT NULL
, status int NOT NULL
, filetype tinyint NOT NULL
, filestate tinyint NOT NULL
, size int NOT NULL
, maxsize int NOT NULL
, growth int NOT NULL
, lname nvarchar(128) NOT NULL
, pname nvarchar(260) NOT NULL
, createlsn binary(10) NULL
, droplsn binary(10) NULL
, fileguid uniqueidentifier NULL
, internalstatus int NOT NULL
, readonlylsn binary(10) NULL
, readwritelsn binary(10) NULL
, readonlybaselsn binary(10) NULL
, firstupdatelsn binary(10) NULL
, lastupdatelsn binary(10) NULL
, backuplsn binary(10) NULL
, diffbaselsn binary(10) NULL
, diffbaseguid uniqueidentifier NULL
, diffbasetime datetime NOT NULL
, diffbaseseclsn binary(10) NULL
, redostartlsn binary(10) NULL
, redotargetlsn binary(10) NULL
, forkguid uniqueidentifier NULL
, forklsn binary(10) NULL
, forkvc bigint NOT NULL
, redostartforkguid uniqueidentifier NULL
);
如您所见,pname 列确实定义为nvarchar(260)。
此外,如果我们尝试使用长度超过 260 个字符的文件名创建数据库,我们会看到返回错误:
消息 103,第 15 级,状态 3,第 7 行
该文件与“F:\ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARGH.mdf”开始太长。最大长度为 259。
在 SQL Server 中使用除 nvarchar(260) 列之外的任何内容来存储文件名既浪费又产生技术债务。
列的长度在性能方面非常重要。列长直接影响:
- 为针对该列的查询授予内存。当查询处理器创建查询计划时,它使用查询中存在的每一列的大小作为运行查询所需的内存量的基础。它不使用每列中存在的数据的实际大小,而是“猜测”数据的平均大小将是列最大长度的 50%。
- 能够有效地索引列。较大的列创建显着更大的索引。与较小的索引相比,较大的索引需要更多的内存和磁盘吞吐量。 SQL Server 的非聚集索引的最大键长度为 1700 字节(自 SQL Server 2016 起),聚集索引的最大键长度为 900 字节。如果您尝试在大于这些最大数量的列上创建索引,则会出现错误,并且可能直到运行时才会出现错误,此时修复成本非常高。
- 基于字符的主键/外键性能受到较大列长度的严重影响。当通过外键引用主键时,每个外键对内存、磁盘和 I/O 的大小要求都是重复的。以
Customer 表为例,其中键是CustomerName 列,定义为varchar(500)。现在,每个引用客户的表都需要一个 500 字节的 CustomerName 列。如果将该列定义为 varchar(100),则每个引用这些列的查询每行将在内存和磁盘 I/O 中节省 200 个字节。
- Erik Darling 表明 Predicate Pushdown 不适用于
(MAX) 数据类型,这会严重限制性能。