T-SQL 递归

SQL递归算法 create function f_id(@parentid nvarchar(10)) returns @re table(orderid int,parentid int,title nvarchar(20),level int) as begin declare @l int set @l=0 insert @re select orderid, parentid ,title,@l from ta where parentid=@parentid while @@rowcount>0 begin 立即下载

Recursion in T–SQL

Alexander Kozak

Recursion is one of the classic techniques all computer science majors learn, often by writing a "Towers of Hanoi" program. (For a trip down memory lane, see Q41154 for a QuickBasic 4.5 implementation of the Towers of Hanoi.) In this article, Alex Kozak explores recursion in T–SQL.

There was a period in my life when I taught college students. When I started the topic of subqueries, I'd ask them to find the youngest employee in Northwind's employees table. Most came up with a solution quite easily:

SELECT * 
  FROM employees
  WHERE BirthDate =
  (SELECT MAX(BirthDate) FROM employees)

However, when I asked them to find the next youngest, many were stumped. A few offered a solution like this:

SELECT * 
  FROM employees
  WHERE BirthDate= 
  (SELECT MAX(BirthDate) 
    FROM employees
      WHERE BirthDate < 
        (SELECT MAX(BirthDate) FROM employees))

The recursive character of the query was obvious, and I can remember vowing to write a stored procedure that would return the nth member of any hierarchy (age, weight, points, and so forth). I eventually wrote the stored proc, but only two years later when I was working on an e–commerce project and someone was paying me to do it.

Overview

