[Day13] SQL server Group By 資料分組與聚合函數COUNT(), MAX(), MIN(), SUM(), AVG() - SQL Server資料庫入門

在這回中,將要探討SQL server中的Group By用法,並搭配聚合函數使用。
資料在資料表中都是一列一列的,而分組則是把這些列的某些特徵欄位視為分組依據,並且將他們合為一組,並使用聚合函數就能夠做出簡易的統計。

測試資料

還是一樣,先放上資料表:

customers 顧客

products 產品

orders 出貨單

order_details 出貨單明細

GROUP BY

把相同值列分組成匯總列。
Group By常常與COUNT(), MAX(), MIN(), SUM(), AVG()這些總計有關的語法一起用。

聚合函數
COUNT(column_name):取得欄位資料筆數。(ps.如果想統計整列不管有沒有空值問題可以下 COUNT(*) )
MAX(column_name):取得欄位最大值。
MIN(column_name):取得欄位最小值。
SUM(column_name):取得欄位加總。
AVG(column_name):取得欄位平均值。

語法:

1
2
3
4
5
SELECT column_name(s)
FROM table_name
WHERE condition
GROUP BY column_name(s)
ORDER BY column_name(s);

實作:統計訂單明細的資料內容。

1
2
3
4
5
Select order_number,sum(price*amount) as 訂單總額 
,avg(price) as 平均商品單價,min(price) as 最低商品單價
,max(price) 最高商品單價,sum(amount) as 總貨量
,count(order_number) as 細項數量
from order_details group by order_details.order_number

結果:
以order_number訂單編號為分組要件,把order_details表中所有重複的order_number都獨立識別成一個群組。
再以這個群組去統計資料。
如下圖,因為資料庫中order_details只有2筆資料,所以把這2筆的order_number嘗試分組,發現都只有A002這個訂單編號,於是就只分成了一組A002的資料,而這組A002資料底下則有2筆資料,可供統計聚合函數COUNT(), MAX(), MIN(), SUM(), AVG()使用。

參考資料
https://www.w3schools.com/sql/sql_groupby.asp
https://ithelp.ithome.com.tw/articles/10218537
https://ithelp.ithome.com.tw/articles/10218055

[Day12] 集合運算union、intersect、except 垂直整合 - SQL Server資料庫入門

在先前我們有使用過的多表查詢都是水平整合的概念,如JOIN [Day10] SQL server Select合併查詢JOIN用法 - SQL Server資料庫入門
以及上篇的子查詢 [Day11] SQL server 子查詢 - SQL Server資料庫入門
而這回,將要探討垂直整合這塊,資料庫提供聯集(union)、交集(intersect)、差集(except)語法可以實作這概念。

測試資料

還是一樣,先放上資料表:
customers 顧客

products 產品

orders 出貨單

order_details 出貨單明細

UNION 聯集(剔除重複)

聯集
被用來結合多個Select語句的結果集(result set)。
與UNION ALL差別在”會剔除重複”,有重複的資料只會顯示其中一筆

限制:
1.欄位的屬性必須一樣
2.每個select語句都必須有相同的欄位數
3.select語句有多個欄位時,欄位的屬性要符合順序

語法:

1
2
3
SELECT column_name(s) FROM table1
UNION
SELECT column_name(s) FROM table2;

實作:想要把products的price與order_details的price結合,且剔除重複。

1
2
3
select price from products
union
select price from order_details

結果:

UNION ALL 聯集(不剔除重複)

聯集
與UNION差別在”不剔除重複”,有重複的資料會顯示多筆

1
2
3
SELECT column_name(s) FROM table1
UNION ALL
SELECT column_name(s) FROM table2;

實作:想要把products的price與order_details的price結合,且不剔除重複。

1
2
3
select price from products
union all
select price from order_details

結果:

INTERSECT 交集

交集,將select語句的結果集合併後找出相同的值然後返回結果集。
附註:沒有INTERSECT ALL這種用法
語法:

1
2
3
SELECT column_name(s) FROM table1
INTERSECT
SELECT column_name(s) FROM table2;

實作:取出有訂過訂單的客戶編號,把customers的number與orders的customer_number做交集。

1
2
3
select number from customers
intersect
select customer_number from orders

結果:

EXCEPT 差集

差集,將select語句的結果集合併後找出不相同的值然後返回結果集。
[補充] EXCEPT及只適用於 SQL Server,Oracle的交集則是使用 MINUS,使用方法與EXCEPT一樣; 而MySQL不支援EXCEPT及MINUS,則要使用left join。可參考:http://www.geeksengine.com/database/multiple-table-select/minus-except.php
語法:

