Building a T-SQL Loop

November 5, 2003
T-SQL Programming Part 2 - Building a T-SQL Loop
By Gregory A. Larsen

This is the second article in my T-SQL programming series. This article will discuss building a program loop using T-SQL. In addition to talking about building a loop, I will also discuss ways of controlling the loop processing, and different methods to break out of a loop.

A programming loop is a chunk of code that is executed over and over again. In the loop some logic is executed repeatedly in an iterative fashion until some condition is met that allows the code to break out of the loop. One example of where you might use a loop would be to process through a set of records one record at a time. Another example might be where you need to generate some test data and a loop would allow you to insert a record into your test data table with slightly different column values, each time the loop is executed. In this article I will discuss the WHILE, BREAK, CONTINUE, and GOTO statements.
WHILE Statement
In T-SQL the WHILE statement is the most commonly used way to execute a loop. Here is the basic syntax for a WHILE loop:

WHILE <Boolean expression> <code block>

Where a <Boolean expression> is any expression that equates to a true or false answer, and the <code block> is the desire code to be executed while the <Boolean expression> is true. Let's go through a real simple example.

In this example I will increment a counter from 1 to 10 and display the counter each time through the WHILE loop.

declare @counter intset @counter = 0while @counter < 10begin set @counter = @counter + 1 print 'The counter is ' + cast(@counter as char)end

Here the code executes the WHILE statement as long as the @counter integer variable is less than 10, this is the Boolean expression of the WHILE loop. The @counter variable starts out at zero, and each time through the WHILE loop it is incremented by 1. The PRINT statement displays the value in the @counter variable each time through the WHILE loop. The output from this sample looks like this:

The counter is 1 The counter is 2 The counter is 3 The counter is 4 The counter is 5 The counter is 6 The counter is 7 The counter is 8 The counter is 9 The counter is 10

As you can see, once the @counter variable reaches 10 the Boolean expression that is controlling the WHILE loop is no longer true, so the code within the while loop is no longer executed.

Not only can you have a single while loop, but you can have WHILE loops inside WHILE loops. Or commonly know as nesting of WHILE loops. There are lots of different uses where nesting is valuable. I commonly use nesting of WHILE loops to generate test data. My next example will use the WHILE loop to generate test records for a PART table. A given PART record is uniquely identified by a Part_Id, and a Category_Id. For each Part_Id there are three different Category_Id's. Here is my example that generates 6 unique records for my PART table using a nested WHILE loop.

declare @Part_Id intdeclare @Category_Id intdeclare @Desc varchar(50)create table PART (Part_Id int, Category_Id int, Description varchar(50))set @Part_Id = 0set @Category_Id = 0 while @Part_Id < 2begin set @Part_Id = @Part_Id + 1 while @Category_Id < 3 begin set @Category_Id = @Category_Id + 1 set @Desc = 'Part_Id is ' + cast(@Part_Id as char(1)) + ' Category_Id ' + cast(@Category_Id as char(1)) insert into PART values(@Part_Id, @Category_Id, @Desc ) end set @Category_Id = 0 endselect * from PARTdrop table PART

Here is the output from the SELECT statement at the bottom of this nested WHILE loop example.

Part_Id Category_Id Description ----------- ----------- ----------------------------------------- 1 1 Part_Id is 1 Category_Id 11 2 Part_Id is 1 Category_Id 21 3 Part_Id is 1 Category_Id 32 1 Part_Id is 2 Category_Id 12 2 Part_Id is 2 Category_Id 22 3 Part_Id is 2 Category_Id 3

As you can see, by using a nested WHILE loop each combination of Part_Id and Category_Id is unique. The code within the first WHILE loop controlled the incrementing of the Part_Id, where as the second WHILE loop set the Category_Id to a different value each time through the loop. The code within the first while loop was executed only twice, but the code inside the second WHILE loop was executed 6 times. Thus giving me 6 sample PART records.

BREAK and CONTINUE Statements
Now sometimes you want to build a loop that will process through logically to the end most of the time, but not all the time. In other words, you may want to break out of the loop if some particular condition arises. Also in addition to breaking out of the loop, you may not want to process all the code in the loop before going back to the top of the loop and starting through the next iteration of the loop. For these kinds of programming requirements SQL Server provides the BREAK and CONTINUE statements.

The BREAK statement exits out of the inner most WHILE loop, and proceeds to the statement following the END statement that is associated with the loop in which the BREAK statement is executed. The CONTINUE statement skips executing the rest of the statements between the CONTINUE statement and the END statement of the current loop and starts executing at the first line following the BEGIN statement of the current WHILE loop. Let's go though a couple of BREAK and CONTINUE examples.

For the BREAK statement I'm going to modify my last example that generated PART table records. This time I'm going to BREAK out of the inner WHILE loop when Category_ID is 2 and PART_ID is 1. Here is my code for the BREAK statement.