Recursion, which occurs when a procedure or function calls itself, is a well–known and powerful concept of mathematics and programming. However, it can be dangerous—leading to infinite loops, for example. (That's probably one reason that SQL Server 2000 limits the number of nesting calls or nesting levels to 32; you can use a global variable @@NESTLEVEL to check the nested level of the procedure at runtime.) DBMS programmers also need to remember that recursive calls can significantly add to transaction time, so recursion is usually avoided in OLTP systems.

[Bear in mind that terminology related to recursion in SQL Server can be confusing. Consider this from Q304364, PRB: Recursive Behavior of Triggers in SQL Server 6.5 is Incorrect: "The circuitous implementation of trigger recursion in SQL Server 6.5 should not be confused with 'indirect recursion' in SQL Server 7.0 or later. This circuitous implementation of a recursive trigger is called 'direct recursion,' whereas 'indirect recursion' in SQL Server 7.0 involves a trigger that updates another table whose trigger could update the original table, thus causing the original trigger to fire again. In SQL Server 7.0 and later, you can use the RECURSIVE TRIGGERS database option to enable or disable 'direct recursion.' You use the SQL Server NESTED TRIGGERS configuration to enable or disable 'indirect recursion.'"—Ed.]

Let's start with an example. Imagine that you have a table checkRank that stores the results of the students' tests, and that your task is to find the students that rank between 4th and 10th. To demonstrate the technique, I created a script fillCheckRank and a stored procedure spu_testRecursion. The script creates and loads table checkRank with relatively random data (see CreateCheckRank.sql in the Download file), but the stored procedure (see Listing 1) is more complex and uses a recursive algorithm, nested procedures calls, and nested subqueries to calculate the answer.

Listing 1. Stored procedure spu_testRecursion.

IF EXISTS (SELECT * FROM sysobjects 
WHERE id = object_id('spu_testRecursion') 
and OBJECTPROPERTY(id, 'IsProcedure') = 1)
DROP PROCEDURE spu_testRecursion
GO
CREATE PROC spu_testRecursion 
  @level int, @tblName varchar(30), 
  @colName varchar(30), @answer varchar(8000) OUTPUT	
AS
DECLARE @one_less int
SET NOCOUNT ON
–– Parameter @level greater than 31 is disallowed.
IF (@level < 0 OR @level > 31)
  BEGIN
    PRINT 'Illegal Parameter Value. Must be 0 through 31'
      RETURN –1
  END
IF (@level = 0 OR @level = 1)
  BEGIN
    SELECT @answer= 'select max(' + @colName + ')
    from ' + @tblName
  END
ELSE
  BEGIN
      SELECT @one_less = @level – 1
      –– recursively call itself
      EXEC spu_testRecursion @one_less, @tblName, 
      @colName, @answer output   
      IF @@ERROR <> 0 RETURN(–1) 
      SELECT @answer = 'select max(' + @colName + ') 
      from ' + @tblName + ' where ' + @colName + 
      ' < ' + Char(10) + '(' + @answer + ')'
   END
IF @@NESTLEVEL = 1
BEGIN
PRINT 'NESTED LEVEL ' 
+ CAST(@@NESTLEVEL AS VARCHAR(20)) + CHAR(10) + 
@colName + ' rank ' + CAST(@level AS VARCHAR(10)) 
+ CHAR(10) + @answer
  EXEC (@answer)
END
RETURN(0)
GO

/* How to run the procedure
  DECLARE @answer varchar(8000) 
  exec spu_testRecursion 10, 'checkRank', 
  'testPoints', @answer output
*/

Note: When you drop and create the stored procedure, you may receive a message about not being able to add rows to sysdepends for the current stored procedure because it depends on the missing object 'spu_testRecursion'. But don't worry—the stored procedure will still be created. See Q24641 for more information.

The stored procedure receives these parameters:

  • @level—Rank or position in the hierarchy.
  • @colName—Column(domain) name.
  • @tblName—Name of the table.
  • @answer—Output parameter that returns the generated SELECT statement.

And it returns these two:

  • Value, which corresponds to required level or position in hierarchy.
  • A script that you can run to retrieve the same result.

To get the result for the 4th highest, you might do this:

DECLARE @answer varchar(4000) 
EXEC spu_TestRecursion 4, 'checkRank', 'testPoints', 
@answer output

Here are my results (yours may differ because data for table checkRank is generated pseudo–randomly):

NESTED LEVEL 1
testPoints rank 4
select max(testPoints) from checkRank where testPoints < 
(select max(testPoints) from checkRank where testPoints < 
(select max(testPoints) from checkRank where testPoints < 
(select max(testPoints) from checkRank)))            
––––––––––– 
93

So, a score of 93 corresponds to rank 4.

When I ran the same query for the 10th highest score, my answer was 87. The ultimate answer to the question of rankings for 4th through 10th can either be inferred by inspection, or determined by running a query (see 4ththru10th.sql in the Download file).

Practical example

The next scenario is typical for many e–commerce marketplaces. Imagine that two sides—buyers and sellers—start trading, sending offers, or requests. Offers and requests can either be directed to a limited number of buyers (sellers), or can be posted and seen by all members of the exchange.

When the first bid or response to a request arrives, the negotiation starts. From here on, different scenarios are possible, and each scenario will create its own negotiation chain. Offers, bids, or any another part of the chain can expire, be canceled, be declined, or be accepted. Users can send a counter–offer, receive a counter–offer back, and so on. The cycle can start again as many times as defined by the rules of the market. Different deviations from the basic rules also might be allowed. For instance, you might want to allow parties to make limited changes to already closed deals, which, in turn, could be accepted or declined, and so on.

The physical implementations of your market might vary in the details, but usually each member of a negotiation chain is represented by a document—which might be saved as an XML document, a Java object, or broken up and stored in several tables. To find the sequence of the documents in a negotiation chain, you can use a document path. This is similar to a linked list where each member of such a list (path) has links to previous and next documents, except for the root and end documents in the list.

For example, assume that a table called Documents stores all the documents and has a column docPath. For a row with docID (primary key) = 12315 and a docPath = 12217/12267/12299/12315, there exists the next negotiation chain: 12217(the posted offer source that serves as the root document or template for the offer) à 12267(posted offer item—the actual offer) à 12299(bid) à 12315(counter—current document).

Now suppose I want to analyze the negotiation process, finding the differences between the prices, freight rates, volumes, and values in final and source documents. If I want to analyze the tendencies that cause the deals to fail, I have to consider the documents with statuses that are canceled, expired, or declined. To analyze the actual values and volumes, I need to take the agreements and purchase orders with a status accepted. In both cases, the final document will be the last document in docPath (in our example, 12315), but the source document won't be the first. In my example, the first document (12217) is a template, which has only basic parameters of the offer. (Only when I receive a bid am I able to calculate freight rate, total price, and some other parameters.) So in my example, the second document (12267) is a source. In more general terms, any document from the negotiation chain, except the last one, can be a source, because each subsequent document adds some new characteristics to the original document, and I might be interested in exactly those new parameters.

So my task is to extract the nth member of docPath according to some conditions—and this is a quite trivial task, if you write a script, stored procedure, or UDF, using T–SQL functions. But, if you want to get your result "on the fly" using a SELECT statement (think real–time e–commerce), the task becomes more complicated.

Substring helper routine

Imagine a sample–string '12/23/34/45/56/67/78/89/90/11/22/33/44/55/66/77/88/99/00/A/B/E/F/'. To find any member of this string, we can use a simple algorithm that extracts a substring between two subsequent positions of delimiter:

  • Member (1) = substring (string, 1, pos(1) – 1);
  • Member (2) = substring (string, pos(1) + 1, pos(2) – pos(1) – 1);
  • . . .
  • Member (n) = substring (string, pos(n–1) + 1, pos(n) – pos(n–1) – 1),

The T–SQL looks more like this (see Figure 1 for details):

  • Member (1) = SUBSTRING (string,1,CHARINDEX('/', string,1)–1)
  • Member (2) = SUBSTRING (string, CHARINDEX('/', string,1)+1,CHARINDEX('/', string, CHARINDEX('/', string,1)+1)–CHARINDEX('/', string,1)–1)
  • And so on.

Stored procedure spu_findDocID (available in the Download file) generates the script that allows us to extract any member from the 1st to the 31st in the string. The procedure implements the algorithm I sketched out earlier and uses these parameters:

  • @str—The name of the string, usually variable or column name.
  • @level—This is actually a member's number or depth of recursion call.
  • @prevPos and @pos—I use these output parameters to save positions of delimiters and use them in the next procedure call.
  • @answer—One more output parameter to accumulate the result.

An example

To see an example of the negotiation chain, run the script FindSource.sql. The first part of the script creates a "documents" table and loads it with sample data. These are the underlying rules of the scenario:

  • If docTypeID of the first (left–most) document in docPath is 1, the document's source is the first document in docPath.
  • If docTypeID of the first document is 2, the document's source is the second document in docPath.
  • If docTypeID of the first document is 3, the document's source is the third document in docPath.

Then, using spu_findDocID, it generate scripts for the first, second, and third documents in docPath:

DECLARE @answer varchar(8000), @prevPos varchar(3000),
@pos varchar(3000) 
EXEC spu_findDocID 'docPath', 1, @prevPos output, 
@pos output, @answer output
EXEC spu_findDocID 'docPath', 2, @prevPos output, 
@pos output, @answer output
EXEC spu_findDocID 'docPath', 3, @prevPos output, 
@pos output, @answer output

Finally, to see the source for all failed transactions (docTypeID 7 and 8), use this script, adding the generated scripts from before:

SELECT 
  failed.docID [failedDoc], 
  failed.docParh, 
  frst.docID [firstDoc], 
  frst.docTypeID [first docType], 
  CASE
  WHEN frst.docTypeID = 1 THEN /*COPY GENERATED SCRIPT
    FOR FIRST MEMBER OF docPath HERE*/
  WHEN frst.docTypeID = 2 THEN /*COPY GENERATED SCRIPT
    FOR SECOND MEMBER OF docPath HERE*/
  WHEN frst.docTypeID = 3 THEN /*COPY GENERATED SCRIPT
    FOR THIRD MEMBER OF docPath*/
  END sourceDoc	  
  FROM
  (SELECT docID, docParh 
    FROM documents WHERE docTypeID IN(7, 8)) failed 
  INNER JOIN 
  (SELECT docID, docTypeID FROM documents) frst
  ON frst.docID = SUBSTRING(docParh,1,
  CHARINDEX('/', docParh,1)–1)

Here are the results of the query with my data:

failedDoc  docParh    firstDoc  first docType  sourceDoc 
10         1/5/7/10/     1            1                1
11         3/7/9/11/     3            3                9
12         2/6/8/12/     2            2                6

And there you have it!

Download RECURSION.ZIP

To find out more about Microsoft SQL Server Professional and Pinnacle Publishing, visit their website at http://www.pinpub.com/html/main.isx?sub=57

S32K144后门密钥实战:如何用JLINK脚本绕过复位陷阱更新加密固件 本文详细介绍了在S32K144微控制器上,如何利用JLINK脚本绕过调试器默认复位陷阱,实现通过后门密钥安全更新加密固件的实战方案。针对汽车电子现场维护中常见的“解锁-复位-锁定”循环问题,文章提供了完整的JLinkScript定制方法、批处理工具及故障排除指南,帮助工程师高效完成固件升级。 阅读详情

相关推荐

RC滤波电路工作原理:【带图+公式计算】

容抗Xc和电容C成反比,和频率f也成反比,C越大,交流电容易通过电容,说明电容量大,电容的阻碍作用小;交流电的频率高,交流电也容易通过电容,说明频率高,电容的阻碍作用也小。我们可以看到,一个2V的纹波,经常RC电路后,幅值下降到mv级别,基本可以忽略不计,这就是RC滤波电路的工作原理。一个频率为1Khz的信号,通过一个容值为0.1uf的电容,计算电容对该信号产生的阻抗。将RC滤波器电路中的电阻,取值为1KΩ,电容取值为0.47uf,纹波幅值为2V。从公式1可以看到,Xc越大,Vout值就越接近Vin的值。

weixin_66299220的博客 2万+

sql 一张表递归_查看我的递归视觉指南(因为一张图片价值1,000字)

sql 一张表递归In this article, I will explain recursion (almost) completely with visual representations. I’ll show, rather than explain, how each recursive function call interacts with the entirety of the ...

cumi6497的博客 267

Kafka 系列 —(17)Kafka 流式处理:Kafka Streams架构、使用场景、框架选择

本文介绍了KafkaStreams的架构特点、应用场景及与其他流处理框架的比较。KafkaStreams作为嵌入式分布式流处理库,其核心架构包含流处理器、任务、拓扑和本地状态存储,具备自动容错和伸缩能力。适用于实时监控、ETL、聚合、Join、物化视图等低延迟场景。与Flink、Spark相比,KafkaStreams适合简单架构和微服务,而Flink适合大规模处理,Spark更适合批处理。选择框架需根据具体场景需求,如微服务推荐KafkaStreams,大规模处理推荐Flink。

moton2017的博客 1631

python基础-递归、二分法查找(for\递归)、三级菜单、压栈思想

递归方法 # age(1) n = 1 age(2)+2 # age(2) n = 2 age(3)+2 # age(3) n = 3 age(4)+2 # age(4) n = 4 40def age(n): if n == 4: return 40 return age(n+1)+2print(age(1))输出如下:E:\python\python_sdk\py

全干工程师 1912

微生物16S数据上传NCBI的SRA库

本篇内容仅包括:俺自己的实验收集了淡水、淡水沉积物、海水、海草附着微生物样品,测序所得的Fastq格式数据,在文章投稿发表时被要求数据需上传NCBI的SRA库,得到一个PRJNA2347899这样子的IDs。仅解决了这个上传过程!

Xf_Niu的博客 7435

利用GPAC生成MP4文件

GPAC主要针对学生和内容创作者,代表了一个跨平台的多媒体框架,开发人员可以使用它在 LGPL 许可下制作开源媒体。GPAC多媒体框架兼容范围广泛的流行文件类型,从常见格式(如 AVI、MPEG 和 MOV)到复杂格式(如 MPEG-4 系统或 VRML/X3D)和 360 电影。

深之JohnChen的专栏 1771

sql 实现递归算法

根据传入参数查找指定项的子项的递归用法 样表: child,parent 1      0 2      0 3      1 4      2 要 实现这种用法一般都通过两种方式来实现: procedure 方式: create procedure usp_getallchild(@child int) as declare @t table(child int nul...

gtts 292

使用再生龙还原镜像文件到硬盘

1. 把制作好的再生龙光盘插入需要的电脑,开机按F4使用再生龙启动2. 选择默认的:Clonezailla live(Default settings,VGA 800*600),然后等待几分钟 3. 选择语言:简体中文 4. 选择不修改键盘映射 5. 选择使用再生龙          6. 选择硬盘/分区(存到/来自)镜像文件,此选项既可以制作镜像,也可以恢复镜像。 7. 使用本机的分区     ...

weixin_42168893的博客 1万+

SQL中的递归查询

点击关注上方“SQL数据库开发”,设为“置顶或星标”,第一时间送达干货递归查询原理SQL Server中的递归查询是通过CTE(表表达式)来实现。至少包含两个查询,第一个查询为定点成员,定...

SQL数据库开发 8783

SQL注入详解

SQL注入(SQL Injection)是一种常见的Web安全漏洞,主要形成的原因是在数据交互中,前端的数据传入到后台处理时,没有做严格的判断,导致其传入的“数据”拼接到SQL语句中后,被当作SQL语句的一部分执行。 从而导致数据库受损(被脱库、被删除、甚至整个服务器权限陷)。 即:注入产生的原因是后台服务器接收相关参数未经过滤直接带入数据库查询...

渗透之路,攻防之间皆学问 26万+

UE5、CesiumForUnreal实现加载GeoJson绘制多面(MultiPolygon)功能(支持点选高亮)

UE5、CesiumForUnreal实现加载GeoJson实现多面(MultiPolygon)的绘制功能,并且支持点选高亮和属性查询。

专注GIS开发 4628

T-SQL中的递归

T-SQL中的递归 --作者:Alexander Kozak   [摘要]递归是经典的技巧之一,主修计算机科学的学生经常通过编写汉诺塔程序来学习它。在这篇文章中,Alex Kozak探讨了T-SQL中的递归。    我曾经担任过大学的老师。我在开始讲授子查询时,会让学生从Northwind数据库的employees表中,找到年龄最小的雇员。大多数的学生很轻松地给出了下面的解答。SELE

388

基于STM32+华为云IOT设计的云平台监控系统_102

本次设计结合现代生活的具体需求,设计出一套基于华为云IOT设计的云平台监控系统,可以远程实时查看家里光照强度、温湿度等信息,支持在云端控制家里的电器设备,实现远程控制。

7644

T-Sql 递归查询

T-Sql 递归查询(给定节点查所有父节点、所有子节点的方法 1.查找所有父节点 2.查找所有子节点 3.查找从子节点到定级节点的路径 4.父查子并数据

求知若渴,虚心若愚。 410

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

T-SQL递归

摘要:在程序中调用递归很简单只要在方法中调用自身就可以了,可是在数据库操作中这种方法并不可行,那么下面就来看一下在SQL Server中如何书写递归调用。 主要内容: 可以说直到SQL Server 2005之前,SQL Server在运行时级别是没有对递归处理的设计的。这也就是说如果你想要实现递归就只能自己控制递归逻辑,将递归转换为循环操作。但是到了SQL Server2005之后,递归的

Kenshin Cui's Blog 2838

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

T-SQL 递归查询示例

1. 数据库表创建 -- Create an Employee table. CREATE TABLE dbo.MyEmployees ( EmployeeID smallint NOT NULL, FirstName nvarchar(30) NOT NULL, LastName nvarchar(40) NOT NULL, Title nvarchar(50) NOT NUL

上步七星 1135

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

T-SQL中的递归查询

表结构 ProductCategory CategoryID,Level,ParentCategoryID 数据 1,1,-1 2,1,-1 3,2,1 4,3,3 5,2,2 6,4,5 T-SQL WITH CategoryTemp(CategoryID,ParentCategoryID)--临时表用来保存查到的Category (   SELECT Category...

weixin_30633507的博客 107

T-SQL中的递归 --作者:Alexander Kozak

[摘要]递归是经典的技巧之一,主修计算机科学的学生经常通过编写汉诺塔程序来学习它。在这篇文章中,Alex Kozak探讨了T-SQL中的递归。 我曾经担任过大学的老师。我在开始讲授子查询时,会让学生从Northwind数据库的employees表中,找到年龄最小的雇员。大多数的学生很轻松地给出了下面的解答。 SELECT * FROM employeesWHERE BirthDate...

weixin_30420305的博客 91

T-SQL使用CTE递归

DECLARE @DiGui TABLE ( Id INT, ParentId INT ) INSERT INTO @DiGui ( Id , ParentId ) SELECT 1 , 2 UNION SELECT 2 , ...

weixin_30341735的博客 103

使用T-sql递归语句注意的地方

当我们使用如下递归时: --Creates an infinite loop WITH cte (EmployeeID, ManagerID, Title) as (     SELECT EmployeeID, ManagerID, Title     FROM dbo.MyEmployees     WHERE ManagerID IS NOT NULL   UN

IT点滴 1218

T-SQL递归学习

参考文件:http://blog.youkuaiyun.com/jianxin160/article/details/7166379 结合项目修改如下: 表结构: CREATE TABLE [dbo].[UserRelation]( [UserRelationId] [int] IDENTITY(1,1) NOT NULL, [UserId] [bigint] NOT NULL, [LeaderI

Jimcn2009的专栏 693

T_Sql 处理递归操作

数据库表数据: 1.使用CTS查询 --1.指定ID ,当前项+向下查询所有的子节点 with temp as ( select * from Tree where NodeID=4 union all select t.* from Tree t,temp p where t.ParentID=p.NodeID ) select * from temp; --

天马3798 790

SQL在存储过程中使用递归

<!--google_ad_client = "pub-2947489232296736";/* 728x15, 创建于 08-4-23MSDN */google_ad_slot = "3624277373";google_ad_width = 728;google_ad_height = 15;//--><script type="text/javascript"

zgqtxwd的专栏 726
上一篇: 动态声明数组维数
下一篇: JS 下 System.Data.Connection V1
slightboy
博客等级 码龄23年 3粉丝 18原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值