1
2
3
SELECT column_name(s) FROM table1
EXCEPT
SELECT column_name(s) FROM table2;

實作:取出”沒有”訂過訂單的客戶編號,把customers的number與orders的customer_number做差集。

1
2
3
select number from customers
except
select customer_number from orders

結果:

小結

發現蠻多細節的語法在不同資料庫的名稱、寫法又會不一樣。
如果不熟某一資料庫,只能盡可能查,否則就得在程式裡面做(雖然較耗記憶體)。
而本篇中練習了聯集(union)、交集(intersect)、差集(except),在使用上蠻直覺的,沒有太困難的操作語法,只要注意一些欄位的屬性排列不要排錯就好。
而我回顧以前,就是使用ORM(Object Relational Mapping),雖然有提供語法,但卻在Select時很難直覺寫出語句,有時甚至不支援還要變成半原生寫法。
所以,以個人經驗覺得原生的SQL語法比較好懂@@。
然後,下篇預告是GROUP BY分組的用法。
參考資料
https://www.w3schools.com/sql/sql_union.asp
https://ithelp.ithome.com.tw/articles/10216761

[Day11] SQL server 子查詢 - SQL Server資料庫入門

在上回 [Day10] SQL server Select合併查詢JOIN用法 - SQL Server資料庫入門 中,我們練習了合併查詢的JOIN。

在這回中,我們主要淺談一下子查詢。
他跟JOIN一樣都可以使用在多表的查詢上。

子查詢

子查詢通常使用在Select的Where子句上。
首先處理的是子查詢條件,才會向外處理外部的主查詢。有點像是寫一個function,然後回傳一個結果再給主程式去跑。

語法:

1
2
3
4
SELECT 欄位名稱1,欄位名稱2,...,欄位名稱n 
FROM 資料表名稱1
WHERE 欄位名稱 =
(SELECT 欄位名稱 FROM 資料表名稱2 WHERE 條件)

1.子查詢要在括號()中。
2.通常子查詢SELECT只會取得單一欄位的值,以便主查詢的欄位進行比較運算。
3.如需排序,子查詢不能使用ORDER BY,只能使用GROUP BY 子句。
4.如果子查詢可以取得多筆資料,在主查詢需使用IN邏輯運算子。

在WHERE使用子查詢主要目的是取得其他資料表紀錄的欄位值來建立WHERE句的條件運算式。

實際演練

目前表中資料

在此,提供一下目前資料表中的測試資料,作為核對。

customers 顧客

products 產品

orders 出貨單

order_details 出貨單明細

實作:想要取得名子為XXX的訂單。

1
2
Select * from orders 
where orders.customer_number = (Select number from customers where customers.name = 'XXX' )

結果:
子查詢的語句只會返回單一一筆的number叫做U001,之後就會變成orders.customer_number = ‘U001’去搜尋,最後得到結果。

實作:想要取得大於平均值的商品列表。

1
2
Select *,(select avg(price) from products) as avg_price from products 
where price > (select avg(price) from products)

結果:
原本想要寫成 where price > avg_price,但發現這樣的寫法不可以,因為前面的這種匿名欄位(原本就不在資料表中的欄位)在where後沒辦法取得,所以又補了一段(select avg(price) from products)。
總之,子查詢也可以用在欄位上。

實作:想取得訂單明細中有商品PS4及PS5的明細。
[補充]EXISTS指令:可以判斷子查詢的結果有沒有回傳資料,若有則會執行查詢中的結果。

1
2
3
Select * from order_details 
where exists
(Select * from products where (products.name = 'PS4' or products.name ='PS5') and products.number = order_details.product_number)

結果:
搜尋order_details表單後用Exists子查詢,用Select取出商品名稱為PS4或PS5的集合,並且這集合的產品編號在訂單明細的產品編號裡。

實作:想取得還沒有下過訂單的顧客。
[補充]IN指令可以檢查是否存在子查詢的資料紀錄中。

1
2
Select * from customers 
where customers.number not in (Select orders.customer_number from orders )

結果:
用子查詢去查orders資料表,得到客戶編號的集合。之後主查詢用not in去篩選不在集合中的顧客編號。
最終得到U002這個沒下訂單的顧客。

小結

