【问题标题】:Two trees and a joint table between them两棵树和它们之间的联合表
【发布时间】:2016-01-24 20:55:16
【问题描述】:

我有三个表:两个分层表和它们之间的一个连接。

Teams
id  idParent
1   null
2   1
3   null
4   null
5   4

Projects
id  idParent
1   null
2   null
3   2
4   2
5   null

TeamProjects
idTeam  idProject
2       2
3       1
5       5

一个项目总是至少依赖一个团队,这就是 teamprojects 的用途。

我想要达到的结果:对于每个对象(团队和项目),我想知道什么是上层和下层对象(id 连接)

idObject    ascendantTeams  descendantTeam  ascendantProjects   descendantProjects
1                                   2                               7, 10
2            1                                                      7
3                                                                   6
4               
5            4                                                      10
6            3          
7            2                                                      8, 9
8            2                                  7   
9            2                                  7   
10           5          

我正在尝试使用 linq to entity 查询来实现这一点,但我需要 CTE(用于递归部分)和(stuff-for-xml)用于连接部分......并且既不需要将 linq 转换为实体。 所以我正在尝试提供帮助,但我也没有设法为它编写 sql。

您将如何解决这个问题,无论是使用 linq to entity 还是 sql?

【问题讨论】:

  • 请明确这一点。我认为递归查询是主要的绊脚石。 展示你自己的尝试,并专注于做到这一点。 (这使得这只是一个 SQL 问题)。
  • 最终结果中的idObject 是什么意思?它是来自团队或项目表的 id 吗?
  • 是的,idObject 可以从两个表中取值
  • 如果一个团队没有父或子,那么它的项目就属于descendantProjects?
  • 是的,绝对是,任何父项目是这个团队的项目都将是这个团队的后代项目

标签: sql-server entity-framework linq tsql entity-framework-6


【解决方案1】:

你有什么不是很清楚。

所以,为了想出解决方案,我做了一些假设。我假设一个项目将包含在 descendantProjects 中以获得基于团队的结果(请参阅dbo.GetProjectsForTeam),并且一个团队将包含在 descendantTeams 中以获得基于项目的结果(请参阅dbo.GetTeamsForProject)。从你的问题来看,这一点都不清楚。

您需要创建标量值函数来实现您的场景。

如何调用标量值函数

这些函数中的每一个都有两个参数 - 一个类型参数将是 a 代表后代或 d 代表后代,ID 将是基于团队的查询的团队 ID 或项目的项目 ID - 基于查询

团队相关的标量值函数

CREATE FUNCTION [dbo].[GetTeamsForTeam](
               @teamType VARCHAR(20) , --either 'a' for ascendants or 'd' for descendants
               @teamId   INT)
RETURNS VARCHAR(MAX)
AS
     BEGIN
         DECLARE @list VARCHAR(MAX);
         IF @teamType = 'a'
             BEGIN
                 --get all parent teams
                 SELECT @list = COALESCE(@list+','+CAST(t.Id AS VARCHAR(10)) , CAST(t.Id AS VARCHAR(10)))
                 FROM dbo.Teams AS t
                 WHERE t.Id IN ( SELECT t2.IdParent
                                 FROM dbo.Teams AS t2
                                 WHERE t2.id = @teamId
                               );
             END;
         ELSE
             BEGIN
                 --get all children teams including @teamId
                 SELECT @list = COALESCE(@list+','+CAST(t.Id AS VARCHAR(10)) , CAST(t.Id AS VARCHAR(10)))
                 FROM dbo.Teams AS t
                 WHERE t.Id IN ( SELECT t2.Id
                                 FROM dbo.Teams AS t2
                                 WHERE t2.IdParent = @teamId
                                       OR
                                       t2.Id = @teamId
                               );
             END;
         RETURN @list;
     END;
GO


CREATE FUNCTION [dbo].[GetProjectsForTeam](
               @projectType VARCHAR(1) , --either 'a' for ascendants or 'd' for descendants
               @teamId       INT)
RETURNS VARCHAR(MAX)
AS
     BEGIN
         DECLARE @projects VARCHAR(MAX);
         IF @projectType = 'a'
             BEGIN
                 --get projects for all the parents of @teamId
                 SELECT @projects = COALESCE(@projects+','+CAST(tp.idProject AS VARCHAR(10)) , CAST(tp.idProject AS VARCHAR(10)))
                 FROM dbo.TeamProjects AS tp
                 WHERE tp.idTeam IN ( SELECT t.IdParent
                                      FROM dbo.Teams AS t
                                      WHERE t.Id = @teamId
                                    );
             END;
         ELSE
             BEGIN
                 --get projects for all children of @teamId including @teamId
                 SELECT @projects = COALESCE(@projects+','+CAST(tp.idProject AS VARCHAR(10)) , CAST(tp.idProject AS VARCHAR(10)))
                 FROM dbo.TeamProjects AS tp
                 WHERE tp.idTeam IN ( SELECT t.Id
                                      FROM dbo.Teams AS t
                                      WHERE t.IdParent = @teamId OR t.Id = @teamId
                                    );
             END;
         RETURN @projects;
     END;
