Замена внешнего ключа на поле длинного текста (для экономии места) вызывает ошибку: невозможно выполнить объединение, группировку, сортировку или ограничение индексирования

#ms-access-2016

#ms-access-2016

Вопрос:

У меня есть таблица с именем JobQueue, которая содержит поле с именем Command типа LongText.

Поскольку многие строки содержат одно и то же значение для команды (часто длиной около 2000 символов) Я хотел бы заменить командное поле в таблице JobQueue внешним ключом с именем idCommand, который ссылается на поле id в таблице Text_1G. Таким образом, вместо того, чтобы хранить одну и ту же строку из 2000 символов снова и снова, я могу просто сохранять целое число в idCommand (снова и снова), чтобы указывать на единственную копию команды из 2000 (или более) символов.

Поэтому я

 CREATE TABLE Text_1G (id AUTOINCREMENT PRIMARY KEY, [text] LONGTEXT) 
CREATE UNIQUE INDEX idxText_1G ON Text_1G ([text] ASC)
 

Итак, я смог успешно вставить строку в Text_1G, но когда я попытался (слева) ПРИСОЕДИНИТЬСЯ к JobQueue и Text_1G с помощью idCommand внешнего ключа, я получил эту ошибку:

 System.Data.OleDb.OleDbException: Cannot perform join, group, sort, or indexed restriction. A value being searched or sorted on is too long
 

Это левое соединение, по-видимому, работает для более коротких строк, но не для длинных…

Черт возьми… Есть ли обходной путь для решения этой проблемы?

Я использую Access версии 16.0.13801.20240.

Ответ №1:

На самом деле, Access не может присоединиться к длинным текстовым (памятным) полям.

Распространенным обходным путем является усечение поля перед объединением, например ON LEFT(LongTextField, 255) = LEFT(ForeignLongTextField, 255) . Однако это может привести к непреднамеренным совпадениям, особенно при использовании кода в качестве ключа, поскольку может быть разница за пределами первых 255 символов.

Более сложным, но более правильным решением является использование хэша в качестве ключа соединения и соединение с хэшем, например

 ON SHA1(LongTextField) = SHA1(ForeignLongTextField)
 

Однако для этого требуется, чтобы вы могли использовать функцию хеширования, а Access не имеет встроенной поддержки хеширования. Поскольку вы, похоже, используете .Net, вы можете хэшировать на прикладном уровне, сохранять хэши в базе данных, а затем использовать хэши для создания соединения. Если вы используете сам Access, вы можете выполнить хеширование в VBA и использовать его в запросе, однако это приводит к большому снижению производительности и требует от вас реализации быстрого хеширования.

Обратите внимание, что, поскольку хэш используется не для обеспечения безопасности, а для хеширования, я обычно предпочитаю использовать хэш с небольшим результатом выше, чем тот, который обеспечивает хорошую безопасность, поэтому я обычно использую MD5 или SHA1.

Комментарии:

1. Варианты SHA1 или SHA256 кажутся мне хорошими… Я использую C #. Что бы вы посоветовали? Поскольку я вижу, что access не имеет varbinary типа, может быть, мне следует использовать shorttext (64) и преобразовать байты из хэша с sBuilder.Append(data[i].ToString("x2")); помощью? Или сэкономить место и просто хранить байты напрямую? Сколько байтов я могу хранить в символе доступа?

2. @user3477493 Access на самом деле имеет VARBINARY поле. Он просто не раскрывает это пользователю. Я рекомендую использовать двоичное поле фиксированной длины (не VAR), так как хэш будет иметь точно такой же размер каждый раз. Используйте ALTER TABLE Text_1G ADD FIELD (HashField BINARY(32)) (32 байта, если SHA256, 20 для SHA1).

3. Мой код работает, если выполняется поиск только по SHA256 (который индексируется) или только по полю [text] (линейный поиск), но не работает, если я ищу как SHA256, так и [text]. Я делаю что-то не так? Я решил, что мне следует искать и то, и другое в случае отдаленной вероятности того, что SHA256 не был уникальным.

4. @user3477493 Я не могу точно знать, что вы делаете, но, как правило, не выполняйте поиск по тексту, только по хэшу. Дубликат известен как столкновение. Чтобы проверить вероятность столкновений, существуют онлайн-калькуляторы , но вероятность использования 256-битного хэша фактически равна 0 даже при хэшировании миллиардов элементов. Если бы вы выбрали MD5, как я советовал, он не масштабировался бы до миллиардов, но все равно был бы хорош для миллионов.