在這篇中練習了子查詢的寫法,並且知道子查詢可以放在欄位、或where後,且可以搭配如exists、in等等做法去擴展用法。
可能會找一時間把一些擴展語法如exists、in等等類型的都拉出來獨立講一遍。
下篇預計會是垂直的UNION查詢的做法。

參考資料
https://ithelp.ithome.com.tw/articles/10219497

[Day10] SQL server Select合併查詢JOIN用法 - SQL Server資料庫入門

前言

在上回 [Day9] SQL server查詢語法Select - SQL Server資料庫入門 中,我們練習了一些查詢語法,且都是針對同一張表的查詢。
而在這回中,我們將要開始活用關聯式資料庫的核心用法「JOIN」,透過它實踐合併的查詢。

在這回中我們主要探討五種查詢,並實際來操作:

1.inner Join 內部合併查詢
2.left (outer) Join 左外部合併查詢
3.right (outer) Join 右外部合併查詢
4.full (outer) Join 完全外部合併查詢
5.cross Join 交叉合併查詢

目前表中資料

在此,提供一下目前資料表中的測試資料,作為核對。

關聯圖

customers 顧客

products 產品

orders 出貨單

order_details 出貨單明細

Inner Join 內部合併查詢

內部合併查詢,為兩個表中都有的值才把資料拉出來。
如下圖,就是A、B的交集。

語法:

1
2
3
4
SELECT column_name(s)
FROM table1
INNER JOIN table2
ON table1.column_name = table2.column_name;

實作:對customers以及orders做inner join,得到出貨單與顧客關聯的資料集。
ps.稍微補充一下 orders.number as orders_number 是代表把某東西用as設定別稱

1
2
3
4
Select customers.number,customers.name,orders.number as orders_number 
from customers
inner join orders
on customers.number = orders.customer_number

結果:
可以發現,原本在order表內看不到的客戶名稱name出現了。且customers表中的編號U002並沒出現在表裡,因為在orders表中它找不到相同的值。

Left (outer) Join 左外部合併查詢

取回左邊資料表的所有紀錄,並拉進右邊資料表的值。結果會以左邊資料表為主。

語法:

1
2
3
4
SELECT column_name(s)
FROM table1
LEFT JOIN table2
ON table1.column_name = table2.column_name;

實作:想要取得顧客名單同時也想知道他們有關的出訂單,對customers以及orders做left join。

1
2
3
4
Select customers.number,customers.name,orders.number as orders_number 
from customers
left join orders
on customers.number = orders.customer_number

結果:
可以發現,customers為主表,所以有客戶即使沒出貨單也被拉出來,而U001這位客戶因為關連到兩筆所以列出兩列。

Right (outer) Join 右外部合併查詢

取回右邊資料表的所有紀錄,並拉進左邊資料表的值。結果會以右邊資料表為主。
其實就是跟左外部查詢一樣,只是表顛倒放而已,通常左邊比較直覺。

語法:

1
2
3
4
SELECT column_name(s)
FROM table1
RIGHT JOIN table2
ON table1.column_name = table2.column_name;

實作:想要取得訂單,並嘗試抓出顧客名單即使客戶名單消失或為NULL,對customers以及orders做right join。

1
2
3
4
Select customers.number,customers.name,orders.number as orders_number 
from customers
right join orders
on customers.number = orders.customer_number

結果:
可以發現,orders為主表列出了所有訂單2筆A002、A003,並嘗試關聯到顧客。

Full (outer) Join 完全外部合併查詢

full Join(完全外部合併)可以取回左右兩邊資料表的所有紀錄。

語法:

1
2
3
4
5
SELECT column_name(s)
FROM table1
FULL OUTER JOIN table2
ON table1.column_name = table2.column_name
WHERE condition;

實作:訂單、顧客名單的左右資料全部合併

1
2
3
4
Select customers.number,customers.name,orders.number as orders_number 
from customers
full join orders
on customers.number = orders.customer_number

結果:
跟inner join比較的話,就是原本因為orders資料表沒有相關資料消失的U002客戶出現了。

Cross Join 交叉合併查詢

交叉連接為兩個資料表間的笛卡兒乘積 (Cartesian product),兩個資料表在結合時,不指定任何條件,即將兩個資料表中所有的可能排列組合出來
語法:

1
Select * From 表A Cross Join 表B;

也就是說表A有5筆紀錄,表B有8筆紀錄,交查合併後會得到5 * 8=40筆的紀錄,如果沒有指名欄位,欄位數就是2個資料表的欄位總數。

