SQL left join с несколькими строками в одну строку
В основном, у меня есть две таблицы, таблица A содержит фактические элементы, которые я хочу получить, а таблица B используется для языковых переводов.
Так, например, таблица А содержит фактическое содержание. Всякий раз, когда текст используется в таблице, вместо хранения фактических значений varchar сохраняются идентификаторы, которые относятся обратно к тексту, хранящемуся в таблице B. Это позволяет мне, добавив столбец languageID в таблицу B, иметь несколько переводов для одной и той же строки в таблице B. база данных.
Пример:
Таблица А
- Title (int)
- описание (int)
- Другие Данные....
Таблица B
- TextID (int) - это столбец
значение которого хранится в других таблицах - LanguageID (int)
- текст (varchar)
Мой вопрос-это скорее призыв к предложениям о том, как лучше всего справиться с этим. В идеале мне нужен запрос, который я могу использовать, чтобы выбрать из таблицы и получить текст, а не идентификаторы текста из таблицы. В настоящее время, когда у меня есть два текстовых элемента в таблице, это то, что я делаю:
SELECT C.ID, C.Title, D.Text AS Description
FROM
(SELECT A.ID, A.Description, B.Text AS Title
FROM TableA A, TranslationsTable B
WHERE A.Title = B.TextID AND B.LanguaugeID = 1) C
LEFT JOIN TranslationsTable D
ON C.Description = D.TextID AND D.LanguaugeID = 1
Этот запрос дает мне строку из таблицы A, которую я ищу (используя операторы where во внутреннем операторе select) с фактическим текстом, основанным на идентификаторе языка, используемом вместо текстовых идентификаторов.
Это прекрасно работает, когда я использую только один или два текстовых элемента, которые должны быть переведены, но добавляя третий элемент или больше, он начинает становиться очень грязным - по сути, еще один левый присоединяйтесь к примеру сверху.
Есть предложения по улучшению запроса или, по крайней мере, хорошему способу обработки 3 или более текстовых элементов в одной строке?
В соответствии с предложениями я добавил пример двух таблиц:
Table A
---------------------------
ID |Title |Description
---------------------------
1 |1 |2
---------------------------
2 |3 |4
---------------------------
Table B (Translations Table)
---------------------------
ID |LanguaugeID|Text
---------------------------
1 |1 |Here is title one
---------------------------
1 |2 |Here is a title one in espanol
---------------------------
2 |1 |Here is description one
---------------------------
2 |2 |Here is description one in espanol
---------------------------
3 |1 |Title 2
---------------------------
4 |1 |Description 2
---------------------------
Что я хочу, так это иметь возможность вытащить строку из таблицы A, в которой уже есть текст из таблицы B, а не только идентификаторы - и иметь возможность сделать это для нескольких столбцов, которые нуждаются в переводе.
5 ответов:
Рассмотрите возможность использования функции для возврата данных для каждого столбца, требующего перевода. Одним из них может быть:
CREATE FUNCTION dbo.fTranslate (@TextId int, @LanguageId int) RETURNS nvarchar(100) -- Should use nvarchar, and set to max length of string AS BEGIN SELECT [Text] -- Reserved wordin SQL, rename that column! FROM TableB WHERE TextId = @TextId And LanguageId = @LanguageId ENDЗатем запишите запрос следующим образом:
SELECT ID, dbo.fTranslate(Title, @LanguageId) Title, dbo.fTranslate(Description, @LanguageId) Description FROM TableAЭто может работать не особенно хорошо, так как вы должны вызывать функцию один раз для каждого переведенного столбца для каждой возвращаемой строки (т. е. 3 столбца для 100 строк = 300 вызовов функций), но если вы возвращаете только одну строку за раз, она может работать не так плохо. Проверьте это и будьте осторожны.
Похоже, что вы хотите получить выгоду от преобразования некоторых данных строк в данные столбцов. В этом случае, после вращения
Http://msdn.microsoft.com/en-us/library/ms177410.aspx
Вы можете написать запрос для вывода текста, чтобы получить следующий вывод
ID, Title, Description, LanguageId, Text1, Text2, Text3, Text4Единственным недостатком PIVOT в TSQL является то, что вы должны заранее определить количество столбцов pivot (при написании запроса). Вы можете, однако преодолейте это, написав динамический SQL.
Таким образом, TableA-это в основном таблица со всеми внешними ключами к TableB? Можно создать представление, включающее все соединения с таблицей B. Он будет иметь столбцы для ID, LanguageID, TitleText, DescriptionText и т. д. Представление будет иметь одну строку для каждого языка, так что вы можете выбрать язык для конкретной строки, ограничивая LanguageID.
Я думаю, что здесь более уместен взгляд, как сказал Джейми Ide. В следующем примере можно создать
LocalisedMessagesиLocalisedArticlesиз второго запроса в виде представлений.-- i18n.sql (TransactSQL) create table #Languages(ID int, Code char(2), LanguageName varchar(32)); create table #_Messages(ID int, DefaultText nvarchar(1024)); create table #Translations(ID int, MessageID int, LanguageID int, TranslatedText nvarchar(1024)); create table #Articles(ID int, TitleID int, ContentID int); insert into #Languages(ID, Code, LanguageName) values ( 1, 'en', 'English' ), ( 2, 'es', 'Espagnol' ); insert into #_Messages(ID, DefaultText) values ( 1, 'Title 1' ), ( 2, 'Content 1' ), ( 3, 'Title 2' ), ( 4, 'Content 2' ); insert into #Translations ( ID, MessageID, LanguageID, TranslatedText ) values ( 1, 1, 1, 'Title 1' ), ( 2, 2, 1, 'Content 1' ), ( 3, 1, 2, 'Título 1' ), ( 4, 2, 2, 'Contenido 1' ), ( 5, 3, 1, 'Title 2' ), ( 6, 4, 1, 'Content 2' ), ( 7, 3, 2, 'Título 2' ), ( 8, 4, 2, 'Contenido 2' ); insert into #Articles(ID, TitleID, ContentID) values ( 1, 1, 2 ), ( 2, 3, 4 ); select _m.ID, _m.DefaultText, _t.TranslatedText, _l.ID as LanguageID, _l.Code as LanguageCode from #_Messages _m inner join #Translations _t on _t.MessageID = _m.ID inner join #Languages _l on _l.ID = _t.LanguageID; with LocalisedMessages as ( select _m.ID, _m.DefaultText, _t.TranslatedText, _l.ID as LanguageID, _l.Code as LanguageCode from #_Messages _m inner join #Translations _t on _t.MessageID = _m.ID inner join #Languages _l on _l.ID = _t.LanguageID ), LocalisedArticles as ( select _a.ID, _l.Code as LanguageCode , isnull(_mt.TranslatedText, _mt.DefaultText) as Title , isnull(_mc.TranslatedText, _mc.DefaultText) as Content from #Articles _a inner join LocalisedMessages _mt on _mt.ID = _a.TitleID inner join LocalisedMessages _mc on _mc.ID = _a.ContentID inner join #Languages _l on _l.ID = _mt.LanguageID and _l.ID = _mc.LanguageID ) select * from LocalisedArticles; drop table #Articles; drop table #Translations; drop table #_Messages; drop table #Languages;
Я делаю это с помощью
UNION, например, 3 различныхSELECT STATEMENTS(по одному для каждого языка), где вы предоставляете пустые столбцы для языка, над которым вы не работаете, что-то вродеSELECT A.ITM_ID, A.ITM_LOOKUPNAME, B.ITM_DESC_TEXT as TitleFR, B.ITM_DESC_UNIT AS UnitFR,'' as TitleEN, '' as UnitEN FROM Item A, Item_description B WHERE A.ITM_ID = B.ITM_ID AND B.ITM_DESC_LANG = 1 UNION SELECT A.ITM_ID, A.ITM_LOOKUPNAME, '' as TitleFR, '' as UnitFR, B.ITM_DESC_TEXT as TitleEN, B.ITM_DESC_UNIT AS UnitEN FROM Item A, Item_description B WHERE A.ITM_ID = B.ITM_ID AND B.ITM_DESC_LANG = 2Затем вы можете сделать выбор с помощью функции group на приобретенном наборе.
Comments