SQL Server - 将多个查询结果合并到一个结果集中

时间:2014-11-27 14:08:48

标签: sql-server sql-server-2008-r2

我有三个查询,每个查询返回一列。

SELECT Name FROM Tenant
SELECT Name FROM Space
SELECT ID FROM Contracts

表定义是:

Tenant    (ID, Name)
Space     (ID, Name, TenantID)
Contracts (ID, TenantID)

我在这些表格中的信息是:

+----+---------+
| id |  name   |
+----+---------+
|  1 | Tenant1 |
|  2 | Tenant2 |
|  3 | Tenant3 |
+----+---------+

+----+------+----------+
| id | name | tenantID |
+----+------+----------+
|  1 | S1   |        1 |
|  2 | S2   |        1 |
|  3 | S3   |        2 |
|  4 | S4   |        3 |
|  5 | S5   |        3 |
+----+------+----------+

+----+----------+
| id | tenantID |
+----+----------+
|  1 |        1 |
|  2 |        1 |
|  3 |        2 |
|  4 |        2 |
|  5 |        2 |
|  6 |        3 |
+----+----------+

如何编写查询以实现以下结构?

+----------+-------+----------+
|  tenant  | space | contract |
+----------+-------+----------+
| tenant 1 | S1    |        1 |
|          | S2    |        2 |
| tenant 2 | S3    |        3 |
|          |       |        4 |
|          |       |        5 |
| tenant 3 | S4    |        6 |
|          | S5    |          |
+----------+-------+----------+

我在Tenant和Contracts表之间有链接,但我不希望他们考虑租户之间的空格,我不希望任何列中的值重复。

我尝试过使用连接,但如果它们之间存在匹配,它们显然会复制列中的值。

SELECT T.NAME 'Tname',
    S.NAME 'Sname',
    C.ID
FROM Tenant T
LEFT JOIN Space S
    ON T.ID = S.TenantID
LEFT JOIN Contracts C
    ON T.ID = C.TenantID

我也尝试将其关联到子查询中并使用ROW_NUMBER()并结合一些CASE语句来实现所需的格式,但不是很成功。

以下是包含一些示例数据的SQLFiddle

非常感谢任何有用的建议/意见或链接。

3 个答案:

答案 0 :(得分:4)

WITH SpaceRow AS (
    SELECT tenantid
          ,name
          ,ROW_NUMBER() OVER (PARTITION BY tenantid ORDER BY id) AS RowNumber
    FROM Space
)
,ContractRow AS (
    SELECT tenantid
          ,id
          ,ROW_NUMBER() OVER (PARTITION BY tenantid ORDER BY id) AS RowNumber
    FROM Contracts
)
,SpaceContracts AS (
    SELECT COALESCE(SpaceRow.tenantid, ContractRow.tenantid) AS tenantid
          ,COALESCE(SpaceRow.RowNumber, ContractRow.RowNumber) AS RowNumber
          ,SpaceRow.name AS SpaceName
          ,ContractRow.id AS ContractId
    FROM SpaceRow
         FULL OUTER JOIN ContractRow
              ON SpaceRow.tenantid = ContractRow.tenantid
                 AND SpaceRow.RowNumber = ContractRow.RowNumber
)
SELECT CASE WHEN SpaceContracts.RowNumber IS NULL  
                 OR SpaceContracts.RowNumber = 1
            THEN Tenant.name 
            ELSE NULL
        END AS TenantName
       ,SpaceContracts.SpaceName
       ,SpaceContracts.ContractId
FROM Tenant
     LEFT JOIN SpaceContracts
         ON SpaceContracts.tenantid = Tenant.id
ORDER BY Tenant.id
        ,SpaceContracts.RowNumber

答案 1 :(得分:2)

嗯,这可能是解决问题的方法之一:

; WITH spaces AS (
SELECT t.ID AS tID, t.NAME AS tName, s.NAME AS sName, ROW_NUMBER() OVER (PARTITION BY t.ID ORDER BY s.ID) AS sSeq
FROM tenant t
LEFT JOIN space s ON t.ID = s.TenantID ),
contrs AS (
SELECT t.ID AS tID, t.NAME AS tName, c.ID AS cID, ROW_NUMBER() OVER (PARTITION BY t.ID ORDER BY c.Id) AS tSeq
FROM tenant t
LEFT JOIN contracts c ON t.ID = c.TenantID ),
partial AS (
SELECT s.tName AS tName, s.sName AS sName, c.cID As cID
FROM spaces s
LEFT JOIN contrs c ON s.tID = c.tID AND s.sSeq = c.tSeq )
SELECT (SELECT NAME FROM tenant WHERE tenant.ID = c.TenantID) AS tName, NULL AS sName, c.ID AS cID
FROM contracts c
WHERE c.ID NOT IN (SELECT cID FROM partial WHERE cID IS NOT NULL)

UNION ALL 

SELECT *
FROM partial

ORDER By tName

上述基于CTE的查询产生以下输出:

tName   sName   cID
--------------------
Tenant1 S1      1
Tenant1 S2      2
Tenant2 S3      3  
Tenant2 NULL    4
Tenant2 NULL    5
Tenant3 S4      6
Tenant3 S5      NULL

足够接近所需的输出。

答案 2 :(得分:0)

你确定这是你想要做的吗?显示所有结果(包括应该在您想要的空间中的数据)会提供更多信息。通过删除字段只是因为它在上面的字段中显示了该值,您就限制了人们可能想要进一步做的任何过滤。

另外,根据您提供的数据,您的结果是错误的,例如租户1将有4个记录而不是2.他们有2个空格和2个合同,空格和合同之间没有直接连接,这意味着您将有2x2 =租户的4条记录1.总而言之,您的输出应该有9条记录,而不是7.在给定当前结构的情况下,您无法更改此记录。

如果经理要求报告中的数据如此呈现,我建议向他们解释这种格式不太有用,如果包含所有内容,他们可以用它做更多事情。例如,如果他们想在Excel中看到它,他们如何将它过滤到Tenant 1的所有内容?在请求的格式中,如果在Tenant 1上放置过滤器,则只能看到一条记录。或者如果在csv中使用过滤器,则会加载不正确的数据。除了美观之外,除了功能之外,你想要它的布局没有任何优势。