连接三个表

时间:2016-06-13 08:29:41

标签: sql sql-server tsql sql-server-2014

我正在使用SQL Server 2014,我遇到了查询问题。我有三张桌子。 ReportClothingObservationHygieneObservation各10个组成。我这样做的方法是在两种类型的观察中分别引用10行ReportId Report,每个报告总共20次观察。我想选择一个报告的所有行。当我尝试这样做时,我得到100行。我的目标是获得10行或20行NULL值。这仅用于测试目的,因此Report仅包含1行,ClothingObservationHygieneObservation各包含10行,所有行都引用现有报告的ReportId

我的表格,为清晰起见省略了详细信息:

CREATE TABLE HygieneObservation
(
    HygieneObservationId int PRIMARY KEY IDENTITY NOT NULL,
    ...
    ReportId int NOT NULL
)

CREATE TABLE ClothingObservation
(
    ClothingObservationId int PRIMARY KEY IDENTITY NOT NULL,
    ...
    ReportId int NOT NULL
)

CREATE TABLE Report
(
    ReportId int PRIMARY KEY IDENTITY NOT NULL,
    Period Date NOT NULL,
    Reporter nvarchar(8) NOT NULL,
    DepartmentId int NOT NULL
)

我的查询:

SELECT 
    Report.ReportId, 
    Report.Period, 
    Report.Reporter, 
    Report.DepartmentId, 

    ClothingObservation.ClothingObservationId,
    HygieneObservation.HygieneObservationId

FROM Report

LEFT JOIN ClothingObservation ON
    (ClothingObservation.ReportId = Report.ReportId)
LEFT JOIN HygieneObservation ON
    (HygieneObservation.ReportId = Report.ReportId)

GROUP BY
    Report.ReportId,
    Period,
    Reporter,
    DepartmentId,

    ClothingObservation.ClothingObservationId,
    HygieneObservation.HygieneObservationId

这给了我100行,据我所知,因为ClothingObservation中的每一行都匹配HygieneObservation中的每一行。我认为使用GROUP BY会导致重复删除,但我显然做错了。任何提示?

编辑:这是我现在的数据(详情略去)。

Report:

ReportId    Period     Reporter DepartmentId
----------- ---------- -------- ------------
1           2016-05-01 username 1

ClothingObservation:

ClothingObservationId ... ReportId
--------------------- ... -----------
1                     ... 1
2                     ... 1
3                     ... 1
4                     ... 1
5                     ... 1
6                     ... 1
7                     ... 1
8                     ... 1
9                     ... 1
10                    ... 1

HygieneObservation:

HygieneObservationId ... ReportId
-------------------- ... -----------
3                    ... 1
4                    ... 1
5                    ... 1
6                    ... 1
7                    ... 1
8                    ... 1
9                    ... 1
10                   ... 1
12                   ... 1
13                   ... 1

编辑2:如果我运行这两个查询,我得到了我想要的输出(再次,从结果中省略了不相关的细节):

SELECT * FROM Report
    LEFT JOIN ClothingObservation ON
    (ClothingObservation.ReportId = Report.ReportId)
SELECT * FROM Report
    LEFT JOIN HygieneObservation ON
    (HygieneObservation.ReportId = Report.ReportId)

ReportId    Period     Reporter DepartmentId ClothingObservationId ...  ReportId
----------- ---------- -------- ------------ --------------------- ...- -----------
1           2016-05-01 username 1            1                     ...  1
1           2016-05-01 username 1            2                     ...  1
1           2016-05-01 username 1            3                     ...  1
1           2016-05-01 username 1            4                     ...  1
1           2016-05-01 username 1            5                     ...  1
1           2016-05-01 username 1            6                     ...  1
1           2016-05-01 username 1            7                     ...  1
1           2016-05-01 username 1            8                     ...  1
1           2016-05-01 username 1            9                     ...  1
1           2016-05-01 username 1            10                    ...  1

ReportId    Period     Reporter DepartmentId HygieneObservationId ... ReportId
----------- ---------- -------- ------------ -------------------- ... -----------
1           2016-05-01 username 1            3                    ... 1
1           2016-05-01 username 1            4                    ... 1
1           2016-05-01 username 1            5                    ... 1
1           2016-05-01 username 1            6                    ... 1
1           2016-05-01 username 1            7                    ... 1
1           2016-05-01 username 1            8                    ... 1
1           2016-05-01 username 1            9                    ... 1
1           2016-05-01 username 1            10                   ... 1
1           2016-05-01 username 1            12                   ... 1
1           2016-05-01 username 1            13                   ... 1

我的目标是通过一个查询获得此输出(或类似的内容)。

2 个答案:

答案 0 :(得分:2)

您也可以尝试使用以下查询:

SELECT
    ReportId = ISNULL(v1.ReportId, v2.ReportId),
    Period = ISNULL(v1.Period, v2.Period), 
    Reporter = ISNULL(v1.Reporter, v2.Reporter), 
    DepartmentId = ISNULL(v1.DepartmentId, v2.DepartmentId),
    v1.ClothingObservationId, 
    v2.HygieneObservationId
