由簡到難生成資料庫報表(一)
在接下來,我想借助一個例子,來加深大家對資料庫報表的認識,由簡到難生成資料庫報表!
這裡建立一個簡化的進銷系統,系統中只有銷售單和採購單,不存在紅衝單據及其庫存、退貨等單據。由於銷售單和採購單存在主從結構,所以將這兩張表中的主從資料分別儲存在不同的表中。下面是這個系統中表之間的關係圖:
表 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