實作:取得訂單、顧客名單的所有可能排列組合

1
2
3
Select customers.number,customers.name,orders.number as orders_number 
from customers
cross join orders

結果:
customers目前兩筆資料、orders目前兩筆資料 2x2 = 4 所有可能排列組合。
因為沒用ON去指定,所以會發現顧客U002明明沒訂單卻關聯上了A002、A003訂單。

多組JOIN寫法

簡單來說就是可以依照你的目標去操作各種JOIN得到你要的結果。
大概畫一下圖:

實作:我想得到訂單詳細資料打平的樣子
ps.這種多組JOIN可以用as簡短,以下是全部簡寫。如果為了求好讀可以多打寫字,可以不要用as。

1
2
3
4
Select * from order_details as a
inner join orders as b on a.order_number = b.number
inner join customers as c on c.number = b.customer_number
inner join products as d on a.product_number = d.number

結果:
其實就是把所有的主鍵、外來鍵拉起來而已R。就完全成為正規化前的模樣XDD

總結

在這回中學習了合併查詢的用法JOIN,一般來說都是搭配主鍵、外來鍵去做這些事。
而平常自己實際習慣用INNER JOIN、LEFT JOIN,而FULL JOIN、CROSS JOIN則超少用到。
也許是不太直覺的關係吧XD?
且因為需求都是人想出來的,如果不是計算機專業,一般很少人會想出FULL JOIN、CROSS JOIN。

然後預告一下,下回是子查詢。@@

參考資料:
https://ithelp.ithome.com.tw/articles/10215741
https://www.fooish.com/sql/cross-join.html
https://charleslin74.pixnet.net/blog/post/460066978

[Day9] SQL server查詢語法Select - SQL Server資料庫入門

前言

在上回我們手動對資料庫填充了一些基礎資料:[Day8] SQL server手動寫入資料 - SQL Server資料庫入門
在這回中我們要利用建立好的資料庫來練習查詢語法。

目前表中資料

先看一下資料庫中的資料:
customers 顧客

products 產品

orders 出貨單

order_details 出貨單明細

SQL SELECT 語法實踐

select為資料庫查詢的語法,被用來選取資料表中的資料。
當語法執行後,資料會被儲存在一個結果表(result table),即結果集(result-set)。

1.選擇表中的部分欄位

1
2
SELECT column1, column2, ...
FROM table_name;

實作:查詢orders中的編號、寄送日、寄送地址

1
Select number,shipping_date,shipping_address from orders

結果:

2.選擇表中的全部欄位

1
SELECT * FROM table_name; 

實作:查詢orders中所有欄位

1
Select * from orders

結果:

SQL SELECT DISTINCT 語法實踐

被用來取得非重複列的集合。

1
2
SELECT DISTINCT column1, column2, ...
FROM table_name;

實作:查詢order_details的order_number,並回傳非重複的列

1
Select DISTINCT order_number from order_details 

結果:

實作:查詢order_details的order_number、id,並回傳非重複的列

1
Select DISTINCT id,order_number from order_details 

結果:
會發現跟上面的只搜尋order_number多增加一列,因為id與order_number這樣視為一列

SQL WHERE 語法

用可來限制Select的條件、過濾資料。
可使用的限制條件:

Operator Description
= Equal
> Greater than
< Less than
>= Greater than or equal
<= Less than or equal
<> Not equal. Note: In some versions of SQL this operator may be written as !=
BETWEEN Between a certain range
LIKE Search for a pattern
IN To specify multiple possible values for a column

語法:

1
2
3
SELECT column1, column2, ...
FROM table_name
WHERE condition;

實作:查詢order_details,並且限制單價為10000的資料

1
Select * from order_details  where price = '10000'

結果:

實作:查詢order_details,並且限制產品編號為P001、P002的結果

1
Select * from order_details  where product_number IN ('P001','P002')

結果:

SQL AND, OR and NOT 語法

在使用where時,我們可以用AND、 OR、 NOT去組合我們理想的條件。
AND:組合多個條件,全為TURE回傳

1
2
3
SELECT column1, column2, ...
FROM table_name
WHERE condition1 AND condition2 AND condition3 ...;

OR:組合多個條件,符合其中一個條件都算在資料集中,最後回傳。

1
2
3
SELECT column1, column2, ...
FROM table_name
WHERE condition1 OR condition2 OR condition3 ...;

NOT:想排除某條件

1
2
3
SELECT column1, column2, ...
FROM table_name
WHERE NOT condition;

