Итак, вот мой сценарий:
Я работаю над локализацией для моего проекта, и обычно я делаю это в коде C #, однако я хочу сделать это в SQL немного больше, так как я пытаюсь немного улучшить свой SQL.
Среда: SQL Server 2014 Standard, C # (.NET 4.5.1)
Примечание: сам язык программирования не имеет значения, я включаю его только для полноты.
Так что я выполнил то, что хотел, но не так, как хотел. Прошло много времени (по крайней мере, год) с тех пор, как я выполнил любые SQL, JOINкроме базовых, и это довольно сложно JOIN.
Вот схема соответствующих таблиц базы данных. (Есть еще много, но не обязательно для этой части.)

Все отношения, описанные в образе, завершены в базе данных - PKи FKограничения и все настроены и работают. Ни один из описанных столбцов не в nullсостоянии. Все таблицы имеют схему dbo.
Теперь у меня есть запрос, который почти выполняет то, что я хочу: то есть, учитывая ЛЮБОЙ Id SupportCategoriesи ЛЮБОЙ Id Languages, он вернет либо:
Если есть правый правильный перевод этого языка для этой строки (Ie StringKeyId-> StringKeys.Idсуществует, и LanguageStringTranslations StringKeyId, LanguageIdи StringTranslationIdкомбинация существует, то он загружает StringTranslations.Textдля этого StringTranslationId.
Если LanguageStringTranslations StringKeyId, LanguageIdи StringTranslationIdкомбинация ничего НЕ существует, то он загружает StringKeys.Nameзначение. Это Languages.Idдано integer.
Мой запрос, будь то беспорядок, выглядит следующим образом:
SELECT CASE WHEN T.x IS NOT NULL THEN T.x ELSE (SELECT
CASE WHEN dbo.StringTranslations.Text IS NULL THEN dbo.StringKeys.Name ELSE dbo.StringTranslations.Text END AS Result
FROM dbo.SupportCategories
INNER JOIN dbo.StringKeys
ON dbo.SupportCategories.StringKeyId = dbo.StringKeys.Id
INNER JOIN dbo.LanguageStringTranslations
ON dbo.StringKeys.Id = dbo.LanguageStringTranslations.StringKeyId
INNER JOIN dbo.StringTranslations
ON dbo.StringTranslations.Id = dbo.LanguageStringTranslations.StringTranslationId
WHERE dbo.LanguageStringTranslations.LanguageId = 38 AND dbo.SupportCategories.Id = 0) END AS Result FROM (SELECT (SELECT
CASE WHEN dbo.StringTranslations.Text IS NULL THEN dbo.StringKeys.Name ELSE dbo.StringTranslations.Text END AS Result
FROM dbo.SupportCategories
INNER JOIN dbo.StringKeys
ON dbo.SupportCategories.StringKeyId = dbo.StringKeys.Id
INNER JOIN dbo.LanguageStringTranslations
ON dbo.StringKeys.Id = dbo.LanguageStringTranslations.StringKeyId
INNER JOIN dbo.StringTranslations
ON dbo.StringTranslations.Id = dbo.LanguageStringTranslations.StringTranslationId
WHERE dbo.LanguageStringTranslations.LanguageId = 5 AND dbo.SupportCategories.Id = 0) AS x) AS T
Проблема заключается в том, что она не способна обеспечить меня все из SupportCategoriesи их соответствующей , StringTranslations.Textесли она существует, или их , StringKeys.Nameесли бы его не существовало. Это идеально подходит для обеспечения любого из них, но не на всех. В основном, это для обеспечения , что если язык не имеет перевода для определенного ключа, то по умолчанию будет использовать , StringKeys.Nameкоторый имеет StringKeys.DefaultLanguageIdперевод. (В идеале, он даже не сделал бы этого, но вместо этого загрузил бы перевод StringKeys.DefaultLanguageId, который я мог бы сделать сам, если бы указывал в правильном направлении для остальной части запроса.)
Я потратил много времени на это, и я знаю, если бы я просто написал это на C # (как я обычно делаю), это было бы сделано к настоящему времени. Я хочу сделать это в SQL, и у меня возникают проблемы с получением результата, который мне нравится.
Единственное предостережение: я хочу ограничить количество примененных запросов. Все столбцы проиндексированы, и такие, какие мне нравятся сейчас, и без реального стресс-тестирования я не смогу их проиндексировать дальше.
Изменить: еще одно примечание, я пытаюсь сохранить базу данных максимально нормализованным, поэтому я не хочу дублировать вещи, если я могу избежать этого.
Пример данных
Источник
dbo.SupportCategories (Целостность):
Id StringKeyId
0 0
1 1
2 2
dbo.Languages (185 записей, показаны только два примера):
Id Abbreviation Family Name Native
38 en Indo-European English English
48 fr Indo-European French français, langue française
dbo.LanguagesStringTranslations (Completety):
StringKeyId LanguageId StringTranslationId
0 38 0
1 38 1
2 38 2
3 38 3
4 38 4
5 38 5
6 38 6
7 38 7
1 48 8 -- added as example
dbo.StringKeys (Completety):
Id Name DefaultLanguageId
0 Billing 38
1 API 38
2 Sales 38
3 Open 38
4 Waiting for Customer 38
5 Waiting for Support 38
6 Work in Progress 38
7 Completed 38
dbo.StringTranslations (Completety):
Id Text
0 Billing
1 API
2 Sales
3 Open
4 Waiting for Customer
5 Waiting for Support
6 Work in Progress
7 Completed
8 Les APIs -- added as example
Текущий выход
Учитывая точный запрос ниже, он выводит:
Result
Billing
Желаемый вывод
В идеале я хотел бы иметь возможность пропустить конкретные SupportCategories.Idи получить все из них, как это так (независимо от того, был ли использован язык 38 English, или 48 French, или ЛЮБОЙ другой язык в данный момент):
Id Result
0 Billing
1 API
2 Sales
Дополнительный пример
Учитывая, что я должен был добавить локализацию для French(т.е. добавить 1 48 8к LanguageStringTranslations), вывод изменится на (примечание: это только пример, очевидно, я бы добавил локализованную строку в StringTranslations) (обновлено на французском примере):
Result
Les APIs
Дополнительный желаемый выход
Учитывая приведенный выше пример, был бы желателен следующий вывод (обновленный на французском примере):
Id Result
0 Billing
1 Les APIs
2 Sales
(Да, технически я знаю, что это неправильно с точки зрения согласованности, но это то, что было бы желательно в данной ситуации.)
Редактировать:
Небольшое обновление. Я изменил структуру dbo.Languagesтаблицы, удалил Id (int)из нее столбец и заменил его Abbreviation(который теперь переименован Id, а все относительные внешние ключи и и отношения обновлены). С технической точки зрения, на мой взгляд, это более подходящая установка, поскольку таблица ограничена кодами ISO 639-1, которые с самого начала являются уникальными.
Tl; др
Итак: вопрос о том , как я мог изменить этот запрос, возвращающий все от SupportCategoriesи затем возвращать либо StringTranslations.Textдля того StringKeys.Id, Languages.Idкомбинации, илиStringKeys.Name если она НЕ существует?
Моя первоначальная мысль, что я мог бы как-то привести текущий запрос к другому временному типу в качестве другого подзапроса, обернуть этот запрос еще одним SELECTоператором и выбрать два поля, которые я хочу ( SupportCategories.Idи Result).
Если я ничего не найду, я просто использую стандартный метод, который я обычно использую, который загружает все SupportCategoriesв мой проект C #, а затем запускает запрос, который у меня есть выше, вручную для каждого SupportCategories.Id.
Спасибо за любые предложения / комментарии / критику.
Кроме того, я прошу прощения за то, что это нелепо долго, я просто не хочу никакой двусмысленности. Я часто бываю на StackOverflow и вижу вопросы, в которых нет смысла, я не хотел бы допустить эту ошибку здесь.
