Así que aquí está mi escenario:
Estoy trabajando en la localización para un proyecto mío, y normalmente haría esto en el código C #, sin embargo, quiero hacerlo un poco más en SQL ya que estoy tratando de mejorar mi SQL un poco.
Entorno: SQL Server 2014 Standard, C # (.NET 4.5.1)
Nota: el lenguaje de programación en sí mismo debe ser irrelevante, solo lo incluyo para completarlo.
Así que logré lo que quería, pero no en la medida en que quería. Ha pasado un tiempo (al menos un año) desde que hice cualquier SQL JOIN
, excepto los básicos, y esto es bastante complejo JOIN
.
Aquí hay un diagrama de las tablas relevantes de la base de datos. (Hay muchos más, pero no son necesarios para esta porción).
Todas las relaciones descritas en la imagen están completas en la base de datos; las restricciones PK
y FK
están configuradas y operativas. Ninguna de las columnas descritas son null
capaces. Todas las tablas tienen el esquema dbo
.
Ahora, tengo una consulta que casi hace lo que quiero: es decir, dado CUALQUIER ID SupportCategories
y CUALQUIER ID de Languages
, devolverá:
Si hay una traducción correcta, adecuada para ese idioma para esa cadena (Ie StringKeyId
-> StringKeys.Id
existe, y en LanguageStringTranslations
StringKeyId
, LanguageId
y StringTranslationId
existe combinación, entonces carga StringTranslations.Text
para que StringTranslationId
.
Si el LanguageStringTranslations
StringKeyId
, LanguageId
y StringTranslationId
tenía la combinación no existe, entonces se carga el StringKeys.Name
valor. El Languages.Id
es un hecho integer
.
Mi consulta, ya sea un desastre, es la siguiente:
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
El problema es que no es capaz de proporcionar mí TODO del SupportCategories
y sus respectivos StringTranslations.Text
si existe, o su StringKeys.Name
si no existiera. Es perfecto para proporcionar cualquiera de ellos, pero para nada. Básicamente, es para hacer cumplir que si un idioma no tiene una traducción para una clave específica, entonces lo predeterminado es usar StringKeys.Name
cuál es la StringKeys.DefaultLanguageId
traducción. (Idealmente, ni siquiera haría eso, sino que cargaría la traducción para StringKeys.DefaultLanguageId
, lo que puedo hacer yo mismo si apunta en la dirección correcta para el resto de la consulta).
He dedicado MUCHO tiempo a esto, y sé que si solo lo escribiera en C # (como lo hago habitualmente) ya estaría hecho. Quiero hacer esto en SQL, y tengo problemas para obtener la salida que me gusta.
La única advertencia es que quiero limitar el número de consultas reales aplicadas. Todas las columnas están indexadas y, por el momento, me gustan, y sin pruebas de tensión reales no puedo indexarlas más.
Editar: Otra nota, estoy tratando de mantener la base de datos lo más normalizada posible, por lo que no quiero duplicar cosas si puedo evitarla.
Datos de ejemplo
Fuente
dbo.SupportCategories (Totalidad):
Id StringKeyId
0 0
1 1
2 2
dbo.Languages (185 registros, mostrando solo dos como ejemplos):
Id Abbreviation Family Name Native
38 en Indo-European English English
48 fr Indo-European French français, langue française
dbo.LanguagesStringTranslations (totalidad):
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 (Totalidad):
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 (totalidad):
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
Salida de corriente
Dada la consulta exacta a continuación, genera:
Result
Billing
Salida deseada
Idealmente, me gustaría poder omitir el específico SupportCategories.Id
y obtener todos ellos, así (independientemente de si English
se usó el idioma 38 , 48 French
o CUALQUIER otro idioma en este momento):
Id Result
0 Billing
1 API
2 Sales
Ejemplo adicional
Dado que debía agregar una localización para French
(es decir, agregar 1 48 8
a LanguageStringTranslations
), la salida cambiaría a (nota: esto es solo un ejemplo, obviamente agregaría una cadena localizada a StringTranslations
) (actualizado con un ejemplo francés):
Result
Les APIs
Salida deseada adicional
Dado el ejemplo anterior, se desearía el siguiente resultado (actualizado con el ejemplo francés):
Id Result
0 Billing
1 Les APIs
2 Sales
(Sí, sé que técnicamente eso está mal desde un punto de vista de coherencia, pero es lo que se desearía en la situación).
Editar:
Pequeño actualizado, cambié la estructura de la dbo.Languages
tabla, solté la Id (int)
columna y la reemplacé por Abbreviation
(que ahora se renombró Id
y se actualizaron todas las claves externas y relaciones relativas). Desde un punto de vista técnico, esta es una configuración más apropiada en mi opinión debido al hecho de que la tabla está limitada a los códigos ISO 639-1, que son únicos para empezar.
Tl; dr
Por lo tanto: la pregunta, ¿cómo podría modificar esta consulta para devolver todo a partir SupportCategories
y luego regresar, ya sea StringTranslations.Text
para que StringKeys.Id
, Languages.Id
combinados, o el StringKeys.Name
si lo hizo no existir?
Mi pensamiento inicial es que de alguna manera podría enviar la consulta actual a otro tipo temporal como otra subconsulta, y envolver esta consulta en otra SELECT
declaración y seleccionar los dos campos que quiero ( SupportCategories.Id
y Result
).
Si no encuentro nada, simplemente haré el método estándar que normalmente uso, que es cargar todo SupportCategories
en mi proyecto C #, y luego ejecutar con él la consulta que tengo arriba manualmente en cada uno SupportCategories.Id
.
Gracias por todas y cada una de las sugerencias / comentarios / críticas.
Además, me disculpo por ser absurdamente largo, simplemente no quiero ninguna ambigüedad. A menudo estoy en StackOverflow y veo preguntas que carecen de sustancia, no quería cometer ese error aquí.
fuente