declare @Part_Id intdeclare @Category_Id intdeclare @Desc varchar(50)create table PART (Part_Id int, Category_Id int, Description varchar(50))set @Part_Id = 0set @Category_Id = 0 while @Part_Id < 2begin set @Part_Id = @Part_Id + 1 while @Category_Id < 3 begin set @Category_Id = @Category_Id + 1 If @Category_ID = 2 and @Part_ID = 1 Break set @Desc = 'Part_Id is ' + cast(@Part_Id as char(1)) + ' Category_Id ' + cast(@Category_Id as char(1)) insert into PART values(@Part_Id, @Category_Id, @Desc ) end set @Category_Id = 0 endselect * from PARTdrop table PART

Here is the output for this code that contains a BREAK statement inside the inner WHILE loop.

Part_Id Category_Id Description ----------- ----------- ----------------------------------------- 1 1 Part_Id is 1 Category_Id 12 1 Part_Id is 2 Category_Id 12 2 Part_Id is 2 Category_Id 22 3 Part_Id is 2 Category_Id 3

From this output you can see that no records were inserted for Part_Id = 1 and Category_Id =2 or 3, where as there are records for Part_Id = 2 with all values for the Category_Id column. This is because the IF statement in the inner loop forced the BREAK statement to exit the inner loop. Since there were records generate for Part_Id = 2, shows that the BREAK statement only exited the inner loop and not the outer loop.

Now just to stay with the same example I've been using, let's replace the BREAK statement in the code above with a CONTINUE statement. Here is the code for demonstrating the CONTINUE statement.

declare @Part_Id intdeclare @Category_Id intdeclare @Desc varchar(50)create table PART (Part_Id int, Category_Id int, Description varchar(50))set @Part_Id = 0set @Category_Id = 0 while @Part_Id < 2begin set @Part_Id = @Part_Id + 1 while @Category_Id < 3 begin set @Category_Id = @Category_Id + 1 If @Category_ID = 2 and @Part_ID = 1 Continue set @Desc = 'Part_Id is ' + cast(@Part_Id as char(1)) + ' Category_Id ' + cast(@Category_Id as char(1)) insert into PART values(@Part_Id, @Category_Id, @Desc ) end set @Category_Id = 0 endselect * from PARTdrop table PART

When you use the CONTINUE statement you get the following output.

----------- ----------- ----------------------------------------- 1 1 Part_Id is 1 Category_Id 11 3 Part_Id is 1 Category_Id 32 1 Part_Id is 2 Category_Id 12 2 Part_Id is 2 Category_Id 22 3 Part_Id is 2 Category_Id 3

As you can see, when I use the CONTINUE statement only the record with Category_Id = 2 and Part_Id = 1 is missing. This is because the CONTINUE statement does not break out of the inner WHILE loop but only goes back to the top of the WHILE loop without inserting the record. This happens only when Category_Id is 2 and Part_Id is equal to 1. When Part_Id = 1 and Category_Id = 3 the insert statement is still executed.
GOTO Statement
The BREAK statement will only exit you from the currently processing WHILE loop, it will not break out of all WHILE loops. However, occasionally this is the kind of functionality your T-SQL script needs. To have your code break out of all WHILE loops, no matter how many nested WHILE statements you have, you will need to use the GOTO statement. Now I know most programmers cringe at the thought of using the GOTO statement, but in this case I feel the GOTO is an except able practice. Using my same example I will use the GOTO to break out of both WHILE loops, when the PART_Id = 1 and the Category_ID=3.

declare @Part_Id intdeclare @Category_Id intdeclare @Desc varchar(50)create table PART (Part_Id int, Category_Id int, Description varchar(50))set @Part_Id = 0set @Category_Id = 0 while @Part_Id < 2begin set @Part_Id = @Part_Id + 1 while @Category_Id < 3 begin set @Category_Id = @Category_Id + 1 If @Category_ID = 3 and @Part_ID = 1 GOTO BREAK_OUT set @Desc = 'Part_Id is ' + cast(@Part_Id as char(1)) + ' Category_Id ' + cast(@Category_Id as char(1)) insert into PART values(@Part_Id, @Category_Id, @Desc ) end set @Category_Id = 0 endBREAK_OUT:select * from PARTdrop table PART

Here is the output from this GOTO code:

Part_Id Category_Id Description ----------- ----------- ----------------------------------------- 1 1 Part_Id is 1 Category_Id 11 2 Part_Id is 1 Category_Id 2

Here the GOTO logic stopped the insertion of records into the PART table when @Category_ID = 3 and @Part_Id = 1. This is done by executing the "GOTO BREAKOUT" statement. Note that when this GOTO statement was executed it branched to the label "BREAK OUT:" which can be found following the END statement for the first, outer most WHILE statement.
Conclusion
Hopefully now you have a better idea of how to code a T-SQL WHILE loop. I've explained how to control the WHILE loop, break out of a loop by using the BREAK statement, use the CONTINUE statement to skip some of the code in the while loop, and/or break out of all WHILE loops using the GOTO statement. The techniques I've described should give you the basis for building all your WHILE statements from a single WHILE loop to a complex set of nested WHILE loops. My next article in this series will discuss how to process through a set of records.

