實作練習 : 如何從實體表單轉換為資料庫表單?

這篇的重點要談到以下幾個 : 

(1)資料表單設計的一些實務程序。
(2)資料欄位中代碼的設計。
(3)資料表單中外鍵約束的例外。
(4)資料表單有時需要違反正規化。
(5)資料表單中有時還是需要必要的冗餘資料。

在前面這篇已經談過將實體表單轉換為資料庫表單,但是並沒有說明過程。這篇來說明一下過程,並且再修正一下之前的答案。

要把紙面資料電子化,先把紙面資料的欄位整理出來,然後再看各欄位適合放在哪些資料表單中。我們從上面「出貨單」,看到以下的資訊 : 

(1) 公司資訊 (公司名稱、電話、傳真、地址)
(2) 客戶資訊 (客戶名稱、電話、傳真、送貨地址)
(3) 出貨日期、出貨單號
(4) 產品資訊 (產品編號、品名及規格、數量、單價、金額、備註)
(5) 經手人資訊 (助理、業務)
(6) 備註
(7) 簽收資訊 (簽收狀態、誰簽收、簽收日期)

通常「出貨單」資料都會從「訂單」資料轉過來,因此之前文章產生以下表單

客戶資料表 customer(cusno, cusname, cuszipcode, cusaddress, custel, cusfax, cuscontact)
職員資料表 employee(empno, empname, deptno, emptitle)
公司部門表 department(deptno, deptname)
產品資料表 product(prodno, prodname, prodprice, prodamount)
訂單主檔資料表 myorder(ordno, cusno, orddate, ordtotal, salesno, assistno)
訂單品項資料表 myorderitem(ordno, serial, prodno, prodqty, prodprice)

我們這次把「訂單」跟「出貨單」的結構都寫出來。

當然以下的實體關係圖 (Entity Relationship Diagram),你也可以稱為類別圖 (Class Diagram) 並不是一下子就完整畫出來,而是來來回回邊畫邊修改,其實就是根據你資料庫知識,在紙面上進行電腦邏輯檢驗。

以下是最後的版本。




公司基本資料表

從(1) 公司資訊 (公司名稱、電話、傳真、地址),產生一個公司基本資料表 :

這是自己的公司基本資料表 company (co_no, co_name, co_tel, co_fax, co_address)
當然這個公司基本資料表只有一筆資料,也可以省略,只是有了公司基本資料表,就不必把公司資訊寫在程式內,修改時會比較方便。


客戶基本資料表

從(2) 客戶資訊 (客戶名稱、電話、傳真、送貨地址),產生一個客戶基本資料表 :

客戶基本資料表其實就是客戶的公司基本資料表
customer (c_no, c_name, c_zipcode, c_address, c_tel, c_fax, c_contact)

也可以再擴充其他必要的欄位,如果想完整記載客戶公司內所有聯繫資訊,可以改成
customer (c_no, c_name, c_zipcode, c_address, c_tel, c_fax, contact_no, c_email)
主鍵c_no,外鍵contact_no關聯到contact的contact_no

再建立一個客戶聯繫人資料表
contact (contact_no, c_no, contact_name, contact_tel, contact_mobile, contact_email, contact_memo)
主鍵就是contact_no
contact_memo可以記載額外資訊,例如職位等,這個欄位是給人看的,不是給電腦使用。


訂單資料表

從(3) 出貨日期、出貨單號,產生一個出貨單表頭資料表 :

因為要從訂單轉成出貨,我們先產生訂單表頭資料表 

訂單表頭資料表 myorder (o_no, c_no, o_date, o_total, salesno, assistno, o_status)

因為salesno與assistno要關聯到公司人員,因此再建立一個職員資料表
employee (e_no, e_name, dept_no, e_tel, e_mobile, e_address, e_email)

如此,myorder的salesno與assistno就可以關聯到employee的e_no
dept_no就關聯到部門dept表單的dept_no
dept (dept_no, dept_name)

myorder的o_status就可以用代碼表示 : 預設0、1已付款、2結案、3取消 (要檢查代碼的邏輯)


資料欄位中代碼的設計

代碼的邏輯是什麼? 例如訂單成立時,o_status=0,然後客戶付款後,o_status=1
o_status可以從0變成3,可以從1變成2,但是不能從1變成3,也不能從0變成2
也就是必須考慮「訂單的生命週期」: 