實作:查詢order_details,並且限制amount為2或total為30000的結果

1
Select * from order_details where amount = '2' or total = '30000'

結果:

實作:查詢order_details,並且限制amount為2或total為30000,且id不為2的結果

1
Select * from order_details where amount = '2' or total = '30000' and NOT id = '2'

結果:

SQL ORDER BY 語法

可用來排序Select結果表的結果集合,透過欄位可以遞增、遞減。

1
2
3
SELECT column1, column2, ...
FROM table_name
ORDER BY column1, column2, ... ASC|DESC;

實作:查詢order_details,並把id遞減排序

1
Select * from order_details order by id desc

結果:

SQL TOP 語法

取得前幾筆資料,在MSSQL中則是用TOP;MYSQL用LIMIT,似乎每個DB都有不同的標準。

1
2
3
SELECT TOP number|percent column_name(s)
FROM table_name
WHERE condition;

實作:查詢order_details,前一筆資料

1
Select TOP 1 * from order_details 

結果:

SQL NULL Values 語法

可以用來篩選欄位是否為空值(未填充)。
為空:

1
2
3
SELECT column_names
FROM table_name
WHERE column_name IS NULL;

不為空:

1
2
3
SELECT column_names
FROM table_name
WHERE column_name IS NOT NULL;

實作:查詢order_details,remarks不為空的結果

1
Select * from order_details where remark IS NOT NULL

結果:

小結

在這回中大量練習了表單的查詢,發現其實還蠻單純的,並不會特別難或什麼。
在下回中會開始練習跨表的搜尋JOIN。

參考資料
https://ithelp.ithome.com.tw/articles/10214938
https://www.w3schools.com/sql/sql_select.asp
https://www.w3schools.com/sql/sql_where.asp

[Day8] SQL server手動寫入資料 - SQL Server資料庫入門

在上回 [Day7] SQL server資料庫關聯 - SQL Server資料庫入門 中,我們拉完了訂單表單的資料庫關聯。
在這回就要實際的將資料寫入。

訂單圖

資料表關聯圖

(Ps.如果你想知道怎麼把訂單圖轉成那四張資料表可以看 [Day5] 規劃資料表,透過資料庫正規化 - SQL Server資料庫入門 )

我們預計將填入以下資料:

將資料手動寫入

1.customers表 > 右鍵 Edit Top 200 Rows 叫出編輯視窗

2.將資料輸入即可,就像Excel那樣

3.products表也一樣

4.orders表也一樣

5.order_details表也一樣,而id設定自動遞增,所以不用填寫會自動補。

6.這樣就就完成把資料手動寫入了。

結語

這回主要是練習將資料手動寫入,並看看是否會有問題。
再來就是SQL語法的撰寫了,會以比較常見>進階>特殊這樣去前進。
下回預計是用語法寫入的話該怎麼寫。

[Day7] SQL server資料庫關聯 - SQL Server資料庫入門

在上回中 [Day6] SQL server資料表建立 - SQL Server資料庫入門 我們建立了正規化後的資料表進資料庫。
在這回中我們要知道資料庫中的關聯關係,並且拉好SQL server中表的主鍵、外來鍵關聯。

一對一(One to One):

有A、B兩個table裡面各自存在一筆記錄,可以使用其主鍵,對應至另一個table的一筆記錄。
如下圖,students_public裡的sid 107000001 在students_private只會出現1筆;students_private裡的sid 107000001在students_public也只會出現1筆。

一對多(One to Many):

如depdepartments的一筆id可以對應到students多筆的dept_id。
所以depdepartments對students是一對多。
相對的,students多筆的dept_id可以對應到depdepartments的一筆id。
所以students對depdepartments是多對一。

多對多(Many to Many):

一個 A 是否會有多個 B?
一個 B 是否會有多個 A?
兩者皆是,就是多對多

以下面例子,
一個文章是否有多個標籤?ANS:是
一個標籤是否有多個文章?ANS:是

這邊稍微解講一下,多對多可能會呈現以下的圖表情況。
posts透過一張中介的表(intermediary table)對應到tags
tags透過一張中介的表(intermediary table)對應到posts
則post與tags為多對多。

(PS.基本上在2NF、3NF時就會不自覺得做出這種表。)

可以參考 [Day5] 規劃資料表,透過資料庫正規化 - SQL Server資料庫入門

ps.圖片來源參考自參考資料。

拉資料表關聯

