Voici donc mon scénario:
Je travaille sur la localisation pour un de mes projets, et en général, j'allais faire cela dans le code C #, mais je veux le faire un peu plus en SQL car j'essaye de buff un peu mon SQL.
Environnement: SQL Server 2014 Standard, C # (.NET 4.5.1)
Remarque: le langage de programmation lui-même ne devrait pas être pertinent, je ne l'inclus que pour être complet.
J'ai donc en quelque sorte accompli ce que je voulais, mais pas autant que je le voulais. Cela fait un moment (au moins un an) que je n'ai fait aucun SQL JOINsauf les plus basiques, et c'est assez complexe JOIN.
Voici un diagramme des tableaux pertinents de la base de données. (Il y en a beaucoup plus, mais pas nécessaire pour cette portion.)

Toutes les relations décrites dans l'image sont complètes dans la base de données - les contraintes PKet FKsont toutes configurées et opérationnelles. Aucune des colonnes décrites n'est nullcapable. Toutes les tables ont le schéma dbo.
Maintenant, j'ai une requête qui fait presque ce que je veux: c'est-à-dire, étant donné TOUT ID de SupportCategorieset TOUT ID de Languages, elle retournera soit:
S'il y a une traduction de droit approprié pour cette langue pour cette chaîne (Ie StringKeyId-> StringKeys.Idexiste, et LanguageStringTranslations StringKeyId, LanguageIdet StringTranslationIdexiste combinaison, puis il charge StringTranslations.Textpour cela StringTranslationId.
Si le LanguageStringTranslations StringKeyId, LanguageIdet la StringTranslationIdcombinaison ne pas exister, il charge la StringKeys.Namevaleur. C'est Languages.Idune donnée integer.
Ma requête, que ce soit un gâchis, est la suivante:
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
Le problème est qu'il n'est pas capable de me fournir TOUS les SupportCategorieset leurs respectifs StringTranslations.Texts'ils existent, OU leurs StringKeys.Names'ils n'existaient pas. Il est parfait pour fournir l'un d'eux, mais pas du tout. Fondamentalement, c'est pour imposer que si une langue n'a pas de traduction pour une clé spécifique, la valeur par défaut est d'utiliser StringKeys.Namecelle de StringKeys.DefaultLanguageIdtraduction. (Idéalement, il ne ferait même pas cela, mais chargerait plutôt la traduction StringKeys.DefaultLanguageId, ce que je peux faire moi-même s'il pointe dans la bonne direction pour le reste de la requête.)
J'ai passé beaucoup de temps là-dessus, et je sais que si je devais simplement l'écrire en C # (comme je le fais habituellement), ce serait fait maintenant. Je veux le faire en SQL et j'ai du mal à obtenir la sortie que j'aime.
La seule mise en garde, c'est que je veux limiter le nombre de requêtes réelles appliquées. Toutes les colonnes sont indexées et telles que je les aime pour l'instant, et sans véritable stress test, je ne peux pas les indexer davantage.
Edit: Une autre note, j'essaie de garder la base de données aussi normalisée que possible, donc je ne veux pas dupliquer les choses si je peux l'éviter.
Exemples de données
La source
dbo.SupportCategories (Intégralité):
Id  StringKeyId
0   0
1   1
2   2
dbo.Languages (185 enregistrements, dont deux par exemple):
Id  Abbreviation    Family  Name    Native
38  en  Indo-European   English English
48  fr  Indo-European   French  français, langue française
dbo.LanguagesStringTranslations (Intégralité):
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 (Intégralité):
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 (Intégralité):
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
Sortie courant
Étant donné la requête exacte ci-dessous, il génère:
Result
Billing
Sortie désirée
Idéalement, j'aimerais pouvoir omettre le spécifique SupportCategories.Idet les obtenir tous comme tels (peu importe si la langue 38 a Englishété utilisée, ou 48 French, ou TOUTE autre langue pour le moment):
Id  Result
0   Billing
1   API
2   Sales
Exemple supplémentaire
Étant donné que je devais ajouter une localisation pour French(c'est- 1 48 8à- dire ajouter à LanguageStringTranslations), la sortie changerait en (note: ceci n'est qu'un exemple, évidemment j'ajouterais une chaîne localisée à StringTranslations) (mise à jour avec l'exemple français):
Result
Les APIs
Sortie souhaitée supplémentaire
Étant donné l'exemple ci-dessus, la sortie suivante serait souhaitée (mise à jour avec l'exemple français):
Id  Result
0   Billing
1   Les APIs
2   Sales
(Oui, je sais techniquement que c'est faux du point de vue de la cohérence, mais c'est ce qui serait souhaité dans la situation.)
Éditer:
Petite mise à jour, j'ai changé la structure de la dbo.Languagestable, et en ai supprimé la Id (int)colonne, et je l'ai remplacée par Abbreviation(qui est maintenant renommée en Id, et toutes les clés étrangères et relations relatives mises à jour). D'un point de vue technique, c'est une configuration plus appropriée à mon avis, car le tableau est limité aux codes ISO 639-1, qui sont uniques au départ.
Tl; dr
Donc: la question, comment pourrais - je modifier cette requête pour renvoyer tout de SupportCategoriespuis revenir soit StringTranslations.Textpour cela StringKeys.Id, Languages.Idcombinaison, ou le StringKeys.Namesi elle ne pas exister?
Ma pensée initiale, c'est que je pourrais en quelque sorte convertir la requête actuelle en un autre type temporaire comme une autre sous-requête, et envelopper cette requête dans une autre SELECTinstruction et sélectionner les deux champs que je veux ( SupportCategories.Idet Result).
Si je ne trouve rien, je vais simplement utiliser la méthode standard que j'utilise généralement, qui consiste à charger tout cela SupportCategoriesdans mon projet C #, puis à exécuter manuellement la requête que j'ai ci-dessus contre chacun SupportCategories.Id.
Merci pour toutes suggestions / commentaires / critiques.
De plus, je m'excuse pour sa longueur absurde, je ne veux tout simplement aucune ambiguïté. Je suis souvent sur StackOverflow et je vois des questions qui manquent de substance, je ne voulais pas faire cette erreur ici.