訂單的生命週期可能是 ~ 訂單成立、轉成出貨單、出貨單完成、完成付款、訂單結案
也可能是 ~ 訂單成立、完成付款、轉成出貨單、出貨單完成、訂單結案
也可能是 ~ 訂單成立、訂單取消
也可能是 ~ 訂單成立、完成付款、退款、訂單取消

如果再成立一個 payment表單,myorder的o_status就可以取消「1已付款」的狀態,因為是否付款只要檢查payment表單就知道 (同樣的,是否已經轉為出貨單,只要檢查出貨表單就知道)。

payment (p_no, o_no, p_date, p_total, p_method, p_status) 
假設允許信用卡付款以及超商取貨付款,p_method就設定 
p_method=1 信用卡付款,p_method=2 超商取貨付款
信用卡付款就要記錄部分卡號或是交易序號之類,超商取貨付款就要記錄超商類別、超商代號、交易序號。因此這兩種付款方式需要儲存的資料不同,就必須分開兩個表單。

p_status就是付款狀態,可以預設0、1完成付款、2取消

然後
信用卡付款的資訊就記錄在 creditcard (cc_no, p_no, cc_card_no, cc_card_holder, cc_serial)
超商取貨付款的資訊就記錄在 storepay (sp_no, p_no, store_cate, store_no, sp_payee, sp_mobile, sp_serial)

當然還有商家的傳統付款 : 轉帳、支票、現金
p_method=3 轉帳付款,p_method=4 支票付款

wirepay (wp_no, p_no, wp_bank, wp_account, wp_date)
checkpay (cp_no, p_no, cp_bank, cp_check_no, cp_date)

現金付款就不需要再一個表單

再來產生訂單表身資料表
order_items (oi_no, o_no, prod_no, prod_qty, prod_price)

還需要跟產品有關的資料表
product (prod_no, category_no, prod_name, prod_price, prod_unit, prod_stock)
category (category_no, category_name)


必要的冗餘資料

在product資料表都已經有prod_price,為何order_items還需要prod_price? 因為產品資料表的價錢是基本定價,在訂單表身資料表中的prod_price是最後的價錢 (可能有折扣)。而且product資料表可能會不定期變動,如果沒有把最後的價錢寫進訂單表身資料表,最後整個資料可能都會產生問題。例如在2024年,微波爐是5000元,如果2025年微波爐變成6000元,如果沒有把價錢寫入訂單中,2025年時去看2024年的舊訂單資料,就變成6000元。


出貨單資料表

再來從訂單資料轉為出貨單 ~~

根據以下資料來決定出貨單的結構

(4) 產品資訊 (產品編號、品名及規格、數量、單價、金額、備註)
(5) 經手人資訊 (助理、業務)
(6) 備註
(7) 簽收資訊 (簽收狀態、誰簽收、簽收日期)

shipping (ship_no, o_no, ship_date, receive_date, ship_status, ship_address, ship_contact, ship_tel, ship_handler, ship_memo)
外鍵ship_contact關聯到contact的contact_no
出貨單也許還需要註明從哪個倉庫出貨等資訊,但在這邊先略過。

shipping_items (si_no, ship_no, prod_no, prod_qty, prod_memo)
實際的出貨可能會分成shipping跟packing,例如把多個產品打包成一個包裝,然後再把包裝箱的號碼寫在出貨單,這時shipping_items表單中就不是產品資訊,而是箱子的序號,這裡就不再細分,只把產品資訊寫在出貨單上。

shipping資料表為何還需要 ship_address等資料,因為訂單地址可能不是送貨地址,因此另外欄位儲存,如果留空白表示跟訂單一樣,這樣子可以保留彈性。

shipping_items為何沒有單價、金額? 基本上可以透過ship_no關聯到o_no,再關聯到order_items去取得prod_price,不過如果會讓查詢變得複雜,也可以改成 : 

shipping_items (si_no, ship_no, prod_no, prod_qty, prod_price, prod_memo)

不過如果這樣做,就是為了效能而「違反正規化」的設計。


違反正規化的設計

通常以下情境會需要「違反正規化」的設計 :

(1)性能需求 : 正規化會產生多個表格,以外鍵來關聯,這樣可能一個查詢需要關聯很多個表單,因此當需要提升性能的時候,就可能會違反正規化來降低表格數量或是參照。

(2)高讀取需求 : 如果系統需要密集讀取某些資料,就可能會違反正規化將這些資料盡可能放在一個表單內。

