Тож ось мій сценарій:
Я працюю над Локалізацією для мого проекту, і, як правило, я б хотів зробити це в коді C #, однак я хочу зробити це в SQL трохи більше, оскільки я намагаюся трохи підключити свій SQL.
Навколишнє середовище: Стандарт SQL Server 2014, C # (.NET 4.5.1)
Примітка: сама мова програмування має бути неактуальною, я включаю її лише для повноти.
Тож я начебто здійснив те, що хотів, але не в тій мірі, яку хотів. Минув деякий час (щонайменше рік), оскільки я робив будь-які SQL JOIN
, крім базових, і це досить складно JOIN
.
Ось схема відповідних таблиць бази даних. (Є ще багато, але не потрібно для цієї порції.)
Всі відносини , описані в зображенні є повними в БД - PK
і FK
обмеження все настройки і експлуатації. Жоден із описаних стовпців не null
вдається. Усі таблиці мають схему dbo
.
Тепер у мене є запит, який майже робить те, що я хочу: тобто, з урахуванням БУДЬ-якого ІД SupportCategories
та будь-якого ІД 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.SupportКатегорії (цілий):
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 (Цілий):
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 (цілий):
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 (Цілий):
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, які є унікальними для початку.
Тл; д-р
Отже: питання про те , як я міг змінити цей запит, який повертає всі від SupportCategories
і потім повертати або StringTranslations.Text
для того StringKeys.Id
, Languages.Id
комбінації, абоStringKeys.Name
якщо вона НЕ існує?
Моя початкова думка полягає в тому, що я можу якимось чином передати поточний запит до іншого тимчасового типу як інший підзапит і обернути цей запит у ще одне SELECT
твердження та вибрати два поля, які я хочу ( SupportCategories.Id
і Result
).
Якщо я нічого не знайду, я просто застосую стандартний метод, який я зазвичай використовую, щоб завантажити все SupportCategories
в мій проект C #, а потім за допомогою нього запустіть запит, який я маю вище, вручну проти кожного SupportCategories.Id
.
Дякую за будь-які пропозиції та коментарі / критику.
Крім того, прошу вибачення за те, що це абсурдно довго, я просто не хочу ніякої двозначності. Я часто буваю на StackOverflow і бачу питання, яким не вистачає суті, не хотів би тут помилитися.