1.建立資料表視圖

2.選取要呈現的表

3.顯示我們剛剛建立的4張表

4.拉關聯
對order_details來說,product_number是外來鍵,而主鍵則是指向products的number。
從products的number拖曳到order_details的product_number即可完成關聯,會跳出視窗點確認後點確定即可。

5.把剩下的也拉完
對orders來說,customer_number是外來鍵,而主鍵則是指向customers的number。
對order_details來說,order_number是外來鍵,而主鍵則是指向orders的number。
如此一來就完成手動關聯了。

6.分析一下資料庫中的表的關聯性

customers、orders
一個顧客有多個訂單?是
一個訂單是否有多個顧客?否
所以customers對orders是一對多

orders、order_details
一個訂單是否有多個訂單明細?是
一個明細是否有多個訂單?否
所以orders對order_details是一對多

orders與products,中間媒介order_details
一個訂單是否有多個產品?是
一個產品是否有多個訂單?是
所以orders對products是多對多

結語

在一對一、一對多,我們可以很直覺的知道。
而多對多時可能需要想:
一個 A 是否會有多個 B?
一個 B 是否會有多個 A?
如果回答都是”是”那就是多對多,少一個就是一對多或多對一。

此外,我覺得在規劃時好像不太需要知道一對一、一對多、多對多,只是如果知道在拆表上可能會更快。
因為在2NF、3NF時會不自覺完成,只要知道主鍵、外來鍵即可。

在下回預計會將資料填入資料庫中。

參考資料:
https://medium.com/pierceshih/%E7%AD%86%E8%A8%98-%E8%B3%87%E6%96%99%E9%97%9C%E8%81%AF%E7%9A%84%E4%B8%89%E7%A8%AE%E9%97%9C%E4%BF%82-245152c093da
https://notes.andywu.tw/2018/%E8%B3%87%E6%96%99%E5%BA%AB-%E9%97%9C%E8%81%AF%E4%BB%8B%E7%B4%B9-%E4%B8%80%E5%B0%8D%E4%B8%80%E3%80%81%E4%B8%80%E5%B0%8D%E5%A4%9A%E3%80%81%E5%A4%9A%E5%B0%8D%E5%A4%9A/
https://ithelp.ithome.com.tw/articles/10214381

[Day6] SQL server資料表建立 - SQL Server資料庫入門

在上回 [Day5] 規劃資料表,透過資料庫正規化 - SQL Server資料庫入門 中,我們經由資料庫正規化1NF->2NF->3NF將訂單表單正規化,再來我們就要使用SSMS(Microsoft SQL Server Management Studio)協助我們在資料庫中建立資料表。

在這篇中,主要涵蓋內容為:
1.如何在SQL server中建立資料表
2.如何設定自動遞增
3.欄位型別選擇

訂單示意圖:

3NF正規化後:

建立資料表orders

1.對table點右鍵>new table

2.接著會出現設計的框框

3.填入欄位與型別
在此,補充一點,金額使用decimal,不要使用float,因為以下原因:
因為float表示近似數值,存在精度損失,存有小數的值可能會造成誤差,因此使用decimal。
decimal(p,s) 需要分別指定小數的最大位數(p)和小數位的數量(s)。
p (precision) :指定小數的最大位數,小數點的左側和右側的數字的總數量不能超過p,p的取值範圍是從1到38,默認值爲18。
s (scale):指定在小數點右側的小數位數,p-s是小數點左邊的最大位數。s必須是從0到p的值,只有在指定了精度的情況下才能指定s,s的默認值是0,因此,0 <= s <= p。

延伸閱讀: [Day3] SQL Server資料型態 - SQL Server資料庫入門 可以多加參考。

4.設定主鍵(PK)

5.儲存Ctrl+S,並命名為orders

6.重新整理,左方會出現orders資料表,代表成功建立

建立資料表customers

基本上可以照前面做法建立即可。

建立資料表products

基本上可以照前面做法建立即可。

建立資料表order_details

1.建構表
這邊我把amount數量設為整數。

2.這邊注意,要把id設定為主鍵,之後設定「自動遞增」,作法是identify設定成YES

3.刷新出現order_details

資料表視圖,總覽資料表

1.建立資料表視圖

2.選取要呈現的表

3.顯示我們剛剛建立的4張表

在跟我們在先前規劃的對照

總結

如果在先前正規化好的話,那建表時真的會很輕鬆XD,就只要考慮型別即可。