FROM
(
    SELECT 
        RowNumber = ROW_NUMBER() OVER(Partition BY r.ReportId ORDER BY c.ClothingObservationId),
        r.ReportId, 
        r.Period, 
        r.Reporter, 
        r.DepartmentId, 
        c.ClothingObservationId
    FROM 
        Report r
        LEFT JOIN ClothingObservation c ON c.ReportId = r.ReportId) v1
FULL JOIN 
(
    SELECT 
        RowNumber = ROW_NUMBER() OVER(Partition BY r.ReportId ORDER BY h.HygieneObservationId),
        r.ReportId, 
        r.Period, 
        r.Reporter, 
        r.DepartmentId, 
        h.HygieneObservationId
    FROM Report r
    LEFT JOIN HygieneObservation h ON h.ReportId = r.ReportId) v2 ON v1.RowNumber = v2.RowNumber AND v1.ReportId = v2.ReportId
ORDER BY ReportId

答案 1 :(得分:1)

正在发生的事情是,将报告(1行)加入到ClothingObservation(10行)会产生10行(1 x 10),然后加入HygieneObservation(10行),这样就可以得到100.这种情况发生的原因是因为在初始连接之后,您有10行具有相同的ReportID,因此下一个连接将获取这10行中的每一行并连接到HygieneObservation中的10行。

“20行具有NULL值”的解决方案:

SELECT 
    Report.ReportId, 
    Report.Period, 
    Report.Reporter, 
    Report.DepartmentId, 
    ClothingObservation.ClothingObservationId,
     NULL AS HygieneObservationId
FROM Report
LEFT JOIN ClothingObservation ON
    (ClothingObservation.ReportId = Report.ReportId)
UNION ALL
SELECT 
    Report.ReportId, 
    Report.Period, 
    Report.Reporter, 
    Report.DepartmentId, 
    NULL AS ClothingObservationId,
    HygieneObservation.HygieneObservationId
FROM Report
LEFT JOIN HygieneObservation ON
    (HygieneObservation.ReportId = Report.ReportId)

工作原理:
你基本上写了两个单独的查询:一个连接Report和ClothingObservation,另一个连接Report到HygieneObservation。然后,将两个查询与UNION ALL组合在一起。

“获取10行”的解决方案

这很复杂,因为它涉及我称之为“垂直合并”或“合并加入”。以下是查询(更新:我已对其进行了测试)。

SELECT 
    Report.ReportId, 
    Report.Period, 
    Report.Reporter, 
    Report.DepartmentId, 

    MergedObservations.ClothingObservationId,
    MergedObservations.HygieneObservationId
FROM Report
    LEFT JOIN 
            ( SELECT COALESCE( ClothingObservation.ReportID, HygieneObservation.ReportID ) AS ReportID,
                    HygieneObservationID, ClothingObservationID -- Add appropriate columns
            FROM
                        ( SELECT ROW_NUMBER() OVER( PARTITION BY ReportID ORDER BY ClothingObservationID ) AS ResultID, ReportID, ClothingObservationID
                        FROM ClothingObservation ) AS ClothingObservation
               FULL OUTER JOIN
                        ( SELECT ROW_NUMBER() OVER( PARTITION BY ReportID ORDER BY HygieneObservationID  ) AS ResultID, ReportID, HygieneObservationID
                        FROM HygieneObservation ) AS HygieneObservation
                    ON ClothingObservation.ReportID = HygieneObservation.ReportID
                        AND ClothingObservation.ResultID = HygieneObservation.ResultID
            ) AS MergedObservations
        ON Report.ReportID = MergedObservations.ReportID

工作原理:
因为ClothingObservation和HygieneObservationId彼此不直接相关并且每个ReportID具有不同的行数,所以我使用ROW_NUMBER()函数来生成连接键。然后,我使用ReportID和ROW_NUMBER()函数的输出进行“合并连接”。

样本数据

我已将您的示例数据转换为可用的表数据,以测试上述查询。

CREATE TABLE Report( ReportId INT, Period DATETIME, Reporter VARCHAR( 20 ), DepartmentId INT )
CREATE TABLE ClothingObservation( ClothingObservationID INT, ReportId INT )
CREATE TABLE HygieneObservation( HygieneObservationID INT, ReportId INT )

INSERT INTO Report
VALUES( 1, '2016-05-01', 'username', 1 )

INSERT INTO ClothingObservation
VALUES
( 1, 1 ), ( 2, 1 ), ( 3, 1 ), ( 4, 1 ), ( 5, 1 ), ( 6, 1 ), ( 7, 1 ), ( 8, 1 ), ( 9, 1 ), ( 10, 1 )

INSERT INTO HygieneObservation
VALUES
( 3, 1 ), ( 4, 1 ), ( 5, 1 ), ( 6, 1 ), ( 7, 1 ), ( 8, 1 ), ( 9, 1 ), ( 10, 1 ), ( 11, 1 ), ( 12, 1 ), ( 13, 1 )