T-SQL recursive

xiaoxiao2021-03-06  45

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 The next Youngest, Many Were Stumped. A Few Officeed 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 E-Commerce Project and someone Was Paying Me To do it.

OverviewRecursion, 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 exissrs (Select * from sysobjects

WHERE ID = Object_ID ('spu_testrecursion')

And ObjectProperty (id, 'isprocedure') = 1)

Drop Procedure SPU_TESTRECURSION

Go

Create Proc SPU_TESTRECURSION

@Lestel 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 (@LesL = 0 or @Lesque = 1)

Begin

SELECT @ Answer = 'SELECT MAX (' @colname ')

From ' @tblname

End

Else

Begin

SELECT @ONE_SS = @Lestel - 1

- Recursively Call itself

EXEC SPU_TESTRECURSION @ONE_SS, @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 do not 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 Difer Because Data For Table CheckRank IS Generated):

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 exampleThe 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 mention 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 ........................ ..

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) ƒƒ 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't. 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 Character istics 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 RoutineImagine a Sample-String '12 / 23/34/45/56/67/78/89/90/67/22/33/44/55/66/77/88/99/00/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);. (N) = Substring (String, POS (N-1) 1, POS (N) - POS (N-1) - 1), The TH 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 exampleTo 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 Faled 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 TEN / * 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 Docty 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

转载请注明原文地址:https://www.9cbs.com/read-54879.html

New Post(0)