`

T-SQL查询学习笔记——求下属和祖先的算法

阅读更多
构建试验环境:
CREATE TABLE dbo.Employees
(
  empid   INT         NOT NULL PRIMARY KEY,
  mgrid   INT         NULL     REFERENCES dbo.Employees,
  empname VARCHAR(25) NOT NULL,
  salary  MONEY       NOT NULL,
  CHECK (empid <> mgrid)
);

INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(1, NULL, 'David', $10000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(2, 1, 'Eitan', $7000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(3, 1, 'Ina', $7500.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(4, 2, 'Seraph', $5000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(5, 2, 'Jiru', $5500.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(6, 2, 'Steve', $4500.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(7, 3, 'Aaron', $5000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(8, 5, 'Lilach', $3500.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(9, 7, 'Rita', $3000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(10, 5, 'Sean', $3000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(11, 7, 'Gabriel', $3000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(12, 9, 'Emilia' , $2000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(13, 9, 'Michael', $2000.00);
INSERT INTO dbo.Employees(empid, mgrid, empname, salary)
  VALUES(14, 9, 'Didi', $1500.00);

CREATE UNIQUE INDEX idx_unc_mgrid_empid ON dbo.Employees(mgrid, empid);
GO

一、下属:
方案一:无级数限制

CREATE FUNCTION dbo.fn_subordinates1(@root AS INT) RETURNS @Subs Table
(
  empid INT NOT NULL PRIMARY KEY NONCLUSTERED,
  lvl   INT NOT NULL,
  UNIQUE CLUSTERED(lvl, empid)  -- Index will be used to filter level
)
AS
BEGIN
  DECLARE @lvl AS INT;
  SET @lvl = 0;                 -- Initialize level counter with 0

  -- Insert root node to @Subs
  INSERT INTO @Subs(empid, lvl)
    SELECT empid, @lvl FROM dbo.Employees WHERE empid = @root;

  WHILE @@rowcount > 0          -- while previous level had rows
  BEGIN
    SET @lvl = @lvl + 1;        -- Increment level counter

    -- Insert next level of subordinates to @Subs
    INSERT INTO @Subs(empid, lvl)
      SELECT C.empid, @lvl
      FROM @Subs AS P           -- P = Parent
        JOIN dbo.Employees AS C -- C = Child
          ON P.lvl = @lvl - 1   -- Filter parents from previous level
          AND C.mgrid = P.empid;
  END

  RETURN;
END
GO

-- Node ids of descendants of a given node
SELECT empid, lvl FROM dbo.fn_subordinates1(3) AS S;

方案二:有级数限制

CREATE FUNCTION dbo.fn_subordinates2
  (@root AS INT, @maxlevels AS INT = NULL) RETURNS @Subs TABLE
(
  empid INT NOT NULL PRIMARY KEY NONCLUSTERED,
  lvl   INT NOT NULL,
  UNIQUE CLUSTERED(lvl, empid)  -- Index will be used to filter level
)
AS
BEGIN
  DECLARE @lvl AS INT;
  SET @lvl = 0;                 -- Initialize level counter with 0
  -- If input @maxlevels is NULL, set it to maximum integer
  -- to virtually have no limit on levels
  SET @maxlevels = COALESCE(@maxlevels, 2147483647);

  -- Insert root node to @Subs
  INSERT INTO @Subs(empid, lvl)
    SELECT empid, @lvl FROM dbo.Employees WHERE empid = @root;

  WHILE @@rowcount > 0          -- while previous level had rows
    AND @lvl < @maxlevels       -- and previous level < @maxlevels
  BEGIN
    SET @lvl = @lvl + 1;        -- Increment level counter

    -- Insert next level of subordinates to @Subs
    INSERT INTO @Subs(empid, lvl)
      SELECT C.empid, @lvl
      FROM @Subs AS P           -- P = Parent
        JOIN dbo.Employees AS C -- C = Child
          ON P.lvl = @lvl - 1   -- Filter parents from previous level
          AND C.mgrid = P.empid;
  END

  RETURN;
END
GO

-- Descendants of a given node, no limit on levels
SELECT empid, lvl
FROM dbo.fn_subordinates2(3, NULL) AS S;

解决方案三:使用cte无级数限制

DECLARE @root AS INT;
SET @root = 3;

WITH SubsCTE
AS
(
  -- Anchor member returns root node
  SELECT empid, empname, 0 AS lvl
  FROM dbo.Employees
  WHERE empid = @root

  UNION ALL

  -- Recursive member returns next level of children
  SELECT C.empid, C.empname, P.lvl + 1
  FROM SubsCTE AS P
    JOIN dbo.Employees AS C
      ON C.mgrid = P.empid
)
SELECT * FROM SubsCTE;

解决方案四:有级数限制的子数,CTE解决方案

DECLARE @root AS INT, @maxlevels AS INT;
SET @root = 3;
SET @maxlevels = 2;

WITH SubsCTE
AS
(
  SELECT empid, empname, 0 AS lvl
  FROM dbo.Employees
  WHERE empid = @root

  UNION ALL

  SELECT C.empid, C.empname, P.lvl + 1
  FROM SubsCTE AS P
    JOIN dbo.Employees AS C
      ON C.mgrid = P.empid
      AND P.lvl < @maxlevels -- limit parent's level
)
SELECT * FROM SubsCTE;


二、祖先

解决方案一:

CREATE FUNCTION dbo.fn_managers
  (@empid AS INT, @maxlevels AS INT = NULL) RETURNS @Mgrs TABLE
(
  empid INT NOT NULL PRIMARY KEY,
  lvl   INT NOT NULL
)
AS
BEGIN
  IF NOT EXISTS(SELECT * FROM dbo.Employees WHERE empid = @empid)
    RETURN; 

  DECLARE @lvl AS INT;
  SET @lvl = 0;                 -- Initialize level counter with 0
  -- If input @maxlevels is NULL, set it to maximum integer
  -- to virtually have no limit on levels
  SET @maxlevels = COALESCE(@maxlevels, 2147483647);

  WHILE @empid IS NOT NULL      -- while current employee has a manager
    AND @lvl <= @maxlevels      -- and previous level < @maxlevels
  BEGIN
    -- Insert current manager to @Mgrs
    INSERT INTO @Mgrs(empid, lvl) VALUES(@empid, @lvl);
    SET @lvl = @lvl + 1;        -- Increment level counter
    -- Get next level manager
    SET @empid = (SELECT mgrid FROM dbo.Employees WHERE empid = @empid);
  END

  RETURN;
END
GO

-- Ancestors of a given node, no limit on levels
SELECT empid, lvl
FROM dbo.fn_managers(8, NULL) AS M;

解决方案二:
DECLARE @empid AS INT, @maxlevels AS INT;
SET @empid = 8;
SET @maxlevels = 2;

WITH MgrsCTE
AS
(
  SELECT empid, mgrid, empname, 0 AS lvl
  FROM dbo.Employees
  WHERE empid = @empid

  UNION ALL

  SELECT P.empid, P.mgrid, P.empname, C.lvl + 1
  FROM MgrsCTE AS C
    JOIN dbo.Employees AS P
      ON C.mgrid = P.empid
      AND C.lvl < @maxlevels -- limit child's level
)
SELECT * FROM MgrsCTE;


三、带有路径枚举的子图/字树

解决方案一:通用解决方案

CREATE FUNCTION dbo.fn_subordinates3
  (@root AS INT, @maxlevels AS INT = NULL) RETURNS @Subs TABLE
(
  empid INT          NOT NULL PRIMARY KEY NONCLUSTERED,
  lvl   INT          NOT NULL,
  path  VARCHAR(900) NOT NULL
  UNIQUE CLUSTERED(lvl, empid)  -- Index will be used to filter level
)
AS
BEGIN
  DECLARE @lvl AS INT;
  SET @lvl = 0;                 -- Initialize level counter with 0
  -- If input @maxlevels is NULL, set it to maximum integer
  -- to virtually have no limit on levels
  SET @maxlevels = COALESCE(@maxlevels, 2147483647);

  -- Insert root node to @Subs
  INSERT INTO @Subs(empid, lvl, path)
    SELECT empid, @lvl, '.' + CAST(empid AS VARCHAR(10)) + '.'
    FROM dbo.Employees WHERE empid = @root;

  WHILE @@rowcount > 0          -- while previous level had rows
    AND @lvl < @maxlevels       -- and previous level < @maxlevels
  BEGIN
    SET @lvl = @lvl + 1;        -- Increment level counter

    -- Insert next level of subordinates to @Subs
    INSERT INTO @Subs(empid, lvl, path)
      SELECT C.empid, @lvl,
        P.path + CAST(C.empid AS VARCHAR(10)) + '.'
      FROM @Subs AS P           -- P = Parent
        JOIN dbo.Employees AS C -- C = Child
          ON P.lvl = @lvl - 1   -- Filter parents from previous level
          AND C.mgrid = P.empid;
  END

  RETURN;
END
GO

-- Return descendants of a given node, along with a materialized path
SELECT empid, lvl, path
FROM dbo.fn_subordinates3(1, NULL) AS S;

-- Return descendants of a given node, sorted and indented
SELECT E.empid, REPLICATE(' | ', lvl) + empname AS empname
FROM dbo.fn_subordinates3(1, NULL) AS S
  JOIN dbo.Employees AS E
    ON E.empid = S.empid
ORDER BY path;

解决方案二:CTE解决方案

-- Descendants of a given node, with Materialized Path, CTE Solution
DECLARE @root AS INT;
SET @root = 1;

WITH SubsCTE
AS
(
  SELECT empid, empname, 0 AS lvl,
    -- Path of root = '.' + empid + '.'
    CAST('.' + CAST(empid AS VARCHAR(10)) + '.'
         AS VARCHAR(MAX)) AS path
  FROM dbo.Employees
  WHERE empid = @root

  UNION ALL

  SELECT C.empid, C.empname, P.lvl + 1,
    -- Path of child = parent's path + child empid + '.'
    CAST(P.path + CAST(C.empid AS VARCHAR(10)) + '.'
         AS VARCHAR(MAX)) AS path
  FROM SubsCTE AS P
    JOIN dbo.Employees AS C
      ON C.mgrid = P.empid
)
SELECT empid, REPLICATE(' | ', lvl) + empname AS empname
FROM SubsCTE
ORDER BY path;
分享到:
评论

相关推荐

    T-SQL性能调优秘笈 基于SQL Server2012窗口函数.rar

    1、处理常见业务问题,如总计、间隔、...3、T-SQL性能调优秘笈:基于SQL Server 2012窗口函数》基于SQLServer2012,讨论了SQL窗口、窗口函数、排序集合函数、窗口函数的优化以及利用窗口函数的T-SQL解决方案等内容。

    sql server 2012 T-SQl基础教程 源码和示例数据库

    《SQL Server 2012 T-SQL基础教程——源码与示例数据库》 本教程专注于Microsoft SQL Server 2012中的Transact-SQL...通过系统学习和动手实践,你将能够编写出高效的T-SQL查询,管理和操作SQL Server 2012中的数据。

    Microsoft_SQL_Server_2005技术内幕:T-SQL查询.pdf

    它详细介绍了T-SQL的内部体系结构,包含了非常全面的编程参考,提供了使用Transact-SQL(T-SQL)的专家级指导,囊括了非常全面的编程参考,揭示了基于集合的查询的强大威力,并包含大量来自专家们的参考和建议。...

    T-SQL Querying 源代码

    本书及其续篇——《Microsoft SQL Server 2005技术内幕:T-SQL程序设计》介绍了SQL Server 2005中高级T-SQL查询、查询优化及编程相关的知识。这两本书侧重于解决实践中的常见问题,并讨论了解决这些问题的方法。它们...

    SQLServer2008技术内幕T-SQL查询包含源代码及附录A

    主要内容包括SQL的基础理论、查询优化、查询算法及复杂度,以及在使用子查询、表表达式、排名函数、数据聚合和透视转换、TOP和APPLY、数据修改、分区表、特殊数据结构等实际应用时会遇到的各种高级查询问题和解决...

    Microsoft SQL Server 2008技术内幕:T-SQL查询_源代码及附录A

    "TSQLQuerying20090710.zip"这个文件很可能包含了书中所有示例的源代码,读者可以通过这些代码直接在SQL Server环境中执行,以便更好地理解和学习T-SQL查询的实际应用。这些源代码对于动手实践和解决问题来说是非常...

    Microsoft SQL Server 2005技术内幕:T-SQL查询 pdf 中文版 2

    Microsoft SQL Server 2005技术内幕:T-SQL查询 pdf 中文版 第二部分 第一部分地址:http://download.csdn.net/source/2684220

    T-SQL阅读笔记和习题

    但是对于SQL语言不是很熟悉,于是找了一些书来看,经过多方阅读和对比,最后看到一本《MICROSOFT SQL SERVER 2008技术内幕:T-SQL语言基础》。这本书非常适合于SQL语言初学者,内容比较全面,其次是示例比较简单,...

    Microsoft SQL Server 2005技术内幕: T-SQ程序设计.pdf

    从1999年开始,他一直是Microsoft SQL Server MVP(最有价值专家),并在全世界已经开展过无数次T-SQL查询、T-SQL优化和编程方面的培训。Itzik是Microsoft SQL Server方面几本著作的作者。他在SQL ServerMagazine和...

    SQL Server2005 T-SQL 概述

    ### SQL Server 2005 T-SQL 概览与逻辑查询处理深度解析 #### T-SQL基础与增强功能 T-SQL(Transact-SQL)是Microsoft SQL Server特有的一种SQL方言,用于管理和操作数据库。SQL Server 2005版本对T-SQL进行了显著...

    学习笔记——sql.zip

    "学习笔记——sql.zip"这个压缩包文件很可能包含了关于SQL的学习资料,如教程、笔记、示例代码等,旨在帮助用户掌握SQL的基本概念、语法和高级特性。 首先,SQL的基础知识包括数据类型,如整型(INT)、浮点型...

    SQL Server 2005 技术内幕之T-SQL编程原版CHM

    深入讨论了SQL Server 2005中新增的T-SQL编程特性,包含了大量的代码示例、表示例和逻辑难题以帮助数据库开发人员和管理员理解复杂的逻辑并掌握T-SQL。  本书适合于专业数据库开发者、BI开发者、DBA和以SQL Server...

    sql server 2005技术内幕T-SQL查询(第一章 逻辑查询处理)

    sql server 2005技术内幕T-SQL查询 第一章 逻辑查询处理 自己整理的笔记,希望对各位朋友有用!

    经典T-SQL-MD5算法实现

    能过sql server TSQL 实现MD5算法过程函数。

    一种有效的XML-TO-SQL查询翻译优化算法.pdf

    《一种有效的XML-TO-SQL查询翻译优化算法》这篇论文主要探讨了XML查询与SQL查询转换的优化策略,特别是针对XPath路径表达式的处理。在XML数据处理领域,将XML查询转化为SQL是关键的技术之一,因为它涉及到关系数据库...

    Transact-SQL权威指南

    《Transact-SQL权威指南》是一本深入探讨SQL在数据库管理中的应用的书籍,主要针对Transact-SQL,这是Microsoft SQL ...书中的内容丰富多样,涵盖了从基础概念到高级应用的广泛知识,是学习和提升T-SQL技能的宝贵资源。

    存储过程嵌入脚本——让T-SQL访问外部对象.pdf

    本文将深入探讨一种技术,该技术涉及将T-SQL(Transact-SQL,微软SQL Server的SQL扩展)存储过程与外部对象相结合,特别是如何通过T-SQL访问并操作如Microsoft Excel电子表格等非SQL Server对象的数据。 首先,文章...

    Mybatis 学习笔记——原生DAO实现数据增删改查SQL

    Mybatis 学习笔记——原生DAO实现数据增删改查SQL:https://blog.csdn.net/qq_24598601/article/details/83037252

Global site tag (gtag.js) - Google Analytics