1. 程式人生 > >由簡到難生成資料庫報表(一)

由簡到難生成資料庫報表(一)

                在接下來,我想借助一個例子,來加深大家對資料庫報表的認識,由簡到難生成資料庫報表!

這裡建立一個簡化的進銷系統,系統中只有銷售單和採購單,不存在紅衝單據及其庫存、退貨等單據。由於銷售單和採購單存在主從結構,所以將這兩張表中的主從資料分別儲存在不同的表中。下面是這個系統中表之間的關係圖:


表 T_Person 為人員表,FId欄位為主鍵,FNumber 欄位為人員工號,FName 欄位為人

員姓名,FManagerId欄位為上級主管主鍵(指向T_Person表的 FId欄位的外來鍵) 。

表T_Merchandise為商品表,FId欄位為主鍵,FNumber欄位為產品編號,FName欄位

為商品名,FPrice為商品價格;

表T_SaleBill為銷售單主表,FNumber欄位為銷售單編號,

FBillMakerId欄位為開單人主鍵(指向T_Person表的 FId欄位的外來鍵) ,FMakeDate 欄位為

制單日期,FConfirmDate欄位為確認日期;

表T_SaleBillDetail為銷售單明細記錄,FId欄位

為主鍵,FBillId欄位為主表主鍵(指向 T_SaleBill 表的 FId 欄位的外來鍵) ,FMerchandiseI

欄位為商品主鍵(指向T_Merchandise表的FId欄位的外來鍵) ,FCount欄位為銷售數量。

表T_PurchaseBill為採購單主表,FNumber欄位為採購單編號,FBillMakerId欄位為開

單人主鍵(指向T_Person表的FId欄位的外來鍵), FMakeDate欄位為制單日期, FConfirmDat

欄位為確認日期;

表T_PurchaseBillDetail為採購單明細記錄,FId欄位為主鍵,FBillId欄位

為主表主鍵(指向T_PurchaseBill表的FId欄位的外來鍵) , FMerchandiseId欄位為商品主鍵(指

向T_Merchandise表的FId欄位的外來鍵),FCount欄位為採購數量。

 下面是建立表的SQL語句以及插入資料的SQL語句:

--建立T_Person表
create table T_Person
(
	FId varchar(20) not null,
	FNumber varchar(20),
	FName varchar(20),
	FManagerId varchar(20),
	primary key(FId),
	foreign key(FManagerId) references T_Person(Fid)
)
--建立T_Merchandise表
create table T_Merchandise
(
	FId varchar(20) not null,
	FNumber varchar(20),
	FName varchar(20),
	FPrice int,
	primary key(fid)
)
--建立T_SaleBill表
create table T_SaleBill
(
	FId varchar(20) not null,
	FNumber varchar(20),
	FBillMakerId varchar(20),
	FMakeDate datetime,
	FConfirmDate datetime,
	primary key(fid),
	foreign key(Fbillmakerid) references T_Person(fid)
)
--建立T_SaleBillDetail表
create table T_SaleBillDetail
(
	FId varchar(20),
	FBillId varchar(20),
	FMerchandiseId varchar(20),
	FCount int,
	primary key(fid),
	foreign key(Fbillid) references T_SaleBill(fid),
	foreign key(Fmerchandiseid) references T_Merchandise(fid)
)
--建立T_PurchaseBill表
create table T_PurchaseBill
(
	Fid varchar(20) not null,
	FNumber varchar(20),
	FBillMakerId varchar(20),
	FMakeDate datetime,
	FConfirmDate datetime,
	primary key(fid),
	foreign key(FBillMakerId) references T_Person(fid)
)
--建立T_PurchaseBillDetail表
CREATE  TABLE  T_PurchaseBillDetail
(
	FId  VARCHAR(20)  NOT  NULL  ,
	FBillId VARCHAR(20),
	FMerchandiseId  VARCHAR(20),
	FCount  INT,PRIMARY KEY  (FId),
	FOREIGN KEY  (FBillId)  REFERENCES  T_PurchaseBill(FId),
	FOREIGN  KEY  (FMerchandiseId) REFERENCES T_Merchandise(FId)
) 
--首先向T_Person、T_Merchandise兩張表中插入演示資料: 
insert into T_Person(FId,FNumber,FName,FManagerId) 
values('00001','1','Robert',NULL)
insert into T_Person(FId,FNumber,FName,FManagerId) 
values('00002','2','John','00001')
insert into T_Person(FId,FNumber,FName,FManagerId) 
values('00003','3','Tom','00001')
insert into T_Person(FId,FNumber,FName,FManagerId) 
values('00004','4','Jim','00003')
insert into T_Person(FId,FNumber,FName,FManagerId) 
values('00005','5','Lily','00002')
insert into T_Person(FId,FNumber,FName,FManagerId) 
values('00006','6','Merry','00003')
insert into T_Merchandise(FId,FNumber,FName,FPrice) 
values('00001','1','Bacon',30)
insert into T_Merchandise(FId,FNumber,FName,FPrice) 
values('00002','2','Cake',2)
insert into T_Merchandise(FId,FNumber,FName,FPrice) 
values('00003','3','Apple',6

--  還要向T_SaleBill和T_PurchaseBill表中插入演示資料: 
insert into T_SaleBill(FId,FNumber,FBillMakerId,FMakeDate,FConfirmDate) 
values('00001','1','00006','2007-03-15','2007-05-15')
insert into T_SaleBill(FId,FNumber,FBillMakerId,FMakeDate,FConfirmDate) 
values('00002','2',null,'2006-01-25','2006-02-03')
insert into T_SaleBill(FId,FNumber,FBillMakerId,FMakeDate,FConfirmDate) 
values('00003','3','00001','2006-02-12','2007-01-11')
insert into T_SaleBill(FId,FNumber,FBillMakerId,FMakeDate,FConfirmDate) 
values('00004','4','00003','2008-05-25','2008-06-15')
insert into T_SaleBill(FId,FNumber,FBillMakerId,FMakeDate,FConfirmDate) 
values('00005','5','00005','2008-03-17','2007-04-15')
insert into T_SaleBill(FId,FNumber,FBillMakerId,FMakeDate,FConfirmDate) 
values('00006','6','00002','2002-02-03','2007-11-11')
insert into T_PurchaseBill(FId,FNumber,FBillMakerId,FMakeDate,FConfirmDate) 
values('00001','1','00006','2007-02-15','2007-02-15')
insert into T_PurchaseBill(FId,FNumber,FBillMakerId,FMakeDate,FConfirmDate) 
values('00002','2','00004','2003-02-25','2006-03-03')
insert into T_PurchaseBill(FId,FNumber,FBillMakerId,FMakeDate,FConfirmDate) 
values('00003','3','00001','2007-02-12','2007-07-12')
insert into T_PurchaseBill(FId,FNumber,FBillMakerId,FMakeDate,FConfirmDate) 
values('00004','4','00002','2007-05-25','2007-06-15')
insert into T_PurchaseBill(FId,FNumber,FBillMakerId,FMakeDate,FConfirmDate) 
values('00005','5','00002','2007-03-17','2007-04-15')
insert into T_PurchaseBill(FId,FNumber,FBillMakerId,FMakeDate,FConfirmDate) 
values('00006','6',null,'2006-02-03','2006-11-20')

--  向T_SaleBillDetail表和T_PurchaseBillDetail表中插入演示資料: 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00001','00001','00003',20) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00002','00001','00001',30) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00003','00001','00002',22) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00004','00002','00003',12) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00005','00002','00002',11) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00006','00003','00001',60) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00007','00003','00002',2) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00008','00003','00003',5) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00009','00004','00001',16) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00010','00004','00002',8) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00011','00004','00003',9) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00012','00005','00001',6) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00013','00005','00003',26) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00014','00006','00001',66) 
insert into T_SaleBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00015','00006','00002',518) 
 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00001','00001','00002',12) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00002','00001','00001',20) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00003','00002','00001',32) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00004','00002','00003',18) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00005','00002','00002',88) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00006','00003','00003',19) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00007','00003','00002',6) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00008','00003','00001',2) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00009','00004','00001',20) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00010','00004','00003',18) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00011','00005','00002',19) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00012','00005','00001',26) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00013','00006','00003',3) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00014','00006','00001',22) 
insert into T_PurchaseBillDetail(FId,FBillId,FMerchandiseId,FCount) 
values('00015','00006','00002',168) 

顯示制單人詳細資訊

要求顯示每張銷售單的編號、制單人、制單日期等資訊,可以使用簡單的 SELECT 語
句來完成這個任務: 
SELECT FNumber, FBillMakerId, FMakeDate 
FROM T_SaleBill 

這裡的 FBillMakerId 顯示的是制單人在 T_Person 表中的主鍵,業務人員很難將這個編
號與人名對應起來,因此必須將其轉換為制單人的姓名。FBillMakerId 欄位儲存的是
T_Person表的主鍵,而T_Person表的FName欄位則為人員的名稱,因此將這兩個表做連線
查詢即可,SQL語句如下: 
SELECT salebill.FNumber,person.FName,salebill.FMakeDate 
FROM T_SaleBill salebill 
INNER JOIN T_Person person 
ON salebill.FBillMakerId=person.FId; 

  這個查詢結果已經能夠顯示開票人的姓名, 不過仔細觀察會發現編號為 2的記錄並沒有
顯示在執行結果中,這是因為這條記錄的 FBillMakerId 欄位為空值,所以不能與 T_Person
表中的任何記錄進行匹配,而內連線不會顯示沒有匹配的行。一般情況下即使沒有開單人也
要將這張單據顯示出來,這時就要使用外部連線了,如下: 

  因為 T_SaleBill表中的記錄必須全部顯示到結果集中,而 T_SaleBill為左表,所以使用
