FastReport教程:MS SQL中的递归

本文介绍了如何使用FastReport.Net结合通用表表达式(CTE)在MS SQL中进行递归查询。CTE允许在查询中多次使用数据集,优化复杂查询并实现递归功能,如树形数据的遍历。通过示例展示了如何创建和使用递归CTE来获取分层数据,对比了CTE与临时表、表变量的区别和优势。

下载FastReport.Net最新版本

有时,需要存储过程或函数才能多次使用样本的结果。在这种情况下,我们经常使用临时表。但是,值得考虑临时表的一些优点和缺点。

好处:

  • 临时表是完整的表。因此,您可以为它们创建索引和统计信息。这可以显着加快他们的工作。

缺点:

  • 填写与数据移动相关的临时表。虽然这是一个简单的插入操作,但磁盘上仍然存在大量数据的负载;
  • 存在查询执行时间增加的风险。临时表在tempdb数据库中创建。而且这个基地的负荷很大。

考虑到使用临时表的风险,使用通用表表达式看起来更具吸引力。

通用表表达式

公用表表达式(CTE)是一个带有公用表的表达式,可以在查询中多次使用。CTE不会保存数据,但会创建类似临时视图的内容。有人可能会说CTE是主查询之前的子查询。但这并不完全正确,因为子查询不能多次使用,但是,CTE可以。

在哪些情况下使用通用表表达式更好?

  1. 创建递归查询,使用它可以以分层形式获取数据;
  2. 在同一查询中多次引用数据集;
  3. 为了替换视图,临时表,表变量。

CTE的优点包括:递归,高速查询,简洁查询。

缺点只能在有限的使用中。CTE只能用于它所属的查询。您不能在其他查询中使用它。在这种情况下,您将不得不使用临时表或表变量。 通用表表达式简单且递归。 简单的不包括对自己的引用,并且递归分别包括。 递归CTE用于返回分层数据。 考虑一个简单CTE语句的示例:

 WITH CTEQuery (Field1, Field2)
 AS
 (
 SELECT (Field1, Field2) FROM TABLE
 )
 SELECT * FROM CTEQuery

这里CTEQuery是CTE的名称;

  • Field1,Field2 - 请求的字段名称;
  • Table - 从中​​选择数据以在主查询中使用的一些表。

在此示例中,可以而不是显式指定选择字段,因为我们从TestTable表中选择所有字段:

WITH CTEQuery
 AS
 (
 SELECT * FROM Table
 )
SELECT * FROM CTEQuery

在CTE的帮助下,如果取出CTE中的部分逻辑,则可以优化主查询。事实是,CTE允许您一次创建多个表达式(查询)。因此,您可以使用CTE将复杂查询拆分为几个初步“View”,然后将它们链接到一个公共查询中:

WITH CTEQuery1 (Field1, Field2) AS
(
 SELECT Field1 AS ID, Field2 FROM Table1
 WHERE Field2 >= 1000
),
CTEQuery2 (Field3, Field4) AS
(
 SELECT Field3 AS ID, Field4 FROM Table2
 WHERE Field4 = 'Москва'
)
 
SELECT * FROM CTEQuery1 INNER JOIN CTEQuery2 ON CTEQuery2.ID = CTEQuery1.ID

如上所述,CTE的主要目的是递归。递归的典型任务是树遍历。所以我们可以在“with”的帮助下构建一棵树。递归查询结构首先出现在SQL Server 2005中。 看一下WITH语句:

WITH RecursiveQuery AS
(
 {Anchor}
 UNION ALL
 {Joined TO RecursiveQuery}
)
SELECT * FROM RecursiveQuery

{Anchor} - anchor,一个定义树的初始元素的查询(分层列表)。通常在锚中有一个WHERE子句,用于定义表的特定行。 在UNION ALL之后,目标表从JOIN跟随到CTE表达式。 {加入RecursiveQuery} - 从目标表中选择。这通常与锚点中使用的表相同。但是在这个查询中,它连接到CTE表达式,形成递归。此连接的条件决定了父子关系。这取决于你是去树的上层还是下层。 让我们看一个返回组织单元列表的递归查询。准备此请求的数据:

CREATE TABLE Department
(
ID INT,
ParentID INT,
Name VARCHAR(50)
)
 
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (1, 0, 'Finance Director')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (2, 1, 'Deputy Finance Director')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (3, 1, 'Assistance Finance Director')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (4, 3, 'Executive Bodget Office')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (5, 3, 'Comptroller')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (6, 3, 'Purchasing')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (7, 3, 'Debt Management')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (8, 3, 'Risk Management')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (9, 2, 'Public Relations')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (10, 2, 'Finance Personnel')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (11, 2, 'Finance Accounting')
INSERT INTO Department ( ID, ParentID, Name ) 
VALUES (12, 2, 'Liasion to Boards and Commissions')

已经清楚的是,组织中的分支结构是分层的。我们的任务是获得一份隶属于财务总监助理的部门清单。如果我们在分层树的上下文中进行讨论,那么我们必须找到一个分支及其叶子。 但首先,让我们看看整个分部列表:

ID

ParentID

Name

1

0

Finance Director

2

1

Deputy Finance Director

3

1

Assistance Finance Director

4

3

Executive Bodget Office

5

3

Comptroller

6

3

Purchasing

7

3

Debt Management

8

3

Risk Management

9

2

Public Relations

10

2

Finance Personnel

11

2

Finance Accounting

12

2

Liasion to Boards and Commissions

头部有财务总监,副手和助理报表给他。他们每个人在其管辖范围内都有一组单位。ParentID字段指示“主机”标识符。因此,我们有一个现成的主从连接。 让我们用WITH编写一个递归查询。

WITH RecursiveQuery (ID, ParentID, Name)
AS
(
 SELECT ID, ParentID, Name
 FROM Department dep
 WHERE dep.ID = 3
 UNION ALL
 SELECT dep.ID, dep.ParentID, dep.Name
 FROM Department dep
 JOIN RecursiveQuery rec ON dep.ParentID = rec.ID
)
SELECT ID, ParentID, Name
FROM RecursiveQuery

在此示例中,清楚地指示了要在CTE中选择的字段的名称。但是,内部查询具有相同的字段。因此,您只需删除此列表以及括号即可。 在CTE内部,我们有两个类似的查询。第一个选择我们正在构建的树的根元素。第二个是所有后续的从属元素,因为它与CTE本身有关。SQL中的“递归”实际上不是递归,而是迭代。您需要以JOIN作为循环提交查询,然后一切都将立即清除。在每次迭代中,我们都知道前一个样本的值并获得从属元素。在下一步中,我们获得前一个样本的从属元素。也就是说,每次迭代都是向下或向上转换,具体取决于通信条件。 上述查询的结果是:

ID

ParentID

Name

3

1

Assistance Finance Director

4

3

Executive Bodget Office

5

3

Comptroller

6

3

Purchasing

7

3

Debt Management

8

3

Risk Management

但是如果不使用CTE,这个查询会是什么样子:

DECLARE @Department TABLE (ID INT, ParentID INT, Name VARCHAR(50), Status INT DEFAULT 0)
-- First, we select the anchor in the table variable - the initial element from which we build the tree.
INSERT @Department
SELECT ID, ParentID, Name, 0
 FROM Department dep
 WHERE dep.ID = 3
 
DECLARE @rowsAdded INT = @@ROWCOUNT
-- We are going through a cycle until new departments are added in the previous step.
WHILE @rowsAdded > 0 
BEGIN
-- Mark entries in a table variable as ready for processing
UPDATE @Department SET Status = 1 WHERE Status = 0
-- Select child records for the previous record
INSERT @Department 
SELECT dep.ID, dep.ParentID, dep.Name, 0
 FROM Department dep
 JOIN @Department rec ON dep.ParentID = rec.ID
AND rec.Status = 1
SET @rowsAdded = @@ROWCOUNT 
 -- Mark entries found in the current step as processed 
 UPDATE @Department SET Status = 2 WHERE Status = 1 
END
SELECT * FROM @Department

这样的循环比CTE表达慢得多。此外,它需要创建一个表变量。并且代码量增加了一倍。因此,CTE表达式是MS SQL中递归树遍历的最佳解决方案。