GO

使用上述标量值函数,实现最终结果集的基于团队的查询如下。

SELECT t.id AS TeamId,
       dbo.GetTeamsForTeam('a', t.id) AS ascendantTeams,
       dbo.GetTeamsForTeam('d', t.id) AS descendantTeams,
       dbo.GetProjectsForTeam('a', t.id) AS ascendantProjects,
       dbo.GetProjectsForTeam('d', t.id) AS descendantProjects
FROM Teams t;

项目相关的标量值函数

CREATE FUNCTION [dbo].[GetTeamsForProject] (
                @teamType  VARCHAR(20) , --either 'a' for ascendants or 'd' for descendants
                @projectId INT
                                           )
RETURNS VARCHAR(MAX)
AS
     BEGIN
         DECLARE @list VARCHAR(MAX);
         IF @teamType = 'a'
             BEGIN
                 --get all parent teams
                 SELECT @list = COALESCE(@list+','+CAST(t.Id AS VARCHAR(10)) , CAST(t.Id AS VARCHAR(10)))
                 FROM dbo.Teams AS t
                 WHERE t.Id IN ( SELECT t2.IdParent
                                 FROM dbo.Projects AS p INNER JOIN dbo.TeamProjects AS tp ON p.Id = tp.idProject
                                                        INNER JOIN dbo.Teams AS t2 ON t2.id = tp.idTeam
                                 WHERE p.id = @projectId
                               );
             END;
         ELSE
             BEGIN
                 --get all children teams including team for @projectId
                 SELECT @list = COALESCE(@list+','+CAST(t.Id AS VARCHAR(10)) , CAST(t.Id AS VARCHAR(10)))
                 FROM dbo.Teams AS t
                 WHERE t.Id IN ( SELECT t2.Id
                                 FROM dbo.Projects AS p INNER JOIN dbo.TeamProjects AS tp ON p.Id = tp.idProject
                                                        INNER JOIN dbo.Teams AS t2 ON t2.id = tp.idTeam
                                 WHERE p.IdParent = @projectId OR p.Id = @projectId 
                               );
             END;
         RETURN @list;
     END;

GO


CREATE FUNCTION [dbo].[GetProjectsForProject] (
                @projectType VARCHAR(1) , --either 'a' for ascendants or 'd' for descendants
                @projectId   INT
                                              )
RETURNS VARCHAR(MAX)
AS
     BEGIN
         DECLARE @projects VARCHAR(MAX);
         IF @projectType = 'a'
             BEGIN
                 --get projects for all the parents of @projectId
                 SELECT @projects = COALESCE(@projects+','+CAST(p.idParent AS VARCHAR(10)) , CAST(p.idParent AS VARCHAR(10)))
                 FROM dbo.Projects AS p
                 WHERE p.Id IN ( SELECT p.IdParent
                                 FROM dbo.Projects AS p
                                 WHERE p.Id = @projectId
                               );
             END;
         ELSE
             BEGIN
                 --get projects for all children of @projectd
                 SELECT @projects = COALESCE(@projects+','+CAST(p.id AS VARCHAR(10)) , CAST(p.id AS VARCHAR(10)))
                 FROM dbo.Projects AS p
                 WHERE p.IdParent IN ( SELECT p.Id
                                       FROM dbo.Projects AS p
                                       WHERE p.IdParent = @projectId OR p.Id = @projectId     
                                     );
             END;
         RETURN @projects;
     END;

GO

使用上述基于项目的功能,基于项目的查询将如下所示。

SELECT p.id AS ProjectId,
       dbo.GetTeamsForProject('a', p.id) AS ascendantTeams,
       dbo.GetTeamsForProject('d', p.id) AS descendantTeams,
       dbo.GetProjectsForProject('a', p.id) AS ascendantProjects,
       dbo.GetProjectsForProject('a', p.id) AS descendantProjects
FROM dbo.Projects p;

我使用的示例数据的屏幕截图和最终查询如下所示。

【讨论】:

  • 非常感谢。已经很晚了,我会检查它并验证它是否在早上好
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-02
  • 2019-08-10
  • 2019-09-04
  • 1970-01-01
  • 2014-12-06
  • 2022-08-02
  • 1970-01-01
相关资源
最近更新 更多