Дизайн базы данных: предпочтительная длина поля для путей к файлам - PullRequest
15 голосов
/ 07 декабря 2010

Я должен хранить пути к файлам в поле БД (/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), которое должно подходить для наиболее частых (даже если не во всех) случаях, и не слишком большое, как поле БД. Что бы вы порекомендовали?

Спасибо.

Ответы [ 7 ]

16 голосов
/ 02 октября 2017

Используйте соответствующую длину для данных, которые вы намереваетесь поддерживать. Поскольку вы используете 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) типов данных, которые могут серьезно ограничивать производительность.
9 голосов
/ 07 декабря 2010

Вы можете использовать VARCHAR(MAX) или NVARCHAR(MAX).

Это поля переменной длины, означающие, что они предназначены для хранения значений различной длины. Для более длинных значений больше не требуется никаких дополнительных затрат.

Определение MAX означает, что поле может иметь размер до 2 ГБ.

С MSDN (varchar), nvarchar имеет похожую документацию:

Используйте varchar, когда размеры записей данных столбца значительно различаются.

Используйте varchar (max), когда размеры записей данных столбца значительно различаются, а размер может превышать 8 000 байт.

8 голосов
/ 02 декабря 2017

Если вы используете 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.

2 голосов
/ 11 апреля 2015

Длина пути к файлу не может быть предсказана.Он может быть очень коротким как 'C:\' или очень длинным, как 'C:\Program Files\Microsoft SQL Server\110\LocalDB\Binn\Resources\1033' или даже больше.Но на уровне базы данных нет никакого вреда, используя что-то вроде VARCHAR(MAX)

См. Максимальный размер VARCHAR (MAX)

2 голосов
/ 07 декабря 2010

Я предлагаю вам не хранить пути в вашей существующей таблице. Создайте новую таблицу с последовательным счетчиком в качестве кластерного первичного ключа и символьным столбцом максимальной длины вашей программы БД. Я использую SQL Server, поэтому я бы использовал varchar (max).

Создайте столбец в вашей таблице данных для хранения первичного ключа таблицы «путей». Сначала вставьте в таблицу «paths», а затем используйте первичный ключ в качестве внешнего ключа обратно в таблицу данных.

Преимущество сохранения значения в другой таблице заключается в том, что оно не влияет на размер данных базовой таблицы. Запросы базовой таблицы, которые не включают «пути», не страдают от необходимости извлекать большое символьное значение, которое увеличивает трафик ввода-вывода.

1 голос
/ 07 декабря 2010

Поле должно иметь ту же длину, что и длина блока строки.

Поскольку запрос длины имени файла аналогичен запросу длины строки, запросдлина пути аналогична запросу длины всех битов строки в блоке неизвестного размера.

Таким образом, единственная разумная опция без дополнительной информации - это не ограничение длины, напримерNVARCHAR (MAX)

0 голосов
/ 07 декабря 2010

Я бы порекомендовал VARCHAR (2048) или даже VARCHAR (1024) , поскольку пути к файлам обычно не имеют длину 2000 символов.

...