(3)儲存空間充足 : 正規化其一目的是為了降低冗餘,如果儲存空間很大,就可以適時的違反正規化設計。

(4)歷史記錄需求 : 上面在order_items表單還儲存產品價錢,就是為了歷史記錄需求。

(5)簡化設計與維護 : 有時候過度正規化會讓語法變得很複雜,要修改邏輯時會很麻煩,有時候就會利用違反正規化讓語法簡單一些。

但是「違反正規化」需要適時使用,否則會讓資料產生問題。

最後,你可以再修正一下「代碼」的設定,如果沒有規則,會有點混亂。

有代碼的欄位,可以考慮統一一下 : 

myorder的o_status ~ 預設0、1完成付款、2訂單取消、3結案
payment的p_status ~ 預設0、1完成付款、2付款取消
shipping的ship_status ~ 預設0、1完成出貨、2出貨取消


產生建置資料表的語法

-- 建立資料庫

CREATE DATABASE myshop
CHARACTER SET UTF8MB4
COLLATE UTF8MB4_GENERAL_CI;

-- 建立各資料表

CREATE TABLE company (
    co_no INT AUTO_INCREMENT PRIMARY KEY,
    co_name VARCHAR(100) NOT NULL,
    co_tel VARCHAR(15),
    co_fax VARCHAR(15),
    co_address VARCHAR(255)
);

CREATE TABLE customer (
    c_no INT AUTO_INCREMENT PRIMARY KEY,
    c_name VARCHAR(100) NOT NULL,
    c_zipcode VARCHAR(10),
    c_address VARCHAR(255),
    c_tel VARCHAR(15),
    c_fax VARCHAR(15),
    contact_no INT,
    c_email VARCHAR(100)    
);

CREATE TABLE contact (
    contact_no INT AUTO_INCREMENT PRIMARY KEY,
    c_no INT NOT NULL,
    contact_name VARCHAR(100) NOT NULL,
    contact_tel VARCHAR(15),
    contact_mobile VARCHAR(15),
    contact_email VARCHAR(100),
    contact_memo VARCHAR(255),
    FOREIGN KEY (c_no) REFERENCES customer(c_no)
);

CREATE TABLE category (
    category_no INT AUTO_INCREMENT PRIMARY KEY,
    category_name VARCHAR(100) NOT NULL
);

CREATE TABLE product (
    prod_no INT AUTO_INCREMENT PRIMARY KEY,
    category_no INT,
    prod_name VARCHAR(100) NOT NULL,
    prod_price DECIMAL(10, 2) NOT NULL,
    prod_unit VARCHAR(50),
    prod_stock INT,
    FOREIGN KEY (category_no) REFERENCES category(category_no)
);

CREATE TABLE department (
    dept_no INT AUTO_INCREMENT PRIMARY KEY,
    dept_name VARCHAR(100) NOT NULL
);

CREATE TABLE employee (
    e_no INT AUTO_INCREMENT PRIMARY KEY,
    dept_no INT NOT NULL,
    e_name VARCHAR(100) NOT NULL,
    e_title VARCHAR(50),
    e_tel VARCHAR(15),
    FOREIGN KEY (dept_no) REFERENCES department(dept_no)
);

CREATE TABLE myorder (
    o_no INT AUTO_INCREMENT PRIMARY KEY,
    c_no INT NOT NULL,
    o_date DATE NOT NULL,
    o_total DECIMAL(10, 2),
    salesno INT,
    assistno INT,
    o_status TINYINT DEFAULT 0,
    FOREIGN KEY (c_no) REFERENCES customer(c_no),
    FOREIGN KEY (salesno) REFERENCES employee(e_no),
    FOREIGN KEY (assistno) REFERENCES employee(e_no)
);

CREATE TABLE order_items (
    oi_no INT AUTO_INCREMENT PRIMARY KEY,
    o_no INT NOT NULL,
    prod_no INT NOT NULL,
    prod_qty INT NOT NULL,
    prod_price DECIMAL(10, 2),
    FOREIGN KEY (o_no) REFERENCES myorder(o_no),
    FOREIGN KEY (prod_no) REFERENCES product(prod_no)
);

CREATE TABLE shipping (
    ship_no INT AUTO_INCREMENT PRIMARY KEY,
    o_no INT NOT NULL,
    ship_date DATE NOT NULL,
    receive_date DATE,
    ship_status TINYINT DEFAULT 0,
    ship_address VARCHAR(255),
    ship_contact INT,
    ship_tel VARCHAR(15),
    ship_memo VARCHAR(255),
    FOREIGN KEY (o_no) REFERENCES myorder(o_no),
    FOREIGN KEY (ship_contact) REFERENCES contact(contact_no)
);

