【问题标题】:Querying data from different topics into one result set in T-SQL在 T-SQL 中将来自不同主题的数据查询到一个结果集中
【发布时间】:2019-02-01 19:48:12
【问题描述】:

我没有更好的标题,而且我已经找到了解决方案 - 我想知道是否有更优雅的方法来解决这个问题,因为我不太喜欢我这样做的方式。

问题是:我有一些项目(我们称之为人员)与两个不同的事物(我们称之为部门和协会)存在 m:n 关系。假设人 1 是部门 1 和 2 以及协会 1 的成员,人 2 是部门 3 和协会 1、2 和 3 的成员,人 3 是部门 1、2 和 4 和协会 3 的成员。我想要的是结果集看起来像

+==========+===============+===============+ |人 |部门 |协会 | +==========+===============+===============+ |人 1 |部门 1 |协会 1 | +------------+--------------+----------------+ |人 1 |部门 2 |空 | +------------+--------------+----------------+ |人 2 |部门 3 |协会 1 | +------------+--------------+----------------+ |人 2 |空 |协会 2 | +------------+--------------+----------------+ |人 2 |空 |协会 3 | +------------+--------------+----------------+ |人 3 |部门 1 |协会 3 | +------------+--------------+----------------+ |人 3 |部门 2 |空 | +------------+--------------+----------------+ |人 3 |部门 4 |空 | +------------+--------------+----------------+

我做了什么:

首先我创建了 5 个表:

创建表人员( PersonID int IDENTITY(1,1) NOT NULL, 人名 nvarchar(50) 非空, CONSTRAINT PK_Persons 主键集群(PersonID ASC) ) 创建表关联( AssociationID int IDENTITY(1,1) NOT NULL, AssociationName nvarchar(50) 非空, CONSTRAINT PK_Associations 主键集群(AssociationID ASC) ) 创建表部门( DepartmentID int IDENTITY(1,1) NOT NULL, 部门名称 nvarchar(50) 非空, CONSTRAINT PK_Departments PRIMARY KEY CLUSTERED (DepartmentID ASC) ) 创建表协会成员( PersonID int 非空, AssociationID int 非空, CONSTRAINT PK_AssociationMembers 主键集群(PersonID ASC,AssociationID ASC) ) CREATE TABLE 部门成员( PersonID int 非空, DepartmentID int 非空, CONSTRAINT PK_DepartmentMembers 主键集群(PersonID ASC,DepartmentID ASC) )

然后我插入了一些数据:

插入人员(PersonName)值('Person 1') 插入人员(PersonName)值('Person 2') 插入人员(PersonName)值('Person 3') 插入关联(关联名称)值(“关联 1”) 插入关联(关联名称)值(“关联 2”) 插入关联(关联名称)值(“关联 3”) 插入部门(部门名称)值(“部门 1”) 插入部门(部门名称)值(“部门 2”) 插入部门(部门名称)值(“部门 3”) 插入部门(部门名称)值(“部门 4”) 插入 AssociationMembers(PersonID,AssociationID)值 (1, 1) 插入 AssociationMembers(PersonID, AssociationID) 值 (2, 1) 插入 AssociationMembers(PersonID, AssociationID) 值 (2, 2) 插入 AssociationMembers(PersonID, AssociationID) 值 (2, 3) 插入 AssociationMembers(PersonID, AssociationID) 值 (3, 3) 插入部门成员(PersonID、DepartmentID)值 (1, 1) 插入部门成员(PersonID、DepartmentID)值(1、2) 插入部门成员(PersonID,DepartmentID)值(2、3) 插入部门成员(PersonID、DepartmentID)值 (3, 1) 插入部门成员(PersonID、DepartmentID)值 (3, 2) 插入部门成员(PersonID、DepartmentID)值(3、4)

并创建了两个视图:

