【问题标题】:How to Join Multiple Detail Tables to Header Table如何将多个详细信息表连接到标题表
【发布时间】:2011-07-05 06:35:21
【问题描述】:

我需要将一个标题表和六个详细信息表组合成一个结果。为了演示,我创建了一个非常简单的示例,如下所示:

DECLARE @MyHeader TABLE (HeaderPK int, Name varchar(100), Total smallmoney) 插入@MyHeader(HeaderPK,名称,总计) 选择 1,“ABC 公司”,1600 DECLARE @MyDetail1 TABLE (Detail1PK int, HeaderFK int, Detail1Description varchar(100), Detail1Amount smallmoney) 插入@MyDetail1(Detail1PK、HeaderFK、Detail1Description、Detail1Amount) 选择 1, 1, '详细信息 1A', 100 UNION SELECT 2, 1, '详细信息 1B', 300 声明@MyDetail2 TABLE(Detail2PK int,HeaderFK int,Detail2Description varchar(100),Detail2AmountA smallmoney,Detail2AmountB smallmoney) 插入@MyDetail2(Detail2PK、HeaderFK、Detail2Description、Detail2AmountA、Detail2AmountB) 选择 1, 1, '详细信息 2A', 100, 100 UNION SELECT 2, 1, '详细信息 2B', 200, 200 UNION SELECT 3, 1, '详细信息 3C', 300, 300 -- 返回 2 行,预期为 2 选择 我的标题。* ,MyDetail1.* 从 @MyHeader 我的标题 在 MyHeader.HeaderPK = MyDetail1.HeaderFK 上完全加入 @MyDetail1 MyDetail1 订购方式 我的Detail1.Detail1PK -- 返回 6 行,预期为 3 选择 我的标题。* ,MyDetail1.* ,MyDetail2.* 从 @MyHeader 我的标题 在 MyHeader.HeaderPK = MyDetail1.HeaderFK 上完全加入 @MyDetail1 MyDetail1 在 MyHeader.HeaderPK = MyDetail2.HeaderFK 上完全加入 @MyDetail2 MyDetail2

注意事项:

  • MSSQL 2008R2
  • 每个详细信息表将有大约 0 到 15 条记录。

在示例中,详细信息表具有相似的结构。在生产系统中,它们是非常不同的。

【问题讨论】:

    标签: sql-server sql-server-2008


    【解决方案1】:

    这将根据详细信息表的向上检索槽为您提供所需的内容。坦率地说,我展示这段代码只是因为它有点有趣。通常没有充分的理由进行此类查询。

    为了传输效率,最好不要重复标题列中的数据:

    Header
    Detail1
    Detail2
    Detail3
    etc
    

    SQL 查询

    DECLARE @MyHeader TABLE (HeaderPK int, Name varchar(100), Total smallmoney)
    INSERT INTO @MyHeader (HeaderPK, Name, Total) 
        SELECT 1, 'ABC Company', 1600
    
    DECLARE @MyDetail1 TABLE (Detail1PK int, HeaderFK int, Detail1Description varchar(100), Detail1Amount smallmoney)
    INSERT INTO @MyDetail1 (Detail1PK, HeaderFK, Detail1Description, Detail1Amount) 
        SELECT 1, 1, 'Detail 1A', 100
        UNION SELECT 2, 1, 'Detail 1B', 300
    
    DECLARE @MyDetail2 TABLE (Detail2PK int, HeaderFK int, Detail2Description varchar(100), Detail2AmountA smallmoney, Detail2AmountB smallmoney)
    INSERT INTO @MyDetail2 (Detail2PK, HeaderFK, Detail2Description, Detail2AmountA, Detail2AmountB) 
        SELECT 1, 1, 'Detail 2A', 100, 100
        UNION SELECT 2, 1, 'Detail 2B', 200, 200
        UNION SELECT 3, 1, 'Detail 3C', 300, 300
    
    DECLARE @MyDetail3 TABLE (Detail3PK int, HeaderFK int, Detail3Description sysname)
    INSERT INTO @MyDetail3 (Detail3PK, HeaderFK, Detail3Description) 
        SELECT 1, 1, 'Detail 3A'
        UNION SELECT 2, 1, 'Detail 3B'
    
    -- Returns 6 Rows, Expected 3
    SELECT 
        MyHeader.*
        ,MyDetail1.*
        ,MyDetail2.*
        ,MyDetail3.*
    FROM
        @MyHeader MyHeader
        LEFT JOIN
        (SELECT *, RN=ROW_NUMBER() over (order by Detail1PK) FROM @MyDetail1) MyDetail1
        FULL JOIN
        (SELECT *, RN=ROW_NUMBER() over (order by Detail2PK) FROM @MyDetail2) MyDetail2
        ON MyDetail1.HeaderFK = MyDetail2.HeaderFK AND MyDetail1.RN = MyDetail2.RN
        FULL JOIN
        (SELECT *, RN=ROW_NUMBER() over (order by Detail3PK) FROM @MyDetail3) MyDetail3
        ON COALESCE(MyDetail1.HeaderFK, MyDetail2.HeaderFK) = MyDetail3.HeaderFK
           AND COALESCE(MyDetail1.RN, MyDetail2.RN) = MyDetail3.RN
        ON MyHeader.HeaderPK = COALESCE(MyDetail1.HeaderFK, MyDetail2.HeaderFK)
    

    您可以通过向 COALSECE 添加更多内容来扩展更多详细信息表,因此第 6 个详细信息表将是

        FULL JOIN
        (SELECT *, RN=ROW_NUMBER() over (order by Detail6PK) FROM @MyDetail6) MyDetail6
        ON COALESCE(MyDetail1.HeaderFK, MyDetail2.HeaderFK, MyDetail3.HeaderFK,
            MyDetail4.HeaderFK, MyDetail5.HeaderFK) = MyDetail6.HeaderFK
           AND COALESCE(MyDetail1.RN, MyDetail2.RN, MyDetail3.RN, MyDetail4.RN, MyDetail5.RN) = MyDetail6.RN
    

    【讨论】:

    • 谢谢理查德。我想出了一个类似的解决方案。我将 ROW_NUMBER() 添加到每个 Detail 表中,然后执行 FULL JOIN。但我喜欢你在 FROM 子句中的做法。谢谢。
    【解决方案2】:

    对每一行执行每个连接。因此,如果您在具有匹配 2 行的表上加入具有 1 行的表,您将得到 2 行。如果你在一个有 3 行的表上加入它,你会得到 6 行,依此类推。

    一种解决方案是执行多个 SQL 语句,每个明细表一个。客户端可以使用SqlDataReader.NextResult() 等价物移动到下一个结果。

    另一种选择是将union all不同的语句放在一起;这会导致列过多,但行数会减少。

    【讨论】:

    • SqlDataReader 是一个选项,解决方案必须在 T-SQL 中。我会尝试 UNION ALL 的建议。
    【解决方案3】:

    当您将@MyDetail1 与@MyHeader 匹配时,您会得到两行。如果您将@MyDetail2 与@MyHeader 匹配,您将获得三行。但是,在您上次加入时,您没有告诉系统如何将@MyDetail1 中的两行与@MyDetail2 中的三行匹配,因此系统假定您希望将@MyDetail1 的每一行与@MyDetail2 的每一行匹配,结果为2 x 3 行或 6 行。

    解决方案:

    1. 如果@MyDetail1 和@MyDetail2 应该是直接相关的,那么缺少一个Join。
    2. 如果您只需要@MyDetail1 和@MyDetail2,请使用两个Select 子句。

    【讨论】:

      猜你喜欢
      • 2017-01-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多