`
afeiod
  • 浏览: 23037 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

数据递推

    博客分类:
  • SQL
阅读更多

讨论] 经验总结:T-SQL中的经典技巧──递归

针对T-SQL中的递归进行了经典的论述,详细内容请参考下文: 我曾经担任过大学的老师。我在开始讲授子查询时,会让学生从Northwind数据库的employees表中,找到年龄最小的雇员。大多数的学生很轻松地给出了下面的解答。 SELECT * FROM employees WHERE BirthDate = (SELECT MAX(BirthDate) FROM employees) 然而,当我要求他们找出下一个年纪最小的雇员的时候,许多人被难住了。有几个学生给出了下面的解答。 SELECT * FROM employees WHERE BirthDate= (SELECT MAX(BirthDate) FROM employees WHERE BirthDate < (SELECT MAX(BirthDate) FROM employees)) 这个问题的递归特性是很明显的,我记得曾经信誓旦旦地要编写一个可以返回任何数据层次(年龄、重量、分数等等)的第N个记录的存储过程。但是,直到两年后我在做一个有人给我出资的电子商业项目时,才最终完成了这个存储过程的编写。
<script src="http://bbs.zdnet.com.cn/js/shtml/cheshi.js" type="text/javascript"></script>
概述

递归,发生在一个存储过程或者一个函数调用它本身的时候,是一个广为人知并且很有用的数学和编程概念。然而,它也是很危险的,例如它会导致无限循环。(这可能是SQLSERVER2000将嵌套调用和嵌套层数限制为32的一个原因,你可以在存储过程运行时,使用全程变量@@NESTLEVELl来检查该过程的嵌套层。)DBMS程序员要牢记:递归调用会很显著地延长事务处理的时间,因此,在在线事务处理系统中通常要避免使用递归。

我们由一个例子开始。假设你有一个存有学生测验成绩的名为checkRank的表,你的任务是找出成绩排在第4位到第10位的学生。我写了一个名为fillCheckRank的脚本和一个名为spu_testRecursion的存储过程来演示该技巧。该脚本以相关的随机数据创建和装载checkRank表(见附带光盘中的CreateCheckRank.sql),但是该存储过程(见列表1)却复杂得多,并且使用了递归算法、嵌套过程调用和嵌套子查询来计算答案。
 
列表1 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

*/

注:当你删除和创建该存储过程时,你会收到由于缺失“spu_testRecursion”无法在当前存储过程的sysdepend中添加行的信息。但是不必担心,该存储过程仍然可以创建。更多信息请见Q24641。
 
该存储过程会收到这些参数

@level:在层次中的等级或者位置

@tblName:表名

@answer:返回生成的Select语句的输出参数

并返回这两个参数:

值,对应于在层次结构中所需的级别或者位置

一段您可以获得同等结果的脚本

为得到成绩为第四名的结果,你可以这样做:

DECLARE @answer varchar(4000)

EXEC spu_TestRecursion 4, 'checkRank', 'testPoints',

@answer output

下面是我的结果(你的结果可能不一样,因为表里面的数据是随机生成的)。

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

这样,第四名对应的分数是93分。

当我开始执行相同的查询来得到第10名的分数时,我的答案是87分。第4到第10的排名问题的最终答案也可以通过检索来推断,或者通过运行一个查询来确定(见附带光盘中的4ththru10th.sql)
 
实践例子

接下来的场景对于许多电子商务交易是很常见的。假设交易双方――买方和卖方开始交易、报价或者询价。报价和询价可以发给有限数目的买方(卖方),或者可以发给和被所有参加该价格交换的成员看到。

当对询价的第一个出价或者响应到达时,交易开始了。从这时起,各种不同的情形都是可能的,并且每一种情形都将创建自己的交易链。报价、出价或者这个链中的其他部分都可以被终止、取消、拒绝或者接受。用户可以发送一个还价、再收到一个还价等等。这个循环可以按照市场规则反复开始。对基本规则的各种背离也是允许的。例如,你可能会允许当事人对已经完成的交易作一些有限的变更,如,依次地接受或者拒绝,等等。

交易的实际执行情况在细节上可能是会变化的,但是通常交易链的每个组成部分是以可能保存为XML文档、Java对象或者可以拆分和存储在表格中的文档来体现的。你可以使用文档路径来找到这些文档在交易链中的顺序。这与链接表类似,除根文件和结束文件以外,每个表(路径)中的组成部分均有与其前和其后文件的联系。

例如,假定有一个名为Documents的表,存有所有文档并有一个名为docPath的列。对于docID(主键)=12315且docPath= 12217/12267/12299/12315的行,存在下一个洽谈链:12217(作为根文档或模板的已提出的报价原始资料).12267(已提出的报价项目――实际的报价).12299(出价).12315(反向文档)

现在假定我要分析交易过程, 找到最终文档和原始文档中的价格、运费、数量和价值的差别。 如果我要分析交易失败的可能性, 我必须用取消、终止或者拒绝的状态来标注文档。 为了分析实际价值和数量,我需要以接收状态提取协议和购货订单。在这两种情况中, 最终文档将是docPath中的最后一个文档(在我们的例子里,12315),但是原始文档不是第一个文档。在我的例子里,第一个文档(12217)是一个只有基本报价参数的模板。 ( 只有当我得到一个出价,我才能计算货费、总价和其他参数。) 因此在我的例子里,第2 个文档(12267) 是一个原始文档。 总之,来自交易链的任何文档,除了最后一个之外,都可能是原始文档,因为每个随后的文档会给原始文档添加一些新特性,而且我可能正好对那些新参数感兴趣。

因此我的任务是根据一些条件选出docPath的第n个组成部分,如果你使用T-SQL函数编写一个脚本、存储过程或者UDF,这会是一项微不足道的工作。但是,如果你想要使用SELECT语句(你可以想像一下实时的电子商务)得到"on the fly"的结果,任务变得非常复杂。
 
子链帮助程序

假设一个示例字符串'12/23/34/45/56/67/78/89/90/11/22/33/44/55/66/77/88/99/00/A/B/E/F/'。为了找到这个字符串的任何一个组成部分,我们可以使用一个简单的算法选出定界符的两个连续位置之间的子串:

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),
 
T_SQL的方法如下:

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.

spu_indDocID存储过程(在下载文件中可以得到)生成允许我们选取这个字符串第1至第31个任意一个组成部分的脚本。该过程执行了我先前概略介绍过的算法并且使用了这些参数:

@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. 例子

为了察看交易链的例子, 运行FindSource.sql脚本来看一下交易链的例子。脚本的第一部分创建了一个名为“documents“的表, 然后在其中载入了示例数据。这些是这种情形的潜在规则:

假如在docPath中的第一个(最左边)的文档的docTypeID是1,那么在docPath中的第一个文档是文档源。

假如第一个文档的docTypeID是2,那么文档源是docPath中的第二个文档

假如第一个文档的docTypeID是3,那么文档源是docPath中的第三个文档

因此利用存储过程sup_findDocID,它能够为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
 
最后,使用该脚本来看所有未成功交易的原始资料:

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)
 
分享到:
评论

相关推荐

    bestspeculate.rar_matlab循环递推_最小二乘_最小二乘法_递推 MATLAB_递推 最小二乘法

    在描述中提到的"根据最小二乘法的递推公式",指的是在线性系统中,当新数据不断到来时,可以通过递推公式更新参数估计,而无需重新计算整个数据集的最小二乘解。这种递推方法通常更高效,特别是在处理大数据流或实时...

    matlab读取txt文件数据,然后限幅+递推平均滤波

    matlab读取txt数据,然后然后限幅+递推平均滤波,只需改变文件路径就可用

    递推平均滤波python.pdf

    在此文档中,numpy被用来获取输入数据的形状(shape),并在递推平均滤波计算中处理数组。 4. 数据类型转换与处理 在文档中,通过scipy.io的loadmat函数加载了存储在.mat文件中的数据。这表明文件数据是以MATLAB的...

    《算法与数据结构》—递推方程及算法分析

    《算法与数据结构》—递推方程及算法分析 《算法与数据结构》是一门重要的计算机科学课程,旨在教授学生基本的算法设计和数据结构实现。递推方程作为一种重要的算法设计方法,在解决复杂问题时扮演着关键角色。本文...

    算法和数据结构——迭代和递推(上).pdf

    首先,“算法和数据结构——迭代和递推(上)”这个标题提示了文档将讨论算法领域中非常核心的两个概念:迭代(Iteration)和递归(Recursion)。这两者在计算机科学中用于实现程序的重复操作和问题求解的基本方法。...

    数据结构与算法 递推算法

    数据结构与算法 递推算法,Devc++环境,数据结构基础算法

    基于递推预报误差算法的前馈神经网络的设计

    本文所介绍的是一种基于递推预报误差算法的前馈神经网络的设计。这种网络设计在非线性系统模型的仿真试验中表现出了良好的效果,同时在文中也给出了试验的结果和网络应用的讨论。 首先需要了解的是前馈神经网络。...

    算法与数据结构之递推与递归

    算法与数据结构之递推与递归

    递推专项训练 包括题目和标程

    - **复杂度分析**:理解递推解法的时间和空间复杂度,优化算法以适应大规模数据。 通过本专题训练,学习者将能够熟练运用递推解决实际问题,提升编程竞赛的竞争力,并为未来在计算机科学领域的深入学习打下坚实...

    递推平均滤波与加权滤波_加权滤波_递推平均滤波_

    在信号处理领域,滤波是一种常见的技术,用于去除噪声、平滑数据或提取特定频率成分。本主题将深入探讨两种滤波方法:递推平均滤波(Recursive Average Filtering)和加权滤波(Weighted Filter),并通过C++编程...

    最小二乘法 参数辨识

    解决这一问题的方法之一是采用数据递推饱和解决方案,如通过定期重置或重新初始化某些参数来避免计算饱和。 #### 七、增广最小二乘法 增广最小二乘法是对传统最小二乘法的一种扩展,主要用于处理含有多个模型的...

    递推最小二乘法的代码示例

    在IT领域,尤其是在数据分析、控制理论和机器学习中,递推最小二乘法(Recursive Least Squares, RLS)是一种非常重要的算法。它主要用于在线估计线性系统的参数,即随着时间的推移,通过连续的数据流更新模型参数。...

    递推最小二乘法及模型阶次辨识

    RLS算法是一种在线估计方法,它在数据流中实时更新参数估计,因此特别适用于处理大量数据的情况。 递推最小二乘法起源于经典的最小二乘法,后者是通过最小化误差平方和来寻找最佳参数估计。然而,传统的最小二乘法...

    递推最小二乘C+.zip

    本压缩包“递推最小二乘C+.zip”提供了一个C++实现的递推最小二乘算法,这在处理大量实时数据时尤其有用,因为它的计算效率较高。 递推最小二乘法(Sequential/Recursive Least Squares, RLS)是在线学习算法的一种...

    RFF遗忘因子递推算法

    在IT领域,特别是机器学习和数据处理中,"RFF遗忘因子递推算法"是一种用于时间序列分析和预测的重要技术。这个算法的核心在于其结合了辨识模型和遗忘因子的概念,以实现高效、实时的数据处理。 首先,让我们深入...

    xitongbianshi.zip_数据最小二乘_递推最小二乘

    递推最小二乘法(Recursive Least Squares, RLS)是一种在线学习算法,它可以在新数据点到来时实时更新模型参数。与传统的批量最小二乘不同,RLS不需要存储所有历史数据,而是通过逐步调整权重来适应新数据,这使得...

    粒子滤波推导pdf

    在状态估计问题中,贝叶斯公式被用来根据旧的状态估计和新的观测数据递推地得到新的状态估计。在贝叶斯滤波的上下文中,预测过程和更新过程都是贝叶斯公式的应用。在预测步骤中,给定前一时刻的状态,我们可以计算出...

    多元系统耦合带遗忘因子有限数据窗递推最小二乘辨识方法

    针对多元线性或非线性回归系统, 将耦合辨识思想与带遗忘因子有限数据窗辨识理论相结合, 提出一种耦合带遗忘因子有限数据窗递推最小二乘辨识算法. 该算法每次递推计算时既不涉及矩阵求逆运算, 又可以克服数据饱和现象...

    TEST_leastsquare_递推最小二乘法_递推_在线系统辨识_在线辨识_

    递推最小二乘法是一种动态算法,它的主要目标是在接收到新的数据点时,连续更新模型参数,使得所有已观测到的数据点的预测误差平方和最小。这种方法的优势在于计算效率高,适合处理大数据流,且具有良好的稳定性。在...

    递推与递归应用[参照].pdf

    递推法是基于递推关系来求解问题的方法,通常从一组初始边界条件开始,通过递推公式逐步计算出所有后续的数据项,最终达到求解问题的目标。这里的边界是指问题定义中的初始条件,对于斐波那契数列,边界条件是F(1)和...

Global site tag (gtag.js) - Google Analytics