Quindi, ecco il mio scenario:
Sto lavorando su Localization per un mio progetto e in genere vorrei fare questo nel codice C #, tuttavia voglio farlo un po 'di più in SQL poiché sto cercando di potenziare un po' il mio SQL.
Ambiente: SQL Server 2014 Standard, C # (.NET 4.5.1)
Nota: il linguaggio di programmazione stesso dovrebbe essere irrilevante, lo sto solo includendo per completezza.
Quindi ho realizzato quello che volevo, ma non nella misura in cui volevo. È passato un po 'di tempo (almeno un anno) da quando ho fatto qualsiasi SQL JOIN
tranne quelli di base, e questo è piuttosto complesso JOIN
.
Ecco uno schema delle tabelle pertinenti del database. (Ce ne sono molti altri, ma non necessari per questa porzione.)
Tutte le relazioni descritte nell'immagine sono complete nel database: i vincoli PK
e FK
sono tutti impostati e funzionanti. Nessuna delle colonne descritte è in null
grado. Tutte le tabelle hanno lo schema dbo
.
Ora, ho una query che fa quasi quello che voglio: cioè, dato QUALSIASI ID di SupportCategories
e QUALSIASI ID di Languages
, restituirà:
Se c'è una traduzione destro adeguata per tale lingua per quella stringa (Ie StringKeyId
-> StringKeys.Id
esiste, e in LanguageStringTranslations
StringKeyId
, LanguageId
e StringTranslationId
esiste combinazione, allora carichi StringTranslations.Text
per quella StringTranslationId
.
Se la LanguageStringTranslations
StringKeyId
, LanguageId
e StringTranslationId
combinazione ha NON esiste, allora carica il StringKeys.Name
valore. Il Languages.Id
è un dato integer
.
La mia domanda, sia essa un casino, è la seguente:
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
Il problema è che non è in grado di fornire me ALL del SupportCategories
e la loro rispettiva StringTranslations.Text
se esiste, o il loro StringKeys.Name
se non esistesse. È perfetto per fornire uno di loro, ma per niente. Fondamentalmente, è per imporre che se una lingua non ha una traduzione per una chiave specifica, l'impostazione predefinita è quella StringKeys.Name
che è di StringKeys.DefaultLanguageId
traduzione. (Idealmente, non lo farebbe nemmeno, ma caricherà invece la traduzione StringKeys.DefaultLanguageId
, cosa che posso fare da solo se puntato nella direzione giusta per il resto della query.)
Ho trascorso MOLTO tempo su questo, e so che se dovessi semplicemente scriverlo in C # (come faccio di solito) ormai sarebbe stato fatto. Voglio farlo in SQL e ho problemi a ottenere l'output che mi piace.
L'unica avvertenza è che voglio limitare il numero di query effettive applicate. Tutte le colonne sono indicizzate e come quelle che mi piacciono per ora, e senza prove di stress reali non posso indicizzarle ulteriormente.
Modifica: un'altra nota, sto cercando di mantenere il database il più possibile normalizzato, quindi non voglio duplicare le cose se posso evitarlo.
Dati di esempio
fonte
dbo.SupportCategories (intero):
Id StringKeyId
0 0
1 1
2 2
dbo.Languages (185 record, mostrandone solo due per esempi):
Id Abbreviation Family Name Native
38 en Indo-European English English
48 fr Indo-European French français, langue française
dbo.LanguagesStringTranslations (Entirety):
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
Uscita corrente
Data la query esatta di seguito, produce:
Result
Billing
Uscita desiderata
Idealmente, vorrei essere in grado di omettere lo specifico SupportCategories.Id
e di ottenerli tutti, così (indipendentemente dal fatto che English
fosse usata la lingua 38 , o 48 French
, o QUALSIASI altra lingua al momento):
Id Result
0 Billing
1 API
2 Sales
Esempio aggiuntivo
Dato che dovevo aggiungere una localizzazione per French
(cioè aggiungere 1 48 8
a LanguageStringTranslations
), l'output cambierebbe in (nota: questo è solo un esempio, ovviamente aggiungerei una stringa localizzata a StringTranslations
) (aggiornato con l'esempio francese):
Result
Les APIs
Uscita desiderata aggiuntiva
Dato l'esempio sopra, sarebbe desiderato il seguente output (aggiornato con l'esempio francese):
Id Result
0 Billing
1 Les APIs
2 Sales
(Sì, so tecnicamente che è sbagliato dal punto di vista della coerenza, ma è ciò che sarebbe auspicabile nella situazione.)
Modificare:
Piccolo aggiornato, ho modificato la struttura della dbo.Languages
tabella e lasciato cadere la Id (int)
colonna da essa, sostituendola con Abbreviation
(che ora è stata rinominata Id
e tutte le relative chiavi esterne e le relazioni aggiornate). Da un punto di vista tecnico, questa è un'impostazione più appropriata a mio avviso a causa del fatto che la tabella è limitata ai codici ISO 639-1, che sono univoci per cominciare.
Tl; dr
Quindi: la questione, come potrei modificare questo query per restituire tutto da SupportCategories
e poi tornare sia StringTranslations.Text
per quella StringKeys.Id
, Languages.Id
combinazione, o il StringKeys.Name
caso lo ha fatto non esiste?
Il mio pensiero iniziale è che potrei in qualche modo trasmettere la query corrente a un altro tipo temporaneo come un'altra subquery e racchiudere questa query in un'altra SELECT
enunciazione e selezionare i due campi che desidero ( SupportCategories.Id
e Result
).
Se non trovo nulla, farò semplicemente il metodo standard che di solito uso, che è quello di caricare tutto SupportCategories
nel mio progetto C #, e quindi eseguo la query che ho sopra manualmente su ciascuno SupportCategories.Id
.
Grazie per qualsiasi suggerimento / commento / critica.
Inoltre, mi scuso per il fatto che sia assurdamente lungo, ma non voglio alcuna ambiguità. Sono spesso su StackOverflow e vedo domande che mancano di sostanza, non volevo fare questo errore qui.