创建视图 v_AssociationMemberships 作为 选择 p.PersonID, ROW_NUMBER() OVER (PARTITION BY p.PersonID ORDER BY a.AssociationID) AS 'AssociationRow', p.人名, a.AssociationID, a.AssociationName 从 dbo.Persons p INNER JOIN dbo.AssociationMembers am ON am.PersonID = p.PersonID INNER JOIN dbo.Associations a ON a.AssociationID = am.AssociationID 创建视图 v_DepartmentMemberships 作为 选择 p.PersonID, ROW_NUMBER() OVER (PARTITION BY p.PersonID ORDER BY d.DepartmentID) AS 'DepartmentRow', p.人名, d.部门ID, d.部门名称 从 dbo.Persons p 内部加入 dbo.DepartmentMembers dm ON dm.PersonID = p.PersonID INNER JOIN dbo.Departments d ON d.DepartmentID = dm.DepartmentID

这是最终查询现在的样子,并且 - 它有效,它正在做它应该做的事情,但我想知道是否有更简单、更优雅的方式来做它(使用任何 T-SQL 可能提供的方法):

选择 ISNULL(t.aPersonName, t.dPersonName) 作为人名, t.dDepartmentName AS 部门名称, t.aAssociationName AS 关联名称 从 ( 选择 d.PersonName AS dPersonName, d.DepartmentName AS dDepartmentName, a.PersonName AS aPersonName, a.AssociationName AS aAssociationName 从 dbo.v_DepartmentMemberships d 右外连接 dbo.v_AssociationMemberships a ON a.PersonID = d.PersonID AND a.AssociationRow = d.DepartmentRow 联盟 选择 d.PersonName AS dPersonName, d.DepartmentName AS dDepartmentName, a.PersonName AS aPersonName, a.AssociationName AS aAssociationName 从 dbo.v_AssociationMemberships 一个 右外连接 dbo.v_DepartmentMemberships d ON d.PersonID = a.PersonID AND d.DepartmentRow = a.AssociationRow ) 吨 订购方式 人名, ISNULL(t.dDepartmentName, 'zzzzzzzzzzzzz'), ISNULL(t.aAssociationName, 'zzzzzzzzzzzzz')

谢谢 - 也许有人可以为这个问题推荐一个更好的标题。

【问题讨论】:

    标签: sql-server tsql


    【解决方案1】:

    您的查询中有一些彼此不相关的表(关联和部门),并且您不希望结果包含两个表的叉积。因此,您需要创建新的表、视图、子查询或 CTE,以使每个人只显示一个值来关联这两个表。

    我认为您使用 ROW_NUMBER 的想法是正确的,但您采取了迂回的方式。在这个答案中,我使用 CTE 将唯一的 ROW_NUMBER 附加到关联和部门的每个成员关联到一个人,然后使用另一个 CTE 创建用于将关联和部门组合到人员的结果集。

    WITH am AS (
        SELECT PersonID, am.AssociationID
            , ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY am.AssociationID) PersonRowID
        FROM AssociationMembers am
    )
    , dm AS (
        SELECT PersonID, dm.DepartmentID
            , ROW_NUMBER() OVER (PARTITION BY PersonID ORDER BY dm.DepartmentID) PersonRowID
        FROM DepartmentMembers dm
    )
    , personRows AS (
        SELECT PersonID, PersonRowID FROM am 
        UNION 
        SELECT PersonID, PersonRowID FROM dm
    )
    SELECT p.PersonName, d.DepartmentName, a.AssociationName
    FROM personRows
    INNER JOIN Persons p ON p.PersonID = personRows.PersonID
    LEFT JOIN am ON am.PersonID = p.PersonID AND am.PersonRowID = personRows.PersonRowID
    LEFT JOIN Associations a ON a.AssociationID = am.AssociationID
    LEFT JOIN dm ON dm.PersonID = p.PersonID AND dm.PersonRowID = personRows.PersonRowID
    LEFT JOIN Departments d ON d.DepartmentID = dm.DepartmentID
    

    personRows CTE 利用 UNION 仅保留不同值这一事实,因此每个人的 PersonRowID 将具有一个唯一值,直至最大值。

    【讨论】:

    • 非常感谢!看起来比我的好多了:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-26
    • 2023-03-18
    • 2021-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-25
    相关资源
    最近更新 更多