SQL Story摘录(九)————不等联接

 
不等联接

通常来说,SQL语言进行的都是无序操作。想要进行有序的处理,比如比较一个序列的前后项,必须要使用游标。但是,在有些场合下,可采用另一种方法,不用游标,一样能处理有序的信息,这就是不等联接。先看下面一个例子

前一阵, CSDN网友BuildIt来信,和我讨论了这样的问题:以下表HISTORY

CREATE TABLE [HISTORY] (

[TheDate] [datetime] NULL ,

[Quantity] [int] NULL

) ON [PRIMARY]

中存储的是一系列的历史数据,例如:

INSERT HISTORY VALUES('2002-01-01 00:00:00.0',11)

GO

INSERT HISTORY VALUES('2002-01-02 00:00:00.0',34)

GO

INSERT HISTORY VALUES('2002-01-03 00:00:00.0',27)

GO

INSERT HISTORY VALUES('2002-01-04 00:00:00.0',43)

GO

现在,我们要查询自起始日期至每一个日期的总量。也就是说,显示这样一个结果集:

TheDate Quantity q_sum

2002-01-01 00:00:00.0 11 11

2002-01-02 00:00:00.0 34 45

2002-01-03 00:00:00.0 27 72

2002-01-04 00:00:00.0 43 115

直观上来讲,我们可以在SELECT * FROM HISTORY ORDER BY TheDate上建一个游标,从第一条开始,每一条,加一次。那换个想法呢?如果我们建立这样一个结果集,让每一个日期限,都对应它当天的数量及所有在它之前的数量记录。那么我们就可以按这个日期分组,对数量进行求和。很显然,一个不等查询就这样形成了。我最初的写法有错误,以下是经 BuildIt 修改后最终的语句

select l.TheDate,

l.quantity,

sum(r.quantity) as q_sum

from HISTORY l

join HISTORY r

on l.TheDate >= r.TheDate

group by l.TheDate, l.quantity

order by l.TheDate

不等联接本身就不是一一对应,它的对应关系和顺序有着密切的关系。这也就是我们能够拿它来进行有序操作的原因。再给一个很自然的例子:

SELECT L.I, SUM(R.I)

FROM N L

JOIN N R

ON L.I >=R.I

GROUP BY L.I

表N只有一个整型列I,保存自然数列。所以,没什么神秘,这就是求自然数列的和。这里SUM(R.I)表示自然数列N从零至I的累加和,比前面的那个问题还要简单。不过显然这不是不等联接发挥威力的地方,因为它会形成一个巨大的三角形数据集,就像下面这样

1 1

2 1

2 2

3 1

3 2

3 3

...

当我对十六位整型的列表执行这个查询时,我的AthlonXP1700+/256MDDR的机器足足运行了近三十分钟,当我写下现在这段文字时,它返回了一个数据溢出错误。显然,对于这个查询,即使是十六位整数的列表,也太大了。我的建议是,仅当结果集无法用公式表达时,使用不等联接。像这个累加,我们早已有了成熟的公式,何必再让计算机傻算呢?用下面这个语句

SELECT I, ((1+I)*I)/2

FROM N

相比老老实实地累加,速度奇快。发现数据溢出时,连一秒钟都不到,可这台计算机就是想不到用这个方法,唉……

传说数学界一代宗师高斯小学的时候,他老师考过他这个问题。所以几乎所有的中国小学生,都被老师用这道题折磨过。好像老师们的目的就在于告诉我们,我们的智商比不上高斯。可我压根就没想和人家比啊……

上大学时,教我们第一本《数学分析》的范先令老师说计算机是傻子,我当时只是觉得好玩而已,今天算是见识了,看来在归纳总结的能力方面,计算机也就是我小学时的水平,永远也赶不上高斯上小学那会儿了。

