【问题标题】:SQL left join with multiple rows into one rowSQL将多行左连接成一行
【发布时间】:2010-04-01 14:53:24
【问题描述】:

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

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

例子:

表 A

  • 标题(整数)
  • 说明(整数)
  • 其他数据....

表 B

  • TextID (int) - 这是列 其值存储在其他表中
  • 语言 ID(整数)
  • 文本(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
    ---------------------------   

我想要的是能够从表 A 中提取一行,该行已经包含表 B 中的文本,而不仅仅是 id - 并且能够为需要翻译的几个列执行此操作。

【问题讨论】:

  • 你能澄清这个问题吗?您不断在谈论表 A/表 B 和实际表名之间来回切换,这使得这真的很混乱。您可以在整个问题中仅引用实际的表名吗?您还可以引用示例查询中不在表 (ID) 中的字段。最后,如果您在每个表中显示一些示例行和预期的输出,将会有所帮助。
  • 如果有帮助,只需添加表格示例

标签: sql sql-server database tsql


【解决方案1】:

听起来您希望将一些行数据转换为列数据。在这种情况下,请在此处查找 PIVOT 功能

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

您可以编写一个查询来透视文本,以便获得以下输出

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

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

【讨论】:

  • 我认为这不是我要找的 - 我不想在行列名中创建值,只是将不同行的值组合成具有不同列名的一行跨度>
【解决方案2】:

研究使用函数为需要翻译的每一列返回数据。一种可能是:

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 次函数调用),但如果您只返回一行时间,它可能不会表现得那么差。测试它并保持警惕。

【讨论】:

  • 是的,这就是我认为我将要做的更清洁的解决方案 - 我通常一次只拉出一行,所以这可能会正常工作
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    我用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
    

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

    【讨论】:

      【解决方案5】:

      我认为这里的观点更合适,正如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;
      

      【讨论】:

        猜你喜欢
        • 2021-05-06
        • 1970-01-01
        • 2017-06-19
        • 2011-06-24
        • 2020-11-03
        • 2013-05-16
        • 1970-01-01
        • 1970-01-01
        • 2011-08-06
        相关资源
        最近更新 更多