国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > 綜合 > 正文

SQL Story摘錄(三)————可擴展設計

2024-07-21 02:09:03
字體:
來源:轉載
供稿:網友



面向集合的結構化設計。這一點很多人都知道,可真正能夠活用的就太少了。舉一個簡單的例子:
例1-3:有一個簡單的數據表orders,存儲某商店的訂單信息:
create table [dbo].[orders] (
[id] [int] identity (1, 1) not null ,
[customerid] [int] not null ,
[orderdate] [datetime] not null
) on [primary]
go
create clustered index [cu_inx_orderdate] on [dbo].[orders]([orderdate]) with fillfactor = 50 on [primary]
go
alter table [dbo].[orders] with nocheck add
constraint [pk_orders] primary key nonclustered([id])
on [primary]
go
表中現在有以下數據:
id customerid orderdate
----------- ----------- ------------------------------------------------------
1 1 1999-1-4
2 10 1999-3-5
3 22 1999-5-2
4 2 1999-6-7
5 2 2000-3-6
7 101 2001-5-3
8 10 2001-6-5
6 101 2002-4-2
那么,我們如何生成一個1999-2002的年度訂單數報表呢(四年只有8個訂單?我為了演示方便才這樣做的,這并不代表真實的情況:p)?現在,我給出實際報表的數據格式,讀者們請先試一下這個語句的寫法
customerid 1999 2000 2001 2002
-------------- ------ ------ ------ ------
1 1 0 0 0
2 1 1 0 0
10 1 0 1 0
22 1 0 0 0
101 0 0 1 1
最直觀的想法,是在前臺,用其它語言實現這一功能。不過有一個辦法,可以用sql語言來實現它。而且不一定比你想像的更復雜:
select customerid,
sum(case when year(isnull(orderdate, 0)) = 1999 then 1 else 0 end) as "1999",
sum(case when year(isnull(orderdate, 0)) = 2000 then 1 else 0 end) as "2000",
sum(case when year(isnull(orderdate, 0)) = 2001 then 1 else 0 end) as "2001",
sum(case when year(isnull(orderdate, 0)) = 2002 then 1 else 0 end) as "2002"
from orders
group by customerid
我想這時會有朋友提出interbase不支持case的問題。不過即使如此,我還是要向大家推薦這種寫法。因為它優美、簡潔,不僅我們讀著好懂,還可以很方便地寫出程序來自動生成它。事實上,case關鍵字已是sql標準之一,大勢所趨,會有越來越多的數據庫系統支持它的。
那么它又是怎么來的呢?我在設計這個語句時是這樣的思路:
1、我們需要一個同時在時間和客戶兩個坐標軸上展開的報表;
2、縱向上,我們要為每一位客戶建立一行數據,這個比較好辦,我們首先確定了這個語句會有一個基本框架
select customerid,
………………
from orders
group by customerid
如果不區分年度,已下語句就是我們要的結果
select customerid,
count(id) as orders_count,
from orders
group by customerid
3、設所有訂單為一全集,那么這個集合的總數用以下語句來統計:
select count(id) from orders
橫向上,我們為每一年度的訂單數定義一列,以1999年為例,取年份為1999年的訂單子集的元素數為
select sum(case when year(isnull(orderdate, 0)) = 1999 then 1 else 0 end) as "1999"
from orders
其它年份依此類推,我們得到每一年的訂單數:
select sum(case when year(isnull(orderdate, 0)) = 1999 then 1 else 0 end) as "1999",
sum(case when year(isnull(orderdate, 0)) = 2000 then 1 else 0 end) as "2000",
sum(case when year(isnull(orderdate, 0)) = 2001 then 1 else 0 end) as "2001",
sum(case when year(isnull(orderdate, 0)) = 2002 then 1 else 0 end) as "2002"
from orders
其返回結果如下:
1999 2000 2001 2002
----------- ----------- ----------- -----------
4 1 2 1

 

