SQL将多行连接成一行

时间:2010-04-01 14:53:24

标签: sql sql-server database tsql

基本上,我有两个表,表A包含我想要的实际项目,表B用于语言翻译。

因此,例如,表A包含实际内容。无论何时在表中使用文本,而不是存储实际的varchar值,都会存储与表B中存储的文本相关的ID.这允许我通过向表B添加一个languageID列,为表中的同一行提供多个翻译。数据库中。

示例:

表A

  • Title(int)
  • 描述(int)
  • 其他数据....

表B

  • TextID(int) - 这是列 其值存储在其他表中
  • LanguageID(int)
  • 文字(varchar)

我的问题更多的是关于如何最好地处理这个问题的建议。理想情况下,我想要一个可以用来从表中选择的查询,并获取文本而不是表格中文本的ID。目前,当我在表格中有两个文本项目时,这就是我的工作:

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中的行(使用内部select语句中的where语句)和基于所使用的语言ID而不是文本ID的实际文本。

当我只使用一个或两个需要翻译的文本项目但是添加第三项或更多项目时,这种方法很好,它开始变得非常混乱 - 本质上是另一个左连接。

有关更好查询的建议,或者至少是在一行中处理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
    ---------------------------   

我想要的是能够从已经具有表B中的文本的表A中拉出一行,而不仅仅是ids - 并且能够为需要翻译的多个列执行此操作。

5 个答案:

答案 0 :(得分:1)

听起来你想要将一些行数据转换为列数据是有益的。在这种情况下,请在此处查找PIVOT功能

http://msdn.microsoft.com/en-us/library/ms177410.aspx

您可以向PIVOT写出一个查询文本,以便您可以获得以下输出

ID, Title, Description, LanguageId, Text1, Text2, Text3, Text4

TSQL中PIVOT的唯一缺点是您必须事先确定数据透视列的数量(编写查询时)。但是,您可以通过编写动态SQL来克服这个问题。

答案 1 :(得分:1)

查看使用函数返回需要转换的每列的数据。一个可能是:

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

这可能执行得不是很好,因为你必须为返回的每一行的每个翻译列调用一次函数(即100行的3列= 300个函数调用),但是如果你只返回一行的一行时间,它可能不会表现得那么糟糕。测试它并保持警惕。

答案 2 :(得分:1)

那么TableA基本上是一个包含TableB所有外键的表?您可以创建一个包含TableB的所有连接的视图。它将包含ID,LanguageID,TitleText,DescriptionText等列。每种语言的视图都有一行,因此您可以通过约束LanguageID来选择特定于行的语言。

答案 3 :(得分:0)

我使用例如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

然后,您可以在获取的集合上使用组功能进行选择。

答案 4 :(得分:0)

我认为这里的观点更合适,正如Jamie Ide所说的那样。在以下示例中,您可以从第二个查询中创建LocalisedMessagesLocalisedArticles作为视图。

-- 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;