Дизайн базы данных: предпочтительная длина поля для путей к файлам
Я должен хранить пути к файлам в поле БД (/tmp/aaa/bbb
, C:\temp\xxx\yyy
и т.д.). Я не могу точно сказать, как долго они могут быть.
Учитывая это http://en.wikipedia.org/wiki/Comparison_of_file_systems и http://msdn.microsoft.com/en-us/library/aa365247.aspx, в зависимости от файловой системы теоретически не может быть ограничения длины тропинка.
Я полагаю, что было бы неразумно определять это поле как LONGBLOB
или VARCHAR(very high value)
. Я думал о чем-то вроде VARCHAR(1024)
который должен подходить для наиболее частых (даже если не во всех) случаях и не слишком большой, как поле БД. Чтобы вы посоветовали?
Благодарю.
Ответы
Ответ 1
Вы можете использовать VARCHAR(MAX)
или NVARCHAR(MAX)
.
Это поля переменной длины, означающие, что они предназначены для хранения значений разной длины. Нет дополнительных накладных расходов для более длинных значений по более коротким значениям.
Определение MAX
означает, что поле может содержать до 2 ГБ.
Из MSDN (varchar), nvarchar имеет схожую документацию:
Используйте varchar, когда размеры записей данных столбца значительно различаются.
Используйте varchar (max), когда размеры записей данных столбца сильно различаются, а размер может превышать 8 000 байт.
Ответ 2
Используйте соответствующую длину для данных, которые вы намереваетесь поддерживать. Поскольку вы используете SQL Server, вы должны использовать nvarchar(260)
в качестве верхнего предела для хранения имен путей, поскольку это предел спецификации для типичных машин Windows. При определенных обстоятельствах вы можете создавать пути длиннее этого, однако Windows Explorer будет иметь проблемы с их обработкой. SQL Server не может обрабатывать имена файлов длиннее 260 символов. Это включает в себя SQL Server в Linux.
Я могу доказать, что 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 для sys.master_files говорят об этом в столбце physical_name
:
физическое_имя nvarchar (260) Имя файла операционной системы.
Но пусть не верят этому. Мы видим, что физическое имя файла упоминается как physical_name = f.pname
. И псевдоним таблицы "f" указывает на FROM sys.sysbrickfiles f
. Поэтому SQL Server сохраняет имя файла в файле sys.sysbrickfiles, который является внутренней таблицей, видимой только из выделенного соединения администратора или DAC, как его часто называют. Подключаясь к ЦАП и генерируя временную таблицу из выходных данных 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.
Использование чего-либо, кроме столбца nvarchar(260)
для хранения имен файлов в SQL Server, является и расточительным, и создает технический долг.
Длина столбца чрезвычайно важна с точки зрения производительности. Длина столбца напрямую влияет на:
- память предоставляет для запросов к столбцу. Когда обработчик запросов создает план запроса, он использует размер каждого столбца, присутствующего в запросе, в качестве основы для объема памяти, необходимого для выполнения запроса. Он не использует фактический размер данных, присутствующих в каждом столбце, вместо этого он "догадывается", что средний размер данных будет составлять 50% от максимальной длины столбца.
- Возможность индексировать столбец эффективно. Большие столбцы создают значительно большие индексы. Большие индексы требуют большей памяти и пропускной способности диска, чем меньшие индексы. SQL Server имеет максимальную длину ключа 1700 байт для некластеризованных индексов (по состоянию на SQL Server 2016) и максимальную длину ключа 900 байт для кластеризованных индексов. Если вы попытаетесь создать индекс для столбцов, превышающих эти максимальные суммы, вы получите ошибки, и, возможно, только во время выполнения, когда это может быть очень дорогостоящим, чтобы исправить.
- На производительность символьного первичного/внешнего ключа сильно влияют большие длины столбцов. При обращении к первичным ключам через внешние ключи требования к размеру памяти, диска и ввода-вывода дублируются для каждого внешнего ключа. Возьмем, например, таблицу
Customer
ключом которой является столбец CustomerName
, определенный как varchar(500)
. Для каждой таблицы, которая ссылается на клиентов, теперь требуется 500-байтовый столбец CustomerName
. Если вместо этого этот столбец был определен как varchar(100)
, каждый запрос, ссылающийся на эти столбцы, будет сохранять 200 байтов на строку в памяти и дисковых операциях ввода-вывода. - Эрик Дарлинг показывает, что функция Predicate Pushdown не работает для типов данных
(MAX)
, которые могут серьезно ограничивать производительность.
Ответ 3
Если вы используете SQL Server, полезно знать, что Microsoft использует поля nvarchar (260) для хранения пути и имени файла в системных таблицах (например, sys.database_files, или sys.sysaltfiles, или sys.master_files).
Column name Data type Description
------------- ------------- ---------------------------
physical_name nvarchar(260) Operating-system file name.
Хорошей практикой может быть использование того же формата для хранения вашего пути и имени файла.
Вам, конечно, нужно будет установить размер в вашем пользовательском интерфейсе, чтобы быть уверенным, что он не будет усечен во время INSERT или UPDATE.
Ответ 4
Я предлагаю вам не хранить пути в существующей таблице. Создайте новую таблицу с последовательным счетчиком в качестве кластерного первичного ключа и столбца символов максимальной длины вашей программы db. Я использую SQL Server, поэтому я бы использовал varchar (max).
Создайте столбец в таблице данных, чтобы сохранить первичный ключ таблицы "пути". Сначала вставьте в таблицу "пути", затем используйте первичный ключ в качестве внешнего ключа в таблице данных.
Преимущество сохранения значения в другой таблице - это не влияет на размер данных базовой таблицы. Запросы базовой таблицы, которые не связаны с "путями", не страдают от необходимости вытягивать большое значение символа, что увеличивает IO-трафик.
Ответ 5
Длина пути к файлу не может быть предсказана. Он может быть очень коротким как 'C:\'
или может быть очень длинным, как 'C:\Program Files\Microsoft SQL Server\110\LocalDB\Binn\Resources\1033'
или даже больше. Но на уровне базы данных нет никакого вреда, используя что-то вроде VARCHAR(MAX)
См. Максимальный размер VARCHAR (MAX)
Ответ 6
Поле должно быть такой же длины, как длина строки строки.
Как вопрос о длине имени файла, как просить длину строки, задавая длину пути, вы спрашиваете длину всех битов строки в поле неизвестного размера.
Таким образом, единственная разумная опция без какой-либо другой информации не ограничивает длину, например. NVARCHAR (MAX)
Ответ 7
Я бы рекомендовал VARCHAR (2048) или даже VARCHAR (1024), поскольку пути к файлам обычно не длинны 2000 символов.