欄位命名規則上,我使用「全小寫,搭配底線」的規則(參考Laravel&MySQL),如果專案有統一就用專案內的,重點是整份是一樣規則就好。ps.此外,我發現用google翻譯翻出來的英文都怪怪的,有些是形容詞、名詞等等,可能要找天弄清楚。

欄位型態上,可參考先前寫的 [Day3] SQL Server資料型態 - SQL Server資料庫入門
在這回中發現了float與decimal的差別,因此在存有小數的金額請使用decimal,避免造成誤差、不準確。

特殊規則上,如某些欄位是流水號id需要「自動遞增」,作法是identify設定成YES。

在下回,我們將開始關聯這些表。

參考資料:
https://ithelp.ithome.com.tw/articles/10214381
https://ithelp.ithome.com.tw/articles/10214308

[Day5] 規劃資料表,透過資料庫正規化 - SQL Server資料庫入門

前言

這回主要淺談一下資料庫正規化,並且規劃我們的訂單資料表。

資料庫正規化

正規化的目的:

  1. 避免資料重複或相互矛盾的情形。
  2. 使資料庫在使用時能更有效率、更容易維護。

正規化:

  1. 資料庫的正規化共可分為1NF (Normal Form)、2NF、3NF、BCNF (Boyce-Codd Normal Form)、4NF、5NF 等多個階段, 不過對於一般資料庫設計來說, 通常只需要執行到BCNF即可, 而其他更高階的正規化只有在特殊的情況下才用得到。

  2. 在資料表正規化的過程(1NF 到BCNF) 中, 每個階段都是以欄位的相依性, 做為分割資料表的依據之一。

什麼是相依性?
欄位相依:就是在資料表中,乙欄位的值必須搭配甲欄位才有意義,則我們說『乙欄位相依於甲欄位』。

第一正規化規則(1NF):

1.決定主鍵:資料表中有主鍵, 而其他所有的欄位都相依於主鍵。
2.一個欄位只能有單一值:每個欄位中都只有儲存單一值,例如: 姓名欄位中不能存放2 個人的姓名。
3.消除意義上重複的欄位:資料表中沒有意義相同的多個欄位, 例如: 姓名1、姓名2…等重複的欄位。

如果不符合一階可能會造成:存取效率低、資料長度無法確定

第二正規化規則(2NF):

1.符合1NF 的格式
2.去除『部分相依性』:各欄位與主鍵間沒有部分相依的關係。

部分相依: 只有在主鍵是由多個欄位組成時才會發生,部分相依的欄位只與主鍵中的部分欄位有相依性,而與另一部份的欄位沒有相依性。

要除去資料表中的部分相依性, 只需將部份相依的欄位分割成另外的資料表即可。
因為完成第一階時容易會有資料重複問題,因此要執行第二階。

第三正規化規則(3NF):

1.符合2NF 的格式。
2.去除『間接相依性』:各欄位與主鍵間沒有間接相依的關係。

間接(遞移)相依: 是指二個欄位並非直接相依,而是藉助其他欄位來達成資料相依的關係。例如A與B相依且B與C相依,則我們說A與C為間接相依。

消除遞移相依:非主鍵屬性的欄位都只能和候選鍵相關,非主鍵屬性的欄位彼此間應該要是獨立無關的

候選鍵:欄位組合讓資料能是唯一的,並且是最小唯一

要除去資料表中的間接相依性, 其方法和除去部分相依性完全相同。例如:可再分割成N個資料表。

Boyce-Codd 正規化(BCNF)

對於大部分資料庫來說, 通常只需要執行到第三階段的正規化即足夠了。如果資料表的主鍵是由多個欄位組成的, 則必須再執行Boyce-Codd 正規化

1.符合2NF 的格式。
2.各欄位與主鍵沒有間接相依的關係。
3.主鍵中的各欄位不可以相依於其他非主鍵的欄位。

其他

正規化的另類思考:
1.不必要的分割:正規化的工作有時候不必做的太徹底, 因為過多的資料表可能會降低系統執行的效能。
2.人工的分割:有時為了增加資料處理的效率, 我們可以將已經符合BCNF 的資料表再做分割。一個資料表擁有很多欄位, 而其中又有
很多欄位根本很少用到。那麼, 我們就可以將這些很少用到的欄位分離出來, 存放到另外一個資料表中。

正規化實作-訂單

步驟1:正規化前

步驟2:1NF後

