База данных за многоязычным пользовательским интерфейсом

8

Этот вопрос касается вопроса, несколько более сложного, чем тот, который уже был рассмотрен в этих старых вопросах, каждый из которых является дубликатом друг друга:

Предложение по структуре базы данных для мультиязычности (2011 г. июнь)

Какова лучшая структура базы данных для хранения многоязычных данных? (Февраль 2010 г.)

Каковы лучшие практики для проектирования многоязычных баз данных? (Май 2009 г.)

Схема для мультиязычной базы данных (2008 ноябрь)


Кажется, что наиболее популярная схема базы данных для поддержки многоязычных пользовательских интерфейсов состоит в том, что все переведенные тексты всех языков в одной таблице с 3 столбцами: идентификатор текста, код языка и сам текст. Текстовый идентификатор и код языка вместе составляют первичный ключ.

Это все очень хорошо, но теперь рассмотрим усложнение: предположим, что тексты должны быть доступны для поиска. Предположим, например, что это многоязычный интернет-магазин. Это означает, что для каждой категории продуктов, введенной в базу данных, владелец магазина будет вводить название категории продукта на каждом из N поддерживаемых языков, а затем покупатель сможет искать категорию продукта по имени, на своем родном языке .

Есть проблема: сопоставление .

Разные языки имеют разные последовательности сортировки, а последовательность сортировки, которая работает для одного языка, не работает для другого. Итак, если все тексты всех языков находятся в одном столбце, какую последовательность сортировки они будут иметь? Как мы собираемся запросить базу данных, чтобы найти текстовый идентификатор конкретного текста? В то время как в веб-продукте точность поиска и производительность могут быть не очень важны, для целей этого обсуждения давайте предположим, что они действительно имеют значение.

Большинство администраторов баз данных знакомы с понятием сопоставления в смысле «сопоставления базы данных». К счастью, это просто сортировка по умолчанию, которая используется, если нет никакой другой информации, но есть и другие места, где можно указать параметры сортировки:

  • Команда SQL CREATE INDEX поддерживает спецификацию сопоставления. (Хотя ходят слухи, что Microsoft SQL Server его не поддерживает; кто-нибудь знает об этом?)

  • Оператор SQL SELECT также поддерживает параметры сортировки, но в этом случае спецификация параметров сортировки работает как функция, вызывая сканирование индекса вместо поиска индекса, что может быть недопустимо, если мы хотим повысить производительность. (Опять же, если это лучшее, что мы можем иметь, это может быть лучше, чем ничего.)

  • Я также слышал, что в Microsoft SQL Server у вас могут быть непостоянные вычисляемые столбцы, в которых вы можете указать параметры сортировки и создать отфильтрованный индекс, хотя я никогда не слышал об этом раньше, и если это только Microsoft-SQL-Server Я бы предпочел воздержаться от его использования, независимо от того, насколько он крут и продуман.

Итак, в свете всего этого, как мы структурируем нашу базу данных и как мы выполняем наши запросы, если целью является обновляемая и доступная для поиска многоязычная база данных?


Этот вопрос был вдохновлен обсуждением, состоявшимся здесь: как nvarchar (max) будет хранить данные в базе данных, будет ли это быстро, если некоторые данные будут содержать менее 4000 символов?

Майк Накис
источник
2
Если функция Microsoft только для продуктов действительно крутая и продуманная, у нее должны быть хорошие шансы получить поддержку в подобных продуктах от других поставщиков вовремя. Просто мысль.

Ответы:

8

Это возможно для хранения строк с разными параметрами сортировки в той же колонке , используя SQL_VARIANT :

CREATE TABLE dbo.Localized
(
    text_id     INTEGER NOT NULL,
    lang_id     INTEGER NOT NULL,
    text_body   SQL_VARIANT NOT NULL,

    CONSTRAINT [PK dbo.Localized text_id, lang_id]
        PRIMARY KEY CLUSTERED (text_id, lang_id),
)
GO
INSERT dbo.Localized
    (text_id, lang_id, text_body)
VALUES
    (1001, 2057, N'Database problems' COLLATE Latin1_General_CI_AS);
GO
INSERT dbo.Localized
    (text_id, lang_id, text_body)
