从根部门选择部门树结构中的部门,不包括特殊的子部门 - 树(参见文本)

时间:2015-11-08 11:26:21

标签: sql sql-server tsql

问题: 我真的很难从部门表中选择一组特殊的部门,这些部门以树形结构排列(父子结构)。

您可以使用以下设置进行播放: http://sqlfiddle.com/#!3/18a28/3

详细说明: 父子关系由名为FullPath的字符串属性建模。 FullPath-String是由反斜杠分隔的数字顺序。例如:' 1'是一个没有父母的部门。 ' 1 \ 2'是一个部门,有部门' 1'作为父母。 ' 1 \ 3 \ 5 \ 6'是一个有' 1 \ 3 \ 5'作为父母部门,它本身有' 1 \ 3'作为父母等。每个department-record都有一个名为hasFKToOrg的布尔属性,该属性设置为1或0。

这是设置:     CREATE TABLE部门        ([FullPath] varchar(128),[hasFKToOrg] int)     ;

INSERT INTO Department
    ([FullPath], [hasFKToOrg])
VALUES
    ('1', 1),
    ('1\2', 0),
    ('1\3', 1),
    ('1\3\6',0),
    ('1\3\4', 1),
    ('1\3\4\6', 0),
    ('1\3\5', 1),
    ('1\3\5\6', 0)
;

我需要什么: 一个select语句,它传递一个专用父系的所有子系,包括父系本身(根据定义,它总是标记为hasFKToOrg = 1)。必须从结果集中排除该父级部门下也标记为hasFKToOrg = 1的所有子树分支。

例如:

  1. ' 1'是父母部门。 select语句必须提供' 1'和' 1/2'作为部门。
  2. ' 1 \ 3'是父母部门。 select语句必须提供' 1 \ 3'和' 1 \ 3 \ 6'作为部门。
  3. ' 1 \ 3 \ 4'是父母部门。 select语句必须提供' 1 \ 3 \ 4'和' 1 \ 3 \ 4 \ 6'作为部门。
  4. ' 1 \ 3 \ 5'是父母部门。 select语句必须提供' 1 \ 3 \ 5'和' 1 \ 3 \ 5 \ 6'作为部门。
  5. 到目前为止我的解决方案: 我尝试了部门之间的内部联接。内连接方法由于多种原因不起作用。我该怎么做呢?

    DECLARE @root_path varchar(20) = '1\3'; 
    GO
    select d.FullPath, de.FullPath
    from Department d
    join Department de on (d.FullPath not like de.FullPath+'%')
    where
    d.FullPath like @root_path+'%'
    and
    (
      de.hasFKToOrg = 1
      and
      de.FullPath <> @root_path
      and
      not (d.hasFKToOrg = 1 and de.hasFKToOrg = 1 and d.FullPath <> @root_path and de.FullPath <> @root_path)
    )
    

    这提供了&#39; 1 \ 3&#39;和&#39; 1 \ 3 \ 4 \ 6&#39;和&#39; 1 \ 3 \ 5 \ 6&#39;这是假的,因为&#39; 1 \ 3&#39;和&#39; 1 \ 3 \ 6&#39;是正确的部门。有什么建议我可以做什么而不是得到正确的结果集?

3 个答案:

答案 0 :(得分:0)

因为您将孩子定义为下一级,所以您不能使用'%'。你需要单一的野性角色。 有一个限制,这将不会自动支持/ 12 / 更改where子句

d.FullPath like @root_path+'%'

d.FullPath like @root_path+'\_'

或者你可以试试这个

d.FullPath like @root_path+'%' AND  d.FullPath not like @root_path+'%\%'

所以不要接受超过一级的部门。

答案 1 :(得分:0)

您应该停止重新启动滚轮并使用 hiearchyId

CREATE TABLE #Department
    ([FullPath] hierarchyID NOT NULL PRIMARY KEY, [hasFKToOrg] int);

INSERT INTO #Department ([FullPath], [hasFKToOrg])
VALUES ('/1/', 1), ('/1/2/', 0), ('/1/3/', 1),  ('/1/3/6/',0),
       ('/1/3/4/', 1), ('/1/3/4/6/', 0), ('/1/3/5/', 1), ('/1/3/5/6/', 0);

DECLARE @node hierarchyId = '/1/';

SELECT CAST(FullPath AS nvarchar(100)), hasFKToOrg 
FROM #Department
WHERE FullPath.IsDescendantOf(@node) = 1
  AND FullPath.GetLevel() - 1 = @node.GetLevel()
  AND hasFKToOrg = 0
UNION ALL 
SELECT CAST(FullPath AS nvarchar(100)), hasFKToOrg 
FROM #Department
WHERE FullPath = @node

LiveDemo

<强> IsDescendantOf

  

如果这是父级的后代,则返回true。

答案 2 :(得分:0)

简单的子句

我使用所有可能的方案测试了我的脚本,我的结果符合您的预期。最好的部分是我在一个简单的where子句中完成所有操作。没有什么花哨。 性能方面,它应该是体面的,取决于您的表大小。可能比使用带有一堆&#39;%&#39;的where子句更好。通配符。

DECLARE @root_path VARCHAR(1000) = '1';
DECLARE @RootSlashCount INT = (LEN(@root_path) - LEN(REPLACE(@root_path,'\',''))); --number of slashes in @root_path

SELECT *
FROM department
WHERE       (hasFKToOrg = 1 AND FullPath = @root_path) --Exact match when hasFKToOrg = 1
            OR
            (       hasFKToOrg = 0 --Only when it's 0
                AND LEFT(FullPath,LEN(@root_path)) = @root_path --Match root path to all FullPaths that match 
                AND (LEN(FullPath) - LEN(REPLACE(FullPath,'\',''))) = @RootSlashCount + 1 --only return direct descendants by using the number of slashes
            )