这个指南让你熟悉报表设计器和了解报表设计的基本概念(各种区域,数据源,二次表,等等)。指南将帮助你开始用FastReport创建报表,但它不能告诉你怎样使用其它基本的报表设计器。 如果你不熟悉报表设计器,我们建议你参考QuickReport的帮助系统。QuickReport的指南已经包含在你的Delphi拷贝中。QuickReport的大部分基本概念也适用于FastReport,然而,FastReport能提供更多的弹性和最终用户的自定义。 关于FastReport的说明 FastReport是高弹性的报表设计器,用于报表的数据可以从任何类型的数据源获取,包含字符列表,BDE数据库,ADO数据源(不使用BDE),Interbase(使用IBO),Pascal数组和记录,以及一些不常用的数据源。 整个FastReport系统是用Delphi的Pascal编写的。FastReport不需要动态链接库,但需在你的项目中占用大约400kb(Delphi 5)。如果你想最终用户拥有设计能力,这将在你的.EXE中增加大约500kb。虽然这看上去比较大,但这只是其它设计的几分之一。你同样应该考虑到FastReport不仅仅只是包含最终用户更改报表设计的能力,还能够适应查询和数据库的变化。FastReport还包含自己的脚本语言,让应用程序和最终用户能够更容易地改变报表。如果你的大部分应用使用FastReport,你可以简单地配置FastReport BPL(大约1400kb)而所有你的应用程序只需要保留很少的一部分。 你可以发现FastReport有一个非常吸引人的用户界面,使用最新的用户界面组件,例如可停靠的工具栏。你的最终用户将会非常愿意使用这个设计器,只需使用鼠标就可以创建大多数报表FastReport是名副其实的快速报表:较其它一些Delphi报表设计器而言,你可以发现没有什么可以接近于它的开发速度。报表预览窗口一直是大多数报表设计器的弱点,高品质的外观,赋于你的应用程序非常专业的用户界面。 FastReport是一个已经拥有三年历史的非常成熟的报表设计器,成长使它拥有其它Delphi报表设计器所不能相比的诸多先进特性。
这个指南让你熟悉报表设计器和了解报表设计的基本概念(各种区域,数据源,二次表,等等)。指南将帮助你开始用FastReport创建报表,但它不能告诉你怎样使用其它基本的报表设计器。 如果你不熟悉报表设计器,我们建议你参考QuickReport的帮助系统。QuickReport的指南已经包含在你的Delphi拷贝中。QuickReport的大部分基本概念也适用于FastReport,然而,FastReport能提供更多的弹性和最终用户的自定义。 关于FastReport的说明 FastReport是高弹性的报表设计器,用于报表的数据可以从任何类型的数据源获取,包含字符列表,BDE数据库,ADO数据源(不使用BDE),Interbase(使用IBO),Pascal数组和记录,以及一些不常用的数据源。 整个FastReport系统是用Delphi的Pascal编写的。FastReport不需要动态链接库,但需在你的项目中占用大约400kb(Delphi 5)。如果你想最终用户拥有设计能力,这将在你的.EXE中增加大约500kb。虽然这看上去比较大,但这只是其它设计的几分之一。你同样应该考虑到FastReport不仅仅只是包含最终用户更改报表设计的能力,还能够适应查询和数据库的变化。FastReport还包含自己的脚本语言,让应用程序和最终用户能够更容易地改变报表。如果你的大部分应用使用FastReport,你可以简单地配置FastReport BPL(大约1400kb)而所有你的应用程序只需要保留很少的一部分。 你可以发现FastReport有一个非常吸引人的用户界面,使用最新的用户界面组件,例如可停靠的工具栏。你的最终用户将会非常愿意使用这个设计器,只需使用鼠标就可以创建大多数报表FastReport是名副其实的快速报表:较其它一些Delphi报表设计器而言,你可以发现没有什么可以接近于它的开发速度。报表预览窗口一直是大多数报表设计器的弱点,高品质的外观,赋于你的应用程序非常专业的用户界面。 FastReport是一个已经拥有三年历史的非常成熟的报表设计器,成长使它拥有其它Delphi报表设计器所不能相比的诸多先进特性。
第一章 设计 7 1.1、控制键 8 1.2、鼠标操作 9 1.3、工具栏 10 1.3.1、设计模式工具栏 10 1.3.2、“标准”工具栏 10 1.3.3、“文本”工具栏 11 1.3.4、“边框”工具栏 12 1.3.5、“对齐”工具栏 12 1.4、设计选项 13 1.5、报表设置 15 1.6 页面设置 16 第二章 创建报表 19 2.1、报表对象 20 2.2、“世界,你好!”报表示例 20 2.3、“Text”组件 21 2.4、在“Text”组件中使用HTML标记 23 2.5、通过“Text”组件显示公式 24 2.6、FastReport中的Bands 25 2.7、Data Band 26 2.8、TfrxDBDataSet组件 27 2.9、“客户列表”报表 27 2.10、通过text组件显示数据表字段 29 2.11别名 30 2.12、变量。 30 2.13、“Picture”控件 32 2.14、图形报表 33 2.15、多行文本显示 34 2.16、文本拆分 36 2.17、组件的Wrap 37 2.18、显示数据表中的数据 38 2.19、标签式打印 40 2.20、子bands 42 2.21、组件的移动 43 2.22、两个数据阶的报表(主—细) 43 2.23、页首和页尾数据Band 46 2.24、多页报表 47 第三章 分组集合体 50 3.1、分组打印 51 3.2、其他分组特性 53 3.3页码的重设 55 3.4、组的操作 55 3.5、行数 56 3.6、函数集 57 3.7、页和报表的统计 60 3.8、插入汇总函数 61 第四章 格式化、加强 62 4.1、格式化输出结果 63 4.2、内嵌格式化 63 4.3、条件显示 65 4.4、分行显示数据行的颜色 66 第五章 嵌套报表 68 5.1嵌套报表 69 5.2、设计子报表 69 5.3、子报表中的限制 69 5.4、PrintOnParent选项 70 第六章 脚本 72 6.1、体验脚本语言 73 6.2、脚本结构 76 6.3、“世界你好!”脚本 78 6.4、脚本中组件对象的使用 79 6.5、调用报表变量列表中的变量。 79 6.6、调用数据表子段 80 6.7、脚本中调用汇总函数 80 6.8、报表中变量值的显示 80 6.9、事件 81 6.10、一个使用“OnBeforePrint”事件的例子 82 6.11、在组头打印组的汇总信息 84 6.12、“OnAfterData”事件 88 6.13、Service组件 89 6.13.1、Report组件 89 6.13.2、Engine组件 90 6.13.3、“OutLine”组件 91 6.13.4、引擎组件的使用 91 6.15 Anchors 94 6.16 “Outline” 组件的使用方法 96 6.17 “OnManualBuild” 页面事件 98 6.18 脚本中的组件的建立 104 第七章 交叉报表 106 7.1、创建交叉报表 108 7.2、改变显示 110 7.3、使用函数 112 7.4、对结果进行排序 112 7.5、组合标题的表格 113 7.6、调整单元格的宽度 115 7.7、字体颜色和突出显示 117 7.8、使用脚本语言管理组件 118 7.9、调整行和列的大小 123 7.10、手动填充表格单元 124 7.11、在表格单元中加入其他组件 126 7.12、一些有用的设置 129 第八章 制图表 132 8.1、chart数据中数字的限制 137 8.2、设置 137 8.3、指定数字制表 138 8.4、利用脚本进行制表 139 8.5、在delphi环境中创建的报表的打印 139 第九章 点阵报表 140 9.1、点阵报表使用交叉报表 144 9.2、点阵报表的打印 145 9.3、命令组件 146 第十章 对话框窗体 147 10.1、控件 148 10.2、“世界你好!”报表 149 10.3、输入参数,并传递到报表中 150 10.4、组件的交互 150 10.5、多个对话框表单 151 10.6、对话框窗体的管理 152 第十一章 数据访问组件 155 11.1、组件的描述 156 11.1.1、TfrxDBLookupCombobox组件 157 11.1.2、TfrxADOTable组件 157 11.1.3、TfrxAdoQuery组件 159 11.1.4、TfrxADODatabase组件 161 11.2、创建报表 161 11.3、简单的列表式报表 162 11.4、参数查询报表 163 11.5、其他可用配置 164 第十二章 报表的继承性 166 12.1、创建报表 167 12.2、修改基础模板 169 12.3、组件的继承 170 第十三章 报表向导 171 13.1、新报表向导 172 13.2、数据连接向导 175 13.3、新table向导 176 13.4、新query向导 177 13.5、查询语句生成 177 第十四章 报表的预览 打印 导出 180 14.1、控制键 182 14.2、鼠标控制 182 14.3、报表的打印 183 14.4、报表中的文字搜索 183 14.5、报表的导出 184
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值