不过,这种东西用于公式难以表达的地方,还是有意义的。比如我的一个朋友用不等联接写过一个素数筛子,很有趣。它虽说不会比我们用过程化的代码写出来的程序效率更高,但却能把筛法根本的精要表达的清清楚楚,也许以后我们研究数论,会用的上这种SQL风格的表示法呢。这位朋友教了我很多计算机方面的知识,出于对他的尊重,我不会抄录他的代码。不过这个语句本身并不复杂,相信朋友们想到用联接查询后,都一定写得出来,大家有兴趣的话,自己不妨试试。用它还可以实现其它的一些数列,以后我们再讨论几个。

不等联接还有一个用法,可以用它生成一个序号列,比如

SELECT COUNT(L.AFIELD) AS ID,

L.AFIELD

FROM MYTABLE L

JION MYTABLE R

ON L.AFIELD > R.AFIELD

GROUP BY L.AFIELD

AFIELD字段可以是字符串、日期,当然也可以是数值,反正可排序就行。这东西有点奇技淫巧的味道,数据量太大,就不好玩了,一般还是用物理行号的好,虽说不是SQL标准,但实用啊。这个例子我在MCDBA的复习题中见过(据说这道题考过),不过我的那位朋友自己就做出来过,大家可能也有独立实现过这一方法的吧。

不等查询的有序操作能力,显然来自联接字段的可排序和互异性,所以,最好不要在有重复值的字段上做不等联接(事实上,最好不要在有重复值的字段上做任何联接,除非你非常肯定你在干什么)。等值联接出现数据爆炸就够可怕的了,不等联接要是玩爆了……嘿嘿嘿……

想像一个等值联接中有一对重复值,可能出现两对重复结果。不过要是不等联接,就和重复的位置有关了。因为这是一个三角形,所以出现在最上面还好,要是出现在这个三角形的下部……

不等联接查询,显然是一个有力的工具,但它也是招来麻烦的快捷方式之一。有几个建议,是我的经验:
如果联接会生成很大的“三角形”,就不要用,试试子查询或哪怕游标;

生成的结果集相对于原表越小越好,尽可能地把无用的数据先过滤掉;

用不等联接进行数列计算会表达的很清楚(因为是非过程化的),但通常在效率上它没有什么优势,所以,平时玩玩可以,真用的话最好先考虑好;

还有就是不等联接不要轻易用在多重联接中,否则可能会引起杠杆作用。

祝大家在这个神奇的世界中旅行愉快!

时间: 2024-08-03 08:44:33

SQL Story摘录(九)————不等联接的相关文章

SQL Story摘录(十)————游标的应该与不应该

游标  游标概观 相信很多Delphi程序员都写过这样的代码: ... begin MyDataSet.Open; MyDataSet.Frist; while not ( MyDataSet.BOF or MyDataSet.EOF) do begin ... end; MyDataSet.Close; end; ... 很久以来,我们习惯了用这样的代码对数据库返回的数据进行逐行操作.在用客户端程序的代码打开数据集之前,我们把它当做是一个无序集合.不过,在需要时,我们在服务器端就可以直接以行操

SQL Story摘录(二)————联接查询初探

例1-2.键值重复的信息现在看一下压缩掉重复信息的PRODUCT表ID PNAME PRICE NUMBER PDESCRIPTION 1Apple 123000NULL2Banana 16.997600NULL3Olive 25.224500NULL4Coco Nut 40.992000NULL4Orange 15.995500NULL5Pineapple 302500NULL6Olive 25.223000NULL 这里还有几个有问题的地方.表中Coco Nut和Orange的ID都是4,I

SQL连接查询1 内联接查询

在数据库开发方面,通过单表所表现的实现,有时候需要组合查询来找到我们需要的记录集,这时候我们就会用到连接查询. 连接查询主要包括以下几个方面: 内连接 内连接一般是我们最常使用的,也叫自然连接,是用比较运算符比较要联接列的值的联接.它是通过(INNER JOIN或者JOIN)关键字把多表进行连接.我们通过建立两个表来说明问题: StudentID StudentName StudentAge----------- -----------------------------------------