CREATE TABLE shipping_items (
    si_no INT AUTO_INCREMENT PRIMARY KEY,
    ship_no INT NOT NULL,
    prod_no INT NOT NULL,
    prod_qty INT NOT NULL,
    prod_memo VARCHAR(255),
    FOREIGN KEY (ship_no) REFERENCES shipping(ship_no),
    FOREIGN KEY (prod_no) REFERENCES product(prod_no)
);

CREATE TABLE payment (
    p_no INT AUTO_INCREMENT PRIMARY KEY,
    o_no INT NOT NULL,              -- 外鍵,指向 myorder 表
    p_date DATE NOT NULL,
    p_total DECIMAL(10, 2),
    p_method TINYINT,
    p_status TINYINT DEFAULT 0,
    FOREIGN KEY (o_no) REFERENCES myorder(o_no)
);

CREATE TABLE creditcard (
    cc_no INT AUTO_INCREMENT PRIMARY KEY,
    p_no INT NOT NULL,                  -- 外鍵,指向 payment 表
    cc_card_no VARCHAR(16),             -- 信用卡號(僅保存末四碼)
    cc_card_holder VARCHAR(100),        -- 持卡人姓名
    cc_serial VARCHAR(50),              -- 交易序號
    FOREIGN KEY (p_no) REFERENCES payment(p_no)
);

CREATE TABLE storepay (
    sp_no INT AUTO_INCREMENT PRIMARY KEY,
    p_no INT NOT NULL,                  -- 外鍵,指向 payment 表
    store_cate VARCHAR(50),             -- 超商類別(例如 7-11)
    store_no VARCHAR(50),               -- 超商店號
    sp_payee VARCHAR(100),              -- 付款人名稱
    sp_mobile VARCHAR(15),              -- 付款人手機號碼
    sp_serial VARCHAR(50),              -- 交易序號
    FOREIGN KEY (p_no) REFERENCES payment(p_no)
);


外鍵約束的例外

以上有個地方要注意,原本customer表單的contact_no要關聯到contact的contact_no,但是contact表單的c_no又要關聯到customer表單,如果都宣告外鍵約束,會形成循環外鍵約束而無法插入資料,因此我們就不宣告customer表單的contact_no外鍵。


插入範例資料

-- 插入公司資料
INSERT INTO company (co_name, co_tel, co_fax, co_address) VALUES
('宇宙公司', '02-12345678', '02-87654321', '宇宙市宇宙區宇宙路1號');

-- 插入客戶公司資料
INSERT INTO customer (c_name, c_zipcode, c_address, c_tel, c_fax, contact_no, c_email) VALUES
('世界公司', '100', '台北市OO區OO路3號', '02-34567890', '02-65432109', 1, '[email protected]'),
('銀河公司', '200', '高雄市YY區OO路4號', '07-56789012', '07-21098765', 2, '[email protected]'),
('太陽公司', '200', '高雄市XX區OO路5號', '07-56789000', '07-21098700', 3, '[email protected]');
-- 以上就有三家客戶公司,c_no=1,2,3

-- 插入聯絡人資料
INSERT INTO contact (c_no, contact_name, contact_tel, contact_mobile, contact_email, contact_memo) VALUES
(1, '王小明', '02-34567890', '0912345678', '[email protected]', '經理'),
(2, '李大華', '07-56789012', '0923456789', '[email protected]', '副總'),
(3, '李小華', '07-56789011', '0923456711', '[email protected]', '副理');
-- 以上有三個聯絡人,contact_no=1,2,3,各屬於c_no=1,2,3 客戶公司

-- 插入商品分類資料
INSERT INTO category (category_name) VALUES
('電子產品'), ('家用設備');
-- 以上有兩個類別 category_no=1,2

-- 插入商品資料
INSERT INTO product (category_no, prod_name, prod_price, prod_unit, prod_stock) VALUES
(1, '筆記型電腦', 35000.00, '台', 50),
(1, '智慧手機', 20000.00, '支', 100),
(2, '微波爐', 5000.00, '台', 30),
(2, '烤箱', 2000.00, '台', 10),
(1, '桌上型電腦', 28000.00, '台', 5);
-- 以上商品 prod_no=1,2,3,4,5 各屬於category_no=1,1,2,2,1