VALUES
    (1001, 1025, N'قاعدة بيانات المشاكل' COLLATE Arabic_CI_AS)

Эта конструкция имеет несколько недостатков (включая ограничение 8000 байт), не в последнюю очередь в области поиска: SQL_VARIANTне может быть полнотекстовым индексированием, а некоторые функции сравнения строк (например LIKE) также не могут использоваться напрямую. С другой стороны, есть возможность создать регулярный индекс SQL_VARIANTи выполнять несколько основных сравнения (например , <=,>) в сортировках-курсе моды:

CREATE UNIQUE INDEX uq1 ON dbo.Localized (text_body)
GO
-- One row
SELECT
    l.*
FROM dbo.Localized AS l 
WHERE
    l.text_body = CONVERT(SQL_VARIANT, N'Database problems' COLLATE Latin1_General_CI_AS)

-- No rows (and no collation error!)
SELECT
    l.*
FROM dbo.Localized AS l
WHERE
    l.text_body = CONVERT(SQL_VARIANT, N'Database problems' COLLATE Arabic_CI_AS)

-- One row, index seek, manual version of "LIKE 'D%'"
SELECT
    l.*
FROM dbo.Localized AS l 
WHERE
    l.text_body >= CONVERT(SQL_VARIANT, N'D' COLLATE Latin1_General_CI_AS)
    AND l.text_body < CONVERT(SQL_VARIANT, N'E' COLLATE Latin1_General_CI_AS)

Мы также можем написать обычный вид процедур:

CREATE PROCEDURE dbo.GetLocalizedString
    @text_id    INTEGER,
    @lang_id    INTEGER,
    @text_body  SQL_VARIANT OUTPUT
AS
BEGIN
    SELECT
        @text_body = l.text_body
    FROM dbo.Localized AS l
    WHERE
        l.text_id = @text_id
        AND l.lang_id = @lang_id
END
GO
DECLARE @text SQL_VARIANT

EXECUTE dbo.GetLocalizedString
    @text_id = 1001,
    @lang_id = 1025,
    @text_body = @text OUTPUT

SELECT @text

Конечно, полнотекстовая индексация также проблематична в дизайне «единой таблицы для всех переводов», поскольку полнотекстовая индексация (почти все) требует установки идентификатора языка для столбца . Дизайн нескольких таблиц, описанный Joop Eggen, может быть полнотекстовым индексированием (хотя, естественно, для каждой таблицы потребуется один индекс).

Другим основным вариантом является наличие одного столбца на локаль в базовой таблице:

CREATE TABLE dbo.Example
(
    text_id     INTEGER NOT NULL,
    text_2057   NVARCHAR(MAX) COLLATE Latin1_General_CI_AS NULL,
    text_1025   NVARCHAR(MAX) COLLATE Arabic_CI_AS NULL,

    CONSTRAINT [PK dbo.Example text_id]
        PRIMARY KEY CLUSTERED (text_id)
)

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

Каждая из альтернатив имеет свои преимущества и недостатки и потребует косвенного обращения на том или ином уровне, так что это может зависеть от того, где заинтересованные разработчики чувствуют себя счастливее, обнаружив такое косвенное обращение. Я полагаю, что большинство людей предпочитают дизайн с несколькими столами для большинства целей.

Пол Уайт 9
источник
Я, вероятно, использую отдельную таблицу, а не отдельные столбцы для лучшей физической раскладки: именно мой ответ сказал, что это вдохновило меня на этот вопрос dba.stackexchange.com/a/9954/630
gbn
5

Очевидно, вам нужна таблица для каждого языка: xxx_en , xxx_fr , xxx_eo . Это было бы более оптимальным и позволило бы сопоставлять язык. Можно даже представить, что у вас есть база данных для каждого языка [en] [xxx] , [fr] [xxx] , [eo] [xxx] .

Технические детали тогда имеют второстепенное значение (можно или нельзя оптимизировать больше).

Фактические текстовые ключи идут по таблице ххх .

Joop Eggen
источник
2
Проблема в том, что это очень нереляционно.
Майк Накис
Да, мой опыт показывает, что текстовый поиск, независимо от того, поддерживается ли он в БД или выполняется самостоятельно, трудно интегрировать реляционно. В любом случае, спасибо за оценку.