SQL Story摘录(四)————信息挖掘初步

 写这本书的最初想法,来自于在第一个公司工作时,与同事的交流和学习.不过发布这本书的导火索,却在于一次在CSDN上读到一篇关于最新信息的报表问题的贴子.贴子中的问题可以用子查询和联接两种方式完成.由于条件所限,我不能详细解答,由此发贴的朋友不能理解我的本意,让我心生遗憾.所以决定将写书的想法付诸行动,并将这本书贴在CSDN上,与大家一起交流,共同进步.今天正好又见到类似于当日的问题,心生感触,决定在这里把它详细讨论一下.在实际工作中,我们有时会需要建立数据表来存储变动的数据,并由这些数据统计出我

SQL Story摘录(六)————不可能的错误

错误  初学SQL的日子,感觉就像是刚学走路,步态可掬,跌跌撞撞.摔了不少可笑的跟头.拿出来大家娱乐一下,也互相提个醒,这样的错我们可以尽量避免的嘛.先看这个: 例1 不合理的逗号: Select Field1, Field2, Field3, From MyTable 一执行就是个语法错误,什么意思嘛,这可是从书上抄的哎,你不能这么对我--呵呵呵,其实嘛,错误在于我在最后一个字段名后面加了一个逗号.逗号是分隔字段名或表名的嘛,字段名和Form之间加个逗号算什么事?不要小看它,即使老手,也常出这

SQL Story摘录(七)————触摸NULL值

前面的文章中,我们初步见识了NULL这个不可思议的小东西.今天,我尽可能详细的介绍一下它.依照惯例,这是一次尽量浅显但并不严谨的讨论,甚至可能内容也不那么严肃.我的目的在于帮助读者更轻松地工作,并且有兴趣对数据库进一步的学习.从另一方面讲,我相信自己论点中的错误,肯定会有其他人也在犯,所以请发现不妥的朋友一定要公开指出.这样,才有助于我和我的读者朋友们进步.衷心感谢每一个提出批评和指正的朋友,特别是公开提出批评和指正的朋友们.特别感谢sunshine19,专程发来E-mail,指出了<SQL S

SQL Story摘录(八)————数据抽取

数据 数据抽取 理论上的关系型数据库,数据是以关系的形式存在.通常我们都可以把它们视为一种集合.这样,数据一般是以无序的形式存在的.这种做法的好处自不用多加讨论了,不过我们也得承认,有时这样也会带给我们一些麻烦.我最近就遇到这么一件. 我在网上遇上我的一个老同学,他提出了这样一个问题.有一个表(假设就叫myTable),表中有三个整型字段(假设就叫A11,A12,A13),其上有一个唯一键约束.现在他想要在这个表的数据中取一些样品.他希望从中A13的各个取值中,各取一条记录.然后我问他,对A11

SQL Story摘录(一)————简单查询初探

在CSDN上回贴时,我总是苦口婆心地劝告楼上楼下的朋友们多用联接.可响应甚微.往往一个简单的功能,也一定要写成子查询或游标,弄得非常复杂冗长.的确,这样写对于初学者来说,费力不费脑,思路比较好理解.所以往往得分的也是这些回贴.可事实上,如果你真正熟悉了SQL的编程风格,你会明白,联接查询才是最直接.最清晰.最有力的方法,而更好的办法就是无招胜有招,一条简单查询结束战斗.下面我举几个例子来证明一下这个观点.例1-1.重复记录的查询和处理总有一些朋友在网上问,一个表中,有重复的记录,怎么办?当然,一

SQL Story摘录(三)————可扩展设计

设计 面向集合的结构化设计.这一点很多人都知道,可真正能够活用的就太少了.举一个简单的例子:例1-3:有一个简单的数据表Orders,存储某商店的订单信息:CREATE TABLE [dbo].[ORDERS] ([ID] [int] IDENTITY (1, 1) NOT NULL ,[CustomerID] [int] NOT NULL ,[OrderDate] [datetime] NOT NULL ) ON [PRIMARY]GOCREATE CLUSTERED INDEX [CU_IN