-- 插入部門資料
INSERT INTO department (dept_name) VALUES
('業務部'), ('客服部');
-- 以上有兩個部門 dept_no=1,2

-- 插入員工資料
INSERT INTO employee (dept_no, e_name, e_title, e_tel) VALUES
(1, '張三', '業務經理', '02-11112222'), 
(2, '李四', '客服專員', '02-33334444'),
(2, '王五', '客服專員', '02-33330000');
-- 以上有三個職員 e_no=1,2,3 各屬於dept_no=1,2,2

-- 插入訂單資料
INSERT INTO myorder (c_no, o_date, o_total, salesno, assistno, o_status) VALUES
(1, '2024-09-21', 55000.00, 1, 2, 3),
(2, '2024-10-02', 5000.00, 1, 2, 1),
(2, '2024-11-10', 10000.00, 1, 2, 0),
(3, '2024-11-05', 20000.00, 3, 2, 1);
-- 以上有四筆訂單 o_no=1,2,3,4 分別客戶是 c_no=1,2,2,3 訂單狀態是 3,1,0,1
-- myorder的o_status ~ 預設0、1完成付款、2訂單取消、3結案

-- 插入訂單項目資料
INSERT INTO order_items (o_no, prod_no, prod_qty, prod_price) VALUES
(1, 1, 1, 35000.00),
(1, 2, 1, 20000.00),
(2, 3, 1, 5000.00),
(3, 3, 1, 10000.00),
(4, 3, 2, 10000.00);

-- 插入出貨資料
INSERT INTO shipping (o_no, ship_date, receive_date, ship_status, ship_address, ship_contact, ship_tel, ship_memo) VALUES
(1, '2024-10-05', '2024-10-06', 1, '台北市中正區XX路1號', 1, '02-34567890', '正常出貨'),
(2, '2024-10-08', '2024-10-10', 1, '高雄市前鎮區OO路4號', 2, '07-56789012', '正常出貨'),
(4, '2024-11-25', '2024-11-26', 1, '高雄市YY區OO路14號', 3, '07-11122233', '正常出貨');
-- 以上有三筆出貨 ship_no=1,2,3
-- shipping的ship_status ~ 預設0、1完成出貨、2出貨取消

-- 插入出貨項目資料
INSERT INTO shipping_items (ship_no, prod_no, prod_qty, prod_memo) VALUES
(1, 1, 1, '全新出貨'),
(1, 2, 1, '全新出貨'),
(2, 3, 1, '全新出貨'),
(3, 3, 1, '全新出貨');

-- 插入付款資料
INSERT INTO payment (o_no, p_date, p_total, p_method, p_status) VALUES
(1, '2024-11-07', 55000.00, 1, 1),
(2, '2024-11-08', 5000.00, 2, 1),
(4, '2024-11-09', 20000.00, 1, 1);
-- payment的p_status ~ 預設0、1完成付款、2付款取消

-- 插入信用卡付款資料
INSERT INTO creditcard (p_no, cc_card_no, cc_card_holder, cc_serial) VALUES
(1, '1234', '王小明', 'TXN001');

-- 插入超商付款資料
INSERT INTO storepay (p_no, store_cate, store_no, sp_payee, sp_mobile, sp_serial) VALUES
(2, '7-11', 'A001', '李大華', '0923456789', 'STX001'),
(3, '7-11', 'A002', '吳曉莉', '0909111222', 'ABC001');

有了範例資料之後,以下問題該如何寫MySQL指令? 

(1)已經完成出貨的訂單有哪些? 已經完成付款的訂單有哪些? 

SELECT o.o_no, o.o_date, o.o_total
FROM myorder o
JOIN shipping s ON o.o_no = s.o_no
WHERE s.ship_status = 1; -- 出貨狀態 1 表示完成出貨

SELECT o.o_no, o.o_date, o.o_total
FROM myorder o
JOIN payment p ON o.o_no = p.o_no
WHERE p.p_status = 1; -- 付款狀態 1 表示完成付款

(2)尚未完成出貨的訂單有哪些? 尚未完成付款的訂單有哪些?

SELECT o.o_no, o.o_date, o.o_total
FROM myorder o
LEFT JOIN shipping s ON o.o_no = s.o_no
WHERE s.ship_status = 0 OR s.ship_status IS NULL; -- 出貨狀態 0 或未出貨

