`

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解决方案等内容。

    T-SQL性能调优秘笈 基于SQL Server 2012窗口函数 源码和示例数据库

    文件列表 Chapter 01 - SQL Windowing.sql Chapter 02 - A Detailed Look at Window Functions.sql Chapter 03 - Ordered Set Functions.sql ...Chapter 05 - T-SQL Solutions using Window Functions.sql TSQL2012.sql

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

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

    t-sql学习笔记

    t-sql学习笔记,总结的挺好

    Microsoft SQL SERVER 2008技术内幕 T-SQL查询 2/2

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

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

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

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

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

    sql server 2005 技术内幕 T-SQL查询 中文清晰pdf part4

    学习sql server 和sql 的两本经典的著作: 《sql server 2005 技术内幕 T-SQL查询》 《sql server 2005 技术内幕 T-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 server 2005技术内幕T-SQL查询(第一章 逻辑查询处理)

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

    经典T-SQL-MD5算法实现

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

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

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

    一种有效的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对象的数据。 首先,文章...

    实验五--数据查询——复杂查询.doc

    实验五--数据查询——复杂查询 本实验旨在帮助学生掌握 SQL Server 查询语句的基本语法,熟练使用 Select 语句对多表进行查询,掌握 SQL Server 所提供的函数,并熟练使用 SQL 语句进行复杂的连接操作。 实验目的...

    NotePad++格式化sql文本插件

    而在处理SQL文本时,有一款插件——"Poor Man's T-SQL Formatter NppPlugin",它为NotePad++增添了强大的SQL格式化能力,极大地提升了SQL代码的可读性和整洁度。 " PoorMan's T-SQL Formatter NppPlugin ",正如其...

    T-SQL学习笔记.chm

    一:简单的语法知识 二:简单的增 删 改 查和一些相关子句 三:复杂一点的查询 四:约束 五:create drop alter 六:视图 七:存储过程与用户自定义函数 八:触发器 九:全文索引 十:游标

Global site tag (gtag.js) - Google Analytics