1.決定主鍵:資料表中有主鍵, 而其他所有的欄位都相依於主鍵。
2.一個欄位只能有單一值:每個欄位中都只有儲存單一值,例如: 姓名欄位中不能存放2 個人的姓名。
3.消除意義上重複的欄位:資料表中沒有意義相同的多個欄位, 例如: 姓名1、姓名2…等重複的欄位。

把因欄位單一值的欄位獨立成另一個表、決定主鍵,因為沒有重複欄位所以不用消除。

步驟3:經過2NF & 3NF 後

2NF:
1.符合1NF 的格式
2.去除『部分相依性』:各欄位與主鍵間沒有部分相依的關係。
3NF:
1.符合2NF 的格式
2.去除『間接相依性』:各欄位與主鍵間沒有間接相依的關係。

不斷比較各個欄位與該列表的主鍵關係,把表拆出。

*出貨單號、出貨日期、運送地址、客戶簽名、總計,有部分相依性
客戶姓名、客戶電話,有部分相依性
所以各自拆成兩張表

*明細流水號、出貨單號、數量、金額、備註,有部分相依性
產品、單價,有部分相依性
所以各自拆成兩張表

出貨單表的總計容易因為出貨單明細的數量*金額而變動,因此可考慮刪掉,但有時實際上又會因廠商變動而實際上硬壓上一個金額,所以還是先留著。

我在處理時覺得2跟3很難分開做,所以做完已經3階。

然後關聯

20210905修正
為了避免未來產品單價變動,造成日後撈訂單時價格改變,因此在訂單明細增加單價

結語

第一正規化做好後,第二正規化、第三正規要注意一點去做,讓結構拆開來,日後擴充會比較容易。
我自己在2~3階時有點對定義模糊,就是不斷把不相關跟主鍵去比,然後拆出來、拉關聯。(日後如果發覺錯誤會回來改正。)
此外補充一下,我覺得這種抽象的理論比實作重要,因為他沒辦法直接複製貼上,要透過動腦去分析,是累積功力的一個好方法。

Q1:我有一點疑問在"單價"比如往後需要調用歷史資料,而商品的單價改動,會接連造成訂單對不上,所以是否該把明細流水號增加單價?
ANS:20210905補上,針對這種日後可能會調度欄位、有糾紛問題的數值,存當時寫入的值比較穩定。因為是”歷史資料”,通常會限制成不可改,只能把整筆訂單刪除。
後來看了:https://ithelp.ithome.com.tw/m/questions/10197039?fbclid=IwAR1sdXPCl2JtFvhauc76aUcEuNIEtfcvH2TzodALPmC08HL8R-7mp74eLZc
這個問題是「需求」問題,看PM或SA想怎麼呈現
簡單的答案:沒必要
複雜的答案:要看資料欄位的「變化性」

參考資料
http://140.118.9.79/UML-SASD/%E6%AD%A3%E8%A6%8F%E5%8C%96.pdf
http://9715109.blogspot.com/2010/10/12.html
https://ithelp.ithome.com.tw/articles/10229472
https://www.youtube.com/watch?v=FgQ4QrwVNk0
http://spaces.isu.edu.tw/upload/19225/0/news/postfile_349.pdf
https://www.ptt.cc/bbs/Soft_Job/M.1623612131.A.33C.html
https://ithelp.ithome.com.tw/m/questions/10197039?fbclid=IwAR1sdXPCl2JtFvhauc76aUcEuNIEtfcvH2TzodALPmC08HL8R-7mp74eLZc

[Day4] SQL Server資料庫建立 - SQL Server資料庫入門

前言

這回我們利用SQL Server建立訂單的資料庫。
並給出一個例子,模擬PM或SA給你的訂單表格,要你把它轉成資料庫型態。

訂單表單

如下:(ps.這訂單來自參考資料的內容)

建立資料庫

Database>New Database

Database>New Database
輸出資料庫名稱Orders

可以見到左方出現了Orders資料庫

結語

在這回中,我們建立了一個訂單資料庫。
在下回中,會把資料庫正規化拿出來談一下,並且設計這張訂單的資料表。

參考資料
https://ithelp.ithome.com.tw/articles/10214308
http://cc.cust.edu.tw/~ccchen/doc/db_04.pdf
https://ithelp.ithome.com.tw/articles/10229472
https://zh.wikipedia.org/wiki/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%A7%84%E8%8C%83%E5%8C%96
http://140.118.9.79/UML-SASD/%E6%AD%A3%E8%A6%8F%E5%8C%96.pdf