(所影響的行數為 1 行)
4、顧及到關系型數據庫“詭異”的null值問題后,綜合2、3步,我們得出最終的語句:
select customerid,
sum(case when year(isnull(orderdate, 0)) = 1999 then 1 else 0 end) as "1999",
sum(case when year(isnull(orderdate, 0)) = 2000 then 1 else 0 end) as "2000",
sum(case when year(isnull(orderdate, 0)) = 2001 then 1 else 0 end) as "2001",
sum(case when year(isnull(orderdate, 0)) = 2002 then 1 else 0 end) as "2002"
from orders
group by customerid
現在這個報表結構清晰明白。擴展性極強。比如明年我們需要2003年的統計數據,只要再依葫蘆畫瓢,來一列
sum(case when year(isnull(orderdate, 0)) = 2003 then 1 else 0 end) as "2003"
加在最后就可以了,它是全集中的2003年數據的子集。還有,用來判斷空值的isnull函數不一定所有的數據庫都有,沒關系,只要在case的分支里加一行
when orderdate is null then 0
就可以了。基于這個思想,我們可以很容易地寫出一個存儲過程,只要給定起訖年份,就可以生成一個完整的年度報表。由于所有的運算都在服務器端運行,并且是隨著數據檢索一次就完成了。它的速度快于客戶端的報表。而且傳輸的數據量也少,可以有效減輕網絡負載。
在《sql server6.5技術內幕》中,有一個類似的例子。不過作者使用的語句結構比我的復雜,他的例子中,from關鍵字是從一個子查詢導出表中選擇的數據,這讓我百思不得其解。也許6.5版的ms sql server還不支持我的寫法,也許那樣寫性能更好。作者并沒有說明,我也一直沒有機會接觸到ms sql server6.5。
對于interbase,我還沒有辦法用足夠優雅的語句生成這個報表。這主要是由于interbase不支持case。不過,如果你對語句的性能和美感要求不高的話,下面這個語句可以實現與以上的sql server版本相同的功能:
select o.customerid,
(select count(i.id)
from orders i
where (i.customerid = o.customerid)
and (extract(year from i.orderdate) = 1999))
as count_1999,
(select count(i.id)
from orders i
where (i.customerid = o.customerid)
and (extract(year from i.orderdate) = 2000))
as count_2000,
(select count(i.id)
from orders i
where (i.customerid = o.customerid)
and (extract(year from i.orderdate) = 2001))
as count_2001,
(select count(i.id)
from orders i
where (i.customerid = o.customerid)
and (extract(year from i.orderdate) = 2002))
as count_2002
from orders o
group by o.customerid
依照sql server版本,我們完成了interbase版的年度報表。不同的是由于使用了相關子查詢統計數據,它的效率會差一些(好在你不需要即時更新你的年度報表吧)。不過由于它同樣是基于面向集合的設計構架,至少我們保證了它的可擴展性。只是很明顯的,當子查詢版本中增加一列年度統計,所帶來的開銷增長會比case版本多很多。如果你對速度要求較高,還是在客戶端另寫程序生成吧。
interbase數據庫的用戶會在這個示例中遇到很多不滿意的地方:不支持自動標識列、沒有聚簇索引、沒有case、沒有……更可恨的是,這個數據系統的開放源碼版本沒有附帶odbc或ado驅動,在得到一個免費的數據庫系統后,我們卻要為它花幾十美元去買一套odbc驅動?
不過,interbase正在得到開放源碼社區的支持,borland公司也通過dbexpress和interclient技術來為interbase提供開放的接口(目前dbexpress驅動基本上也只存在于borland昂貴的企業版開發工具中:()。只要每一個interbase的程序員和用戶都為這個屬于我們自己的軟件做出貢獻,它的前途還很光明。

面向集合的設計方法雖然只適用于特定的目標,并不是通用的軟件設計方法。但也不是三言兩語能說清的,以后的章節中,我們會一直實際這種設計方法,還會有專門的章節討論這個問題。那時,我們的示例數據庫也建設的比較完整了,我也許會給出更實用的年度訂單統計報表。現在,我們先簡單地總結一下:
1、定義我們要生成的結果集的結構;
2、找出結果集的數據來源,定義全集;
3、定義結果集的取值范圍,定義所取的子集;
4、完成操作。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 建阳市| 赤水市| 桂林市| 航空| 涞水县| 温州市| 陵水| 会昌县| 开远市| 台湾省| 大庆市| 龙海市| 株洲县| 宁陕县| 辰溪县| 邳州市| 承德县| 石泉县| 北票市| 奎屯市| 西峡县| 得荣县| 宁武县| 西乡县| 唐海县| 隆德县| 南充市| 沁水县| 澄城县| 建平县| 广南县| 彰化市| 吴桥县| 安塞县| 安丘市| 铜川市| 夏河县| 镶黄旗| 定南县| 如东县| 阿勒泰市|