SELECT o.o_no, o.o_date, o.o_total
FROM myorder o
LEFT JOIN payment p ON o.o_no = p.o_no
WHERE p.p_status = 0 OR p.p_status IS NULL; -- 付款狀態 0 或未付款

(3)某個客戶在某個日期區間,訂購哪些商品?

SELECT o.o_no, o.o_date, p.prod_name, oi.prod_qty, oi.prod_price
FROM myorder o
JOIN order_items oi ON o.o_no = oi.o_no
JOIN product p ON oi.prod_no = p.prod_no
WHERE o.c_no = 客戶ID AND o.o_date BETWEEN '開始日期' AND '結束日期';

替換 客戶ID、開始日期 和 結束日期。

(4)某個客戶在某個日期區間,訂購總額多少?

SELECT SUM(oi.prod_qty * oi.prod_price) AS total_amount
FROM myorder o
JOIN order_items oi ON o.o_no = oi.o_no
WHERE o.c_no = 客戶ID AND o.o_date BETWEEN '開始日期' AND '結束日期';

替換 客戶ID、開始日期 和 結束日期。

(5)某商品在某個月份的總銷售量是多少?

SELECT SUM(oi.prod_qty) AS total_sales
FROM order_items oi
JOIN myorder o ON oi.o_no = o.o_no
WHERE oi.prod_no = 商品ID AND MONTH(o.o_date) = 月份 AND YEAR(o.o_date) = 年份;

替換 商品ID、月份 和 年份。

(6)某個月份的總銷售量最高的商品是什麼?

SELECT p.prod_name, SUM(oi.prod_qty) AS total_sales
FROM order_items oi
JOIN product p ON oi.prod_no = p.prod_no
JOIN myorder o ON oi.o_no = o.o_no
WHERE MONTH(o.o_date) = 月份 AND YEAR(o.o_date) = 年份
GROUP BY oi.prod_no
ORDER BY total_sales DESC
LIMIT 1;

替換 月份 和 年份。

(7)某個月份的總銷售量最低的商品是什麼?

SELECT p.prod_name, SUM(oi.prod_qty) AS total_sales
FROM order_items oi
JOIN product p ON oi.prod_no = p.prod_no
JOIN myorder o ON oi.o_no = o.o_no
WHERE MONTH(o.o_date) = 月份 AND YEAR(o.o_date) = 年份
GROUP BY oi.prod_no
ORDER BY total_sales ASC
LIMIT 1;

替換 月份 和 年份。

(8)計算某個月份內,各業務人員的業績?

SELECT e.e_name, SUM(o.o_total) AS total_sales
FROM myorder o
JOIN employee e ON o.salesno = e.e_no
WHERE MONTH(o.o_date) = 月份 AND YEAR(o.o_date) = 年份
GROUP BY e.e_no;

替換 月份 和 年份。

(9)由電話去查詢某個月份,該客戶的訂購量?

SELECT SUM(oi.prod_qty) AS total_ordered
FROM myorder o
JOIN order_items oi ON o.o_no = oi.o_no
JOIN customer c ON o.c_no = c.c_no
WHERE c.c_tel = '客戶電話' AND MONTH(o.o_date) = 月份 AND YEAR(o.o_date) = 年份;

替換 客戶電話、月份 和 年份。

(10)依客戶的地區別,統計各地區某個月份的銷售量?

SELECT c.c_zipcode, SUM(o.o_total) AS total_sales
FROM myorder o
JOIN customer c ON o.c_no = c.c_no
WHERE MONTH(o.o_date) = 月份 AND YEAR(o.o_date) = 年份
GROUP BY c.c_zipcode;

替換 月份 和 年份。

(11)依照產品名稱去查詢該產品某個月份的銷售量?

SELECT p.prod_name, SUM(oi.prod_qty) AS total_sales
FROM order_items oi
JOIN product p ON oi.prod_no = p.prod_no
JOIN myorder o ON oi.o_no = o.o_no
WHERE p.prod_name = '產品名稱' AND MONTH(o.o_date) = 月份 AND YEAR(o.o_date) = 年份;

替換 產品名稱、月份 和 年份。

(12)統計各月份的銷售量?

SELECT YEAR(o.o_date) AS sales_year, MONTH(o.o_date) AS sales_month, SUM(o.o_total) AS total_sales
FROM myorder o
GROUP BY YEAR(o.o_date), MONTH(o.o_date)
ORDER BY sales_year, sales_month;


其他細節後續再來談囉 ^^

張貼留言

0 留言