上海交通大学信息安全工程学院,F0503602,石君霄,5050369043
文章及程序实现均采用CreativeCommons BY-NC 3.0协议授权
说明:本设计的适用对象是区域-门店级别的数码商品经销企业,类似于“永乐”上海总部以及其下属的全部门店这样的规模。总部及各门店全部使用VPN网络高速接入同一个平台。
对一家假想的数码商品经销企业进行简单的业务流程分析后画出以下数据流图。
根据上述数据流图,数码商品进销存管理涉及的表格大致如下:
数码商品进销存管理涉及的处理要求大致如下:
根据需求分析的结果,得到的用ER图表示的概念设计结果如图所示。为了清楚起见,只给出了主码属性,其他所有属性均被省略。
由上一步概念设计得到14个基本表:商品类别、商品、商品分类、相关商品、报价、门店、存货、购物单、销售记录、会员卡、仓库、调拨、供应商、进货,得到5个视图表:会员关注表、可提货商品表、销量统计表、需补充商品表、可调出商品表。下面用SQL语言给出它们的结构定义。
#商品分类表
CREATE table category
(
ID int primary key,
title nvarchar(20) not null,
parentID int null references category(ID)
)
;
#商品表
CREATE table product
(
ID char(13) primary key,
pName nvarchar(20) not null,
cat int not null references category(ID),
brandName nvarchar(20) null,
model nvarchar(20) null
)
;
#相关商品表
CREATE table pSuggest
(
pID char(13) references product(ID),
suggestID char(13) references product(ID),
primary key(pID, suggestID)
)
;
#门店表
CREATE table shop
(
ID int primary key,
sName nvarchar(20) not null,
address nvarchar(50) not null,
phone nvarchar(30) not null
)
;
#仓库表
CREATE table warehouse
(
ID int primary key,
wName nvarchar(20) not null,
address nvarchar(50) not null,
shopID int null references shop(ID)
)
;
#报价表
CREATE table pPrice
(
ID int primary key auto_increment,
pID char(13) not null references product(ID),
shopID int not null references shop(ID),
creditReq decimal(8, 2) default 0 check(creditReq>=0),
price decimal(8, 2) not null check(price>0),
creditGet decimal(8, 2) default 0 check(creditGet>=0),
startDate datetime default '1000-01-01 00:00:00',
endDate datetime default '9999-12-31 23:59:59',
check(startDate<endDate)
)
;
#存货表
CREATE table stor
(
pID char(13) references product(ID),
wID int references warehouse(ID),
quantity int not null check(quantity>=0),
primary key(pID, wID)
)
;
#购物单表
CREATE table bill
(
ID int primary key auto_increment,
shopID int not null references shop(ID),
t datetime not null,
pID char(13) not null references product(ID),
salesID int null references sales(ID),
receiptPrinted tinyint default 0 check(receiptPrinted=0 OR receiptPrinted=1)
)
;
#会员卡表
CREATE table member
(
ID int primary key auto_increment,
shopID int not null references shop(ID),
credit decimal(12, 2) default 0,
pname nvarchar(20) not null,
birthday date null,
phone nvarchar(30) null,
email nvarchar(100) null,
address nvarchar(50) null,
postal char(6) null
)
;
#销售表
CREATE table sales
(
receiptID int primary key auto_increment,
pID char(13) not null references product(ID),
shopID int not null references shop(ID),
t datetime not null,
memberID int null references member(ID),
price decimal(8, 2) not null,
credit decimal(8, 2) default 0
)
;
#调拨表
CREATE table flitting
(
ID int primary key auto_increment,
pID char(13) not null references product(ID),
wFrom int not null references warehouse(ID),
wInto int not null references warehouse(ID),
t datetime not null,
quantity int not null check(quantity>0),
isReceived tinyint default 0 check(isReceived=0 OR isReceived=1)
)
;
#供应商表
CREATE table supplier
(
ID int primary key auto_increment,
sName nvarchar(30) not null unique,
sManager nvarchar(20) not null,
phone nvarchar(20) not null
)
;
#进货表
CREATE table stock
(
ID int primary key auto_increment,
supID int not null references supplier(ID),
pID char(13) not null references product(ID),
wInto int not null references warehouse(ID),
t datetime not null,
price decimal(8, 2) not null check(price>=0),
quantity int not null check(quantity>0),
isReceived tinyint default 0 check(isReceived=0 OR isReceived=1)
)
;
#会员关注表,只能查到商品所属的最底层类别
CREATE view interest as
SELECT member.*, category.ID as catID, category.title as catTitle, count(sales.receiptID) as n
FROM member
INNER JOIN sales
ON member.ID=sales.memberID
INNER JOIN product
ON sales.pID=product.ID
INNER JOIN category
ON product.cat=category.ID
GROUP BY member.ID, category.ID
ORDER BY count(sales.receiptID) desc;
#可提货商品表
CREATE view pAvailable as
SELECT product.ID as pID, product.pName as pName, category.ID as catID, category.title as catTitle,
product.brandName as brandName, product.model as model, shop.ID as shopID, shop.sName as sName,
warehouse.ID as wID, warehouse.wName as wName, stor.quantity-
(SELECT count(ID)
FROM bill
WHERE bill.shopID=shop.ID AND bill.pID=product.ID
) as n,
(SELECT price
FROM pPrice
WHERE pPrice.pID=product.ID AND pPrice.shopID=shop.ID AND pPrice.startDate<=NOW() AND pPrice.endDate>=NOW()
AND pPrice.creditReq=0 LIMIT 0, 1
) as price
FROM category
RIGHT JOIN product
ON category.ID=product.cat
INNER JOIN stor
ON product.ID=stor.pID
INNER JOIN warehouse
ON stor.wID=warehouse.ID
LEFT JOIN shop
ON warehouse.shopID=shop.ID
WHERE stor.quantity-
(SELECT count(ID)
FROM bill
WHERE bill.shopID=shop.ID AND bill.pID=product.ID
)
>0
ORDER BY pID;
#销量统计表
CREATE view salesCount as
SELECT product.ID as pID, product.pName as pName, product.brandName as brandName, product.model as model,
shop.ID as shopID, shop.sName as sName, cast(sales.t as date) as t, count(sales.receiptID) as n
FROM product
LEFT JOIN sales
ON product.ID=sales.pID
INNER JOIN shop
ON sales.shopID=shop.ID
GROUP BY product.ID, shop.ID, cast(sales.t as date)
ORDER BY shopID, pID;
#需补充商品表,最近5天销量大于库存的商品
CREATE view pNeed as
SELECT product.ID as pID, product.pName as pName, product.brandName as brandName, product.model as model,
salesCount.shopID as shopID, salesCount.sName as sName, sum(salesCount.n) as sold,
warehouse.ID as wID, warehouse.wName as wName, stor.quantity as remain,
sum(salesCount.n)-stor.quantity as need
FROM product
INNER JOIN salesCount
ON product.ID=salesCount.pID
INNER JOIN warehouse
ON salesCount.shopID=warehouse.shopID
INNER JOIN stor
ON product.ID=stor.pID AND warehouse.ID=stor.wID
WHERE curdate()>=salesCount.t AND curdate()-interval 5 day<salesCount.t
GROUP BY product.ID, warehouse.shopID
HAVING sum(salesCount.n)>2*stor.quantity
ORDER BY wID, pID;
#可调出商品表,门店附属仓库中最近15天销量小于库存的商品,非门店附属仓库中的全部商品
CREATE view pExtra as
(SELECT product.ID as pID, product.pName as pName, product.brandName as brandName, product.model as model,
warehouse.ID as wID, warehouse.wName as wName,
stor.quantity as remain, stor.quantity-sum(salesCount.n) as extra
FROM product
INNER JOIN salesCount
ON product.ID=salesCount.pID
INNER JOIN warehouse
ON salesCount.shopID=warehouse.shopID
INNER JOIN stor
ON product.ID=stor.pID AND warehouse.ID=stor.wID
WHERE curdate()>=salesCount.t AND curdate()-interval 15 day<salesCount.t
GROUP BY product.ID, warehouse.shopID
HAVING sum(salesCount.n)<stor.quantity
)
UNION
(SELECT product.ID as pID, product.pName as pName, product.brandName as brandName, product.model as model,
warehouse.ID as wID, warehouse.wName as wName, stor.quantity as remain, stor.quantity as extra
FROM product
INNER JOIN stor
ON product.ID=stor.pID
INNER JOIN warehouse
ON stor.wID=warehouse.ID
WHERE warehouse.shopID is null
GROUP BY product.ID, warehouse.shopID
)
ORDER BY wID, pID;
机器实现的任务:首先把逻辑阶段设计的结果,即数据库的逻辑模式(它包括基本表和视图的定义)在具体的计算机系统中建立起来,然后安装需求分析对数据库的各种处理要求给出相应的实现方法。我选择了MySQL这款高性能的开源DBMS,并且用AJAX+PHP实现Browser/Server架构的数据库应用系统。
机器实现中用到的数据操纵和查询语句的实现方法,分别如下所示。
SELECT ID, title FROM category WHERE parentID is null;
SELECT * FROM product WHERE cat=?1catID UNION SELECT product.* FROM product INNER JOIN category ON product.cat=category.ID WHERE category.parentID=?1catID;
SELECT ID, title FROM category WHERE parentID=?1catID;
SELECT * FROM product WHERE pName LIKE CONCAT('%',?pName,'%');
SELECT n FROM pAvailable WHERE shopID=?1shopID AND pID=?pID;
INSERT INTO bill(shopID, t, pID, receiptPrinted) VALUES(?1shopID, NOW(), ?pID, 0) ;
SELECT shopID, pID FROM bill WHERE ID=?billID AND salesID is null;
SELECT credit FROM member WHERE ID=?1memberID;
SELECT price, creditGet FROM pPrice WHERE shopID=?1shopID AND pID=?pID AND startDate<=NOW() AND endDate>=NOW() AND creditReq<=?1credit ORDER BY price desc limit 0, 1;
INSERT INTO sales(pID, shopID, t, memberID, price, credit) VALUES(?pID, ?1shopID, NOW(), ?1memberID, ?1price, ?1credit) ;
UPDATE bill SET salesID=LAST_INSERT_ID() WHERE ID=?1billID;
UPDATE member SET credit=credit+?1credit WHERE ID=?1memberID;
SELECT * FROM sales WHERE salesID=(SELECT salesID FROM bill WHERE ID=?1billID AND salesID is not null AND receiptPrinted=0) ;
UPDATE bill SET receiptPrinted=1 WHERE ID=?1billID;
SELECT * FROM bill WHERE ID=?1billID AND salesID is not null;
UPDATE stor SET quantity=quantity-1 WHERE pID=?pID AND wID=(SELECT ID FROM warehouse WHERE shopID=?1shopID) ;
DELETE FROM bill WHERE ID=?1billID;
DELETE FROM bill WHERE salesID is null AND t<NOW()-interval 30 minute;
INSERT INTO member(shopID, credit, pName, birthday, phone, email, address, postal) VALUES(?1shopID, 0, ?pName, ?birthday, ?phone, ?email, ?address, ?postal) ;
SELECT * FROM interest WHERE catID=?1catID;
SELECT * FROM supplier WHERE sName=?sName;
INSERT INTO supplier(sName, sManager, phone) VALUES(?sName, ?sManager, ?phone) ;
UPDATE supplier SET sName=?sName, sManager=?sManager, phone=?phone WHERE ID=?1supID;
SELECT * FROM product WHERE pName=?pName
INSERT INTO product(ID, pName, cat, brandName, model) VALUES(?pID, ?pName, ?1cat, ?brandName, ?model)
INSERT INTO pPrice(pID, shopID, creditReq, price, creditGet, startDate, endDate) VALUES(?pID, ?1shopID, ?1creditReq, ?1price, ?1creditGet, ?startDate, ?endDate)
INSERT INTO stock(supID, pID, wInto, t, price, quantity, isReceived) VALUES(?1supID, ?pID, ?1wInto, NOW(), ?1price, ?1quantity, 0) ;
SELECT * FROM pNeed;
SELECT * FROM pExtra WHERE pID=?pID;
INSERT INTO flitting(pID, wFrom, wInto, t, quantity, isReceived) VALUES(?pID, ?1wFrom, ?1wInto, NOW(), ?1quantity, 0) ;
SELECT * FROM stock INNER JOIN product ON stock.pID=product.ID WHERE stock.ID=?1stockID AND stock.isReceived=0;
SELECT * FROM flitting INNER JOIN product ON flitting.pID=product.ID WHERE flitting.ID=?1flittingID AND flitting.isReceived=0;
UPDATE stock SET isReceived=1 WHERE ID=?1stockID;
UPDATE flitting SET isReceived=1 WHERE ID=?1flittingID;
UPDATE stor SET quantity=quantity+?1quantity WHERE pID=?pID AND wID=?1wID;
INSERT INTO stor(pID, wID, quantity) VALUES(?pID, ?1wID, ?1quantity) ;
SELECT product.pID, product.pName, stor.quantity FROM product INNER JOIN stor ON product.ID=stor.pID WHERE stor.wID=?1wID AND stor.quantity>0;
SELECT * FROM stock WHERE isReceived=0;
SELECT * FROM flitting WHERE isReceived=0;