Also hier ist mein Szenario:
Ich arbeite an der Lokalisierung für ein Projekt von mir, und normalerweise würde ich dies im C # -Code tun, aber ich möchte dies in SQL ein bisschen mehr tun, da ich versuche, mein SQL ein bisschen aufzupolstern.
Umgebung: SQL Server 2014 Standard, C # (.NET 4.5.1)
Hinweis: Die Programmiersprache selbst sollte irrelevant sein, ich beziehe sie nur der Vollständigkeit halber ein.
Also habe ich irgendwie erreicht, was ich wollte, aber nicht in dem Maße, wie ich wollte. Es ist eine Weile her (mindestens ein Jahr), dass ich keine SQL- JOIN
Anweisungen außer den grundlegenden ausgeführt habe, und dies ist ziemlich komplex JOIN
.
Hier ist ein Diagramm der relevanten Tabellen der Datenbank. (Es gibt viel mehr, aber nicht notwendig für diese Portion.)
Alle im Bild beschriebenen Beziehungen sind in der Datenbank vollständig - die PK
und FK
Einschränkungen sind alle eingerichtet und in Betrieb. Keine der beschriebenen Spalten ist in der null
Lage. Alle Tabellen haben das Schema dbo
.
Jetzt habe ich eine Abfrage, die beinahe das macht, was ich will: mit JEDER ID von SupportCategories
und JEDER ID vonLanguages
, wird entweder zurückgegeben:
Wenn es eine rechts richtige Übersetzung für diese Sprache für diese Saite (Ie StringKeyId
-> StringKeys.Id
existiert, und in LanguageStringTranslations
StringKeyId
, LanguageId
und StringTranslationId
Kombination vorhanden ist , dann lädt es StringTranslations.Text
für dieStringTranslationId
.
Wenn das LanguageStringTranslations
StringKeyId
, LanguageId
und StringTranslationId
Kombination ist nicht vorhanden ist , dann gibt er den StringKeys.Name
Wert. Das Languages.Id
ist eine Selbstverständlichkeitinteger
.
Meine Frage, sei es ein Chaos, lautet wie folgt:
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
Das Problem ist , dass es nicht in der Lage ist , mich zu schaffen ALL von der SupportCategories
und deren jeweilige StringTranslations.Text
wenn es vorhanden ist , oder ihr , StringKeys.Name
wenn sie nicht existiert. Es ist perfekt für die Bereitstellung eines von ihnen, aber überhaupt nicht. Grundsätzlich gilt: Wenn eine Sprache keine Übersetzung für einen bestimmten Schlüssel hat, wird standardmäßig StringKeys.Name
die StringKeys.DefaultLanguageId
Übersetzung verwendet. (Idealerweise würde es das gar nicht machen, sondern die Übersetzung für ladenStringKeys.DefaultLanguageId
, die ich selbst machen kann, wenn ich für den Rest der Abfrage in die richtige Richtung zeige.)
Ich habe viel Zeit damit verbracht, und ich weiß, wenn ich es einfach in C # schreiben würde (wie ich es normalerweise tue), wäre es jetzt erledigt. Ich möchte dies in SQL tun, und ich habe Probleme, die Ausgabe zu bekommen, die ich mag.
Die einzige Einschränkung ist, dass ich die Anzahl der tatsächlich angewendeten Abfragen begrenzen möchte. Alle Spalten sind indiziert und so, wie ich sie jetzt mag, und ohne wirklichen Stresstest kann ich sie nicht weiter indizieren.
Bearbeiten: Ein weiterer Hinweis, ich versuche, die Datenbank so normal wie möglich zu halten, damit ich Dinge nicht duplizieren möchte, wenn ich es vermeiden kann.
Beispieldaten
Quelle
dbo.SupportCategories (Entirety):
Id StringKeyId
0 0
1 1
2 2
dbo.Languages (185 Datensätze, davon nur zwei als Beispiele):
Id Abbreviation Family Name Native
38 en Indo-European English English
48 fr Indo-European French français, langue française
dbo.LanguagesStringTranslations (Entirity):
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 (Entirety):
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 (Entirety):
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
Aktueller Output
In Anbetracht der genauen Abfrage unten wird Folgendes ausgegeben:
Result
Billing
Gewünschte Ausgabe
Idealerweise möchte ich in der Lage sein, das Spezifische wegzulassen SupportCategories.Id
und alle davon zu erhalten (unabhängig davon, ob die Sprache 38 English
oder 48 French
oder JEDE andere Sprache im Moment verwendet wurde):
Id Result
0 Billing
1 API
2 Sales
Zusätzliches Beispiel
Wenn ich eine Lokalisierung für French
hinzufügen würde (dh hinzufügen 1 48 8
zu LanguageStringTranslations
), würde sich die Ausgabe ändern zu (Hinweis: Dies ist nur ein Beispiel, offensichtlich würde ich eine lokalisierte Zeichenfolge hinzufügen zu StringTranslations
) (aktualisiert mit französischem Beispiel):
Result
Les APIs
Zusätzlicher gewünschter Output
In Anbetracht des obigen Beispiels wäre die folgende Ausgabe wünschenswert (aktualisiert mit französischem Beispiel):
Id Result
0 Billing
1 Les APIs
2 Sales
(Ja, ich weiß, dass dies technisch gesehen falsch ist, aber es ist das, was in der Situation erwünscht wäre.)
Bearbeiten:
Klein aktualisiert, habe ich die Struktur der dbo.Languages
Tabelle geändert und die Id (int)
Spalte daraus entfernt und durch Abbreviation
(die jetzt in umbenannt Id
wird und alle relativen Fremdschlüssel und Beziehungen aktualisiert) ersetzt. Aus technischer Sicht ist dies meines Erachtens ein geeigneterer Aufbau, da die Tabelle auf ISO 639-1-Codes beschränkt ist, die zunächst einmalig sind.
Tl; dr
Also: die Frage, wie könnte ich diese Abfrage ändern, um alles von zurückzugeben SupportCategories
und dann entweder StringTranslations.Text
für das StringKeys.Id
, die Languages.Id
Kombination oder das zurückzugeben, StringKeys.Name
wenn es NICHT existiert?
Mein erster Gedanke ist, dass ich die aktuelle Abfrage irgendwie in einen anderen temporären Typ als eine andere Unterabfrage umwandeln und diese Abfrage in eine weitere SELECT
Anweisung einschließen und die beiden gewünschten Felder ( SupportCategories.Id
und Result
) auswählen könnte .
Wenn ich nichts finde, benutze ich einfach die Standardmethode, die ich normalerweise verwende, um alles SupportCategories
in mein C # -Projekt zu laden , und führe dann die oben angegebene Abfrage manuell für jedes Objekt aus SupportCategories.Id
.
Vielen Dank für alle Vorschläge / Kommentare / Kritik.
Außerdem entschuldige ich mich dafür, dass es absurd lang ist, ich will einfach keine Mehrdeutigkeit. Ich bin oft auf StackOverflow und sehe Fragen, denen es an Substanz mangelt. Diesen Fehler wollte ich hier nicht machen.