左外部連線。執行完畢我們就能在輸出結果中看到上面的執行結果: 
  這樣沒有開票人的單據也顯示出來了,不過這裡其對應的開票人處顯示的是 NULL,這
讓業務人員感到難以理解,我們使用 COALESCE()函式來解決這個問題。前面章節講到
COALESCE()函式支援多個引數, 該函式返回引數中的第一個非空值, 這樣 COALESCE(f1,f2)
就可以實現“如果 f1為空則將 f2做為返回值”這樣的空值處理邏輯了。將SQL語句做如下改
造: 
 
select saleBill.fid,coalesce(T_person.fname,'沒有開單人'),FMakeDate from t_SaleBill SaleBill
left outer join T_person
on T_Person.FId=saleBill.FBillMakerId

顯示銷售單的資訊

  要求列出所有銷售單的詳細資訊,每行顯示銷售單的每一條銷售記錄,同時每行頭部要
顯示此行所屬的銷售單的資訊,比如單號、開單人、開單日期等。T_SaleBillDetail表儲存的
是銷售單的每一條銷售記錄,T_SaleBill表儲存的是銷售單的頭資訊,T_SaleBillDetail表的
FMerchandiseId欄位儲存的是銷售的商品主鍵,而 T_SaleBill表的 FBillMakerId欄位儲存的
是開單人的主鍵,只要對這四張表做連線查詢即可。由於 T_SaleBill表的 FBillMakerId欄位
有可能為空,所以在 T_SaleBill 表和 T_Person 表進行連線的時候要使用左外連線,而為了
提高查詢效率其他連線都使用內連線。SQL語句如下: 
select T_SaleBill.FNumber,coalesce(T_Person.FName,'沒有開單人') 開單人,T_SaleBill.FMakeDate,T_Merchandise.Fname,T_Merchandise.FPrice
from T_Salebill
left outer join T_Person
on T_Person.FId=T_SaleBill.FBillMakerId
inner join T_SaleBillDetail
on T_SaleBillDetail.FBillId=T_SaleBill.Fid
inner join T_Merchandise
on T_Merchandise.Fid=T_SaleBillDetail.FMerchandiseId
order by T_SaleBill.FMakeDate desc


計算收益

要求計算每種商品的總收益, 受收益的定義為所有的銷售單中該商品的銷售總額減去所
有的採購單中該商品的購買總額。 
  T_SaleBillDetail 表中儲存的所有的銷售單詳細記錄,因此下面的 SQL 語句可以檢索
所有產品的銷售記錄,包括產品名和銷售額: 
select T_Merchandise.FName,T_Merchandise.FPrice*T_SaleBillDetail.Fcount 銷售額
from T_Merchandise
inner join T_SaleBillDetail
on T_Merchandise.Fid=T_SaleBillDetail.FMerchandiseId


同理下面的SQL語句則可以檢索所有產品的購買記錄,包括產品名和採購額:
select T_Merchandise.FName,T_PurchaseBillDetail.Fcount*T_Merchandise.FPrice 採購額
from T_Merchandise
inner join T_PurchaseBillDetail
on T_Merchandise.Fid=T_PurchaseBillDetail.FmerchandiseId


將上述兩個 SQL 語句進行 UNION 運算就可以將兩個檢索的結果集合並了,不過這樣
就無法區分銷售單和採購單了。為了區分銷售單和採購單,同時方便後續運算,我們將檢索
產品的購買記錄的金額全部取負值,這樣就可以表示採購行為的金額為負值:

這樣就可以將銷售記錄和採購記錄同時顯示了:
select T_Merchandise.FName,T_Merchandise.FPrice*T_SaleBillDetail.Fcount Amount
	from T_Merchandise
	inner join T_SaleBillDetail
	on T_Merchandise.Fid=T_SaleBillDetail.FMerchandiseId
	union all
	select T_Merchandise.FName,T_PurchaseBillDetail.Fcount*T_Merchandise.FPrice*(-1) Amount
	from T_Merchandise
	inner join T_PurchaseBillDetail
	on T_Merchandise.Fid=T_PurchaseBillDetail.FmerchandiseId


這個結果集中列出了每一條詳細交易記錄,包括商品名和交易金額,銷售行為的交易額
為正值,而購買行為的交易額為負值。有個這個執行結果,只要將這個 SQL 語句做為子查
詢,然後按照商品名進行分組,然後計算交易金額的總和。SQL語句如下:
select detail.fname,sum(amount) from
(
	select T_Merchandise.FName,T_Merchandise.FPrice*T_SaleBillDetail.Fcount Amount
	from T_Merchandise
	inner join T_SaleBillDetail
	on T_Merchandise.Fid=T_SaleBillDetail.FMerchandiseId
	union all
	select T_Merchandise.FName,T_PurchaseBillDetail.Fcount*T_Merchandise.FPrice*(-1) Amount
	from T_Merchandise
	inner join T_PurchaseBillDetail
	on T_Merchandise.Fid=T_PurchaseBillDetail.FmerchandiseId
) as detail
group by fname