时间: 2024-09-13 16:40:40

Building a T-SQL Loop的相关文章

刚刚梦贴了IP表,这个是一个把它存入SQL里的程序

程序 写得比较粗糙,还有一点错误,不过能达到效果,凑合着用了,是4266条,不知道对也不对 create.asp-----------------------------------------------------------------------<%'Option ExplicitServer.Scripttimeout = 1000On Error Resume Next ' Define your sql info herestrSQLDSN = "xxxx"strS

把IP表存入SQL里的程序

写得比较粗糙,还有一点错误,不过能达到效果,凑合着用了,是4266条,不知道对也不对 create.asp-----------------------------------------------------------------------<%'Option ExplicitServer.Scripttimeout = 1000On Error Resume Next ' Define your sql info herestrSQLDSN = "xxxx"strSQLU

怎样把IP表存入SQL里的程序

程序 写得比较粗糙,还有一点错误,不过能达到效果,凑合着用了,是4266条,不知道对也不对 create.asp-----------------------------------------------------------------------<%'Option ExplicitServer.Scripttimeout = 1000On Error Resume Next ' Define your sql info herestrSQLDSN = "xxxx"strS

Can I use MyBatis to generate Dynamic SQL without executing it?

  Although MyBatis was designed to execute the query after it builds it, you can make use of it's configuration and a little bit of "inside knowledge" to get to what you need. MyBatis is a very nice framework, unfortunately it lacks on the docum

不通过数据源完全控制MDB数据库

控制|数据|数据库|数据源 <% ' BEGIN USER CONSTANTS ' To just use a DSN, the format is shown on the next line: 'Const DSN_NAME = "DSN=ASP101email" ' Two other samples I used it with. Left in as syntax examples for DSN-less connections 'Const DSN_NAME = &

基于pgrouting的任意两点间的最短路径查询函数

    前面文章介绍了如何利用postgresql创建空间数据库,建立空间索引和进行路径规划.但是在真实的场景中用户进行路径规划的时候都是基于经纬度数据进行路径规划的,因为用户根本不会知道道路上节点的ID.因此文本讲述如何查询任意两点间的最短路径.     一.定义函数名及函数参数         函数名定义为: pgr_fromAtoB         参数设置分别为:                  输入为数据库表名,起点和终点的经纬度坐标                  输出为:路段序

PostgreSQL 最佳实践 - 水平分库(基于plproxy)

背景 我一直以来都比较推荐plproxy这个PostgreSQL代理软件, 因为它小巧灵活好用, 效率高. 最近朋友邀请我给他们做个分布式的方案, 所以又把plproxy翻出来了. 本文讲一讲在单节点中如何快速的部署plproxy环境. 环境 PostgreSQL 9.3.1 plproxy 2.x plrpoxy节点 hostaddr 172.16.3.150 port 1921 user proxy password proxy dbname proxy schema digoal // 这

Yii初学者必看-yii 表单验证规则

对yii深入了解总结出:希望对初学者有些帮助 Active Record (AR) 是一个流行的 对象-关系映射 (ORM) 技术. 每个 AR 类代表一个数据表(或视图),数据表(或视图)的列在 AR 类中体现为类的属性,一个 AR 实例则表示表中的一行. 常见的 CRUD 操作作为 AR 的方法实现.因此,我们可以以一种更加面向对象的方式访问数据. 例如,我们可以使用以下代码向 tbl_post 表中插入一个新行. yii 表单验证规则 <?php classContactFormexten

Windows Azure初学者非常重要的学习资料Training Kit

Windows Azure初学者非常重要的学习资料--Training Kit在8月的下旬又进行了一次小的更新,增加了两套讲课用针对Windows Azure Mobile Service的PPT,以及15个动手案例.个人认为最主要的更新来自于对Mobile Service的案例,这对于Windows 8开发者的学习非常重要. Hands On Labs: 1. Introduction to Windows Azure2. Exploring Windows Azure Storage3. D

ALICloudDB for PostgreSQL 试用报告 - 4 水平分库 之 节点扩展

RDS现在还欠缺一个功能,就是数据库克隆,你可以这么理解,给现有的数据库创建STANDBY,然后将这个STANDBY激活,就完成了对数据库的克隆. 为什么我们需要数据库克隆功能呢? 这会使得数据库的扩容变得非常简单,比如我们这里的应用场景,如果要将16个RDS,变成32个RDS,那么克隆无疑是最好的办法.因为不需要做逻辑数据迁移的事情,只需要删除不需要的数据库,以及调整plproxy的cluster配置即可. 我们先假设RDS有创建STANDBYD的功能(相信